diff --git a/app/src/frontend/config/category-maps-config.ts b/app/src/frontend/config/category-maps-config.ts
index 35f3cef2..19c60d84 100644
--- a/app/src/frontend/config/category-maps-config.ts
+++ b/app/src/frontend/config/category-maps-config.ts
@@ -1,4 +1,5 @@
import { Category } from './categories-config';
+import { BuildingMapTileset } from './tileserver-config';
export type LegendElement = {
color: string;
@@ -16,7 +17,7 @@ export interface LegendConfig {
}
export interface CategoryMapDefinition {
- mapStyle: string;
+ mapStyle: BuildingMapTileset;
legend: LegendConfig;
}
diff --git a/app/src/frontend/config/map-config.ts b/app/src/frontend/config/map-config.ts
new file mode 100644
index 00000000..fc818656
--- /dev/null
+++ b/app/src/frontend/config/map-config.ts
@@ -0,0 +1,7 @@
+export const defaultMapPosition = {
+ lat: 51.5245255,
+ lng: -0.1338422,
+ zoom: 16
+};
+
+export type MapTheme = 'light' | 'night';
\ No newline at end of file
diff --git a/app/src/frontend/config/tileserver-config.ts b/app/src/frontend/config/tileserver-config.ts
new file mode 100644
index 00000000..e3745443
--- /dev/null
+++ b/app/src/frontend/config/tileserver-config.ts
@@ -0,0 +1,19 @@
+/**
+ * This file defines all the valid tileset names that can be obtained from the tilserver.
+ * Adjust the values here if modifying the list of styles in the tileserver.
+ */
+
+export type BuildingMapTileset = 'date_year' |
+ 'size_height' |
+ 'construction_core_material' |
+ 'location' |
+ 'likes' |
+ 'planning_combined' |
+ 'sust_dec' |
+ 'building_attachment_form' |
+ 'landuse' |
+ 'dynamics_demolished_count';
+
+export type SpecialMapTileset = 'base_light' | 'base_night' | 'highlight' | 'number_labels';
+
+export type MapTileset = BuildingMapTileset | SpecialMapTileset;
diff --git a/app/src/frontend/map/layers/building-base-layer.tsx b/app/src/frontend/map/layers/building-base-layer.tsx
new file mode 100644
index 00000000..c491102b
--- /dev/null
+++ b/app/src/frontend/map/layers/building-base-layer.tsx
@@ -0,0 +1,19 @@
+import * as React from 'react';
+import { TileLayer } from 'react-leaflet';
+
+import { MapTheme } from '../../config/map-config';
+import { MapTileset } from '../../config/tileserver-config';
+
+import {getTileLayerUrl } from './get-tile-layer-url';
+
+export function BuildingBaseLayer({ theme }: {theme: MapTheme}) {
+ const tileset = `base_${theme}` as const;
+
+ return ;
+}
diff --git a/app/src/frontend/map/layers/building-data-layer.tsx b/app/src/frontend/map/layers/building-data-layer.tsx
new file mode 100644
index 00000000..c841d205
--- /dev/null
+++ b/app/src/frontend/map/layers/building-data-layer.tsx
@@ -0,0 +1,16 @@
+import * as React from 'react';
+import { TileLayer } from 'react-leaflet';
+
+import { BuildingMapTileset } from '../../config/tileserver-config';
+
+import {getTileLayerUrl } from './get-tile-layer-url';
+
+export function BuildingDataLayer({tileset, revisionId} : { tileset: BuildingMapTileset, revisionId: string }) {
+ return ;
+}
\ No newline at end of file
diff --git a/app/src/frontend/map/layers/building-highlight-layer.tsx b/app/src/frontend/map/layers/building-highlight-layer.tsx
new file mode 100644
index 00000000..b958f563
--- /dev/null
+++ b/app/src/frontend/map/layers/building-highlight-layer.tsx
@@ -0,0 +1,16 @@
+import * as React from 'react';
+import { TileLayer } from 'react-leaflet';
+
+import { BuildingMapTileset } from '../../config/tileserver-config';
+
+import { getTileLayerUrl } from './get-tile-layer-url';
+
+export function BuildingHighlightLayer({selectedBuildingId, baseTileset}: {selectedBuildingId: number, baseTileset: BuildingMapTileset}) {
+ return ;
+}
diff --git a/app/src/frontend/map/layers/building-numbers-layer.tsx b/app/src/frontend/map/layers/building-numbers-layer.tsx
new file mode 100644
index 00000000..76f8eb2e
--- /dev/null
+++ b/app/src/frontend/map/layers/building-numbers-layer.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import { TileLayer } from 'react-leaflet';
+
+import {getTileLayerUrl } from './get-tile-layer-url';
+
+export function BuildingNumbersLayer({revisionId}: {revisionId: string}) {
+ return ;
+}
diff --git a/app/src/frontend/map/layers/city-base-map-layer.tsx b/app/src/frontend/map/layers/city-base-map-layer.tsx
new file mode 100644
index 00000000..db6582a7
--- /dev/null
+++ b/app/src/frontend/map/layers/city-base-map-layer.tsx
@@ -0,0 +1,33 @@
+import * as React from 'react';
+import { TileLayer } from 'react-leaflet';
+
+import { MapTheme } from '../../config/map-config';
+
+const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
+
+/**
+ * Base raster layer for the map.
+ * @param theme map theme
+ */
+export function CityBaseMapLayer({theme}: {theme: MapTheme}) {
+
+ /**
+ * Ordnance Survey maps - UK / London specific
+ * (replace with appropriate base map for other cities/countries)
+ */
+ const key = OS_API_KEY;
+ const tilematrixSet = 'EPSG:3857';
+ const layer = theme === 'light' ? 'Light 3857' : 'Night 3857';
+ const baseUrl = `https://api2.ordnancesurvey.co.uk/mapping_api/v1/service/zxy/${tilematrixSet}/${layer}/{z}/{x}/{y}.png?key=${key}`;
+ const attribution = 'Building attribute data is © Colouring London contributors. Maps contain OS data © Crown copyright: OS Maps baselayers and building outlines. OS licence';
+
+ return ;
+}
+
diff --git a/app/src/frontend/map/layers/city-boundary-layer.tsx b/app/src/frontend/map/layers/city-boundary-layer.tsx
new file mode 100644
index 00000000..534b0611
--- /dev/null
+++ b/app/src/frontend/map/layers/city-boundary-layer.tsx
@@ -0,0 +1,17 @@
+import { GeoJsonObject } from 'geojson';
+import React, { useEffect, useState } from 'react';
+import { GeoJSON } from 'react-leaflet';
+
+import { apiGet } from '../../apiHelpers';
+
+export function CityBoundaryLayer() {
+ const [boundaryGeojson, setBoundaryGeojson] = useState(null);
+
+ useEffect(() => {
+ apiGet('/geometries/boundary-detailed.geojson')
+ .then(data => setBoundaryGeojson(data as GeoJsonObject));
+ }, []);
+
+ return boundaryGeojson &&
+ ;
+}
\ No newline at end of file
diff --git a/app/src/frontend/map/layers/get-tile-layer-url.ts b/app/src/frontend/map/layers/get-tile-layer-url.ts
new file mode 100644
index 00000000..f8a97817
--- /dev/null
+++ b/app/src/frontend/map/layers/get-tile-layer-url.ts
@@ -0,0 +1,14 @@
+import { MapTileset } from '../../config/tileserver-config';
+
+/**
+ * Formats a CL tileserver URL for a tileset and a set of parameters
+ * @param tileset the name of the tileset
+ * @param parameters (optional) dictionary of parameter values
+ * @returns A string with the formatted URL
+ */
+export function getTileLayerUrl(tileset: T, parameters?: Record) {
+ let paramString = parameters && new URLSearchParams(parameters).toString();
+ paramString = paramString == undefined ? '' : `?${paramString}`;
+
+ return `/tiles/${tileset}/{z}/{x}/{y}{r}.png${paramString}`;
+}
\ No newline at end of file
diff --git a/app/src/frontend/map/map.tsx b/app/src/frontend/map/map.tsx
index 8ace26d0..32ed24d5 100644
--- a/app/src/frontend/map/map.tsx
+++ b/app/src/frontend/map/map.tsx
@@ -1,21 +1,26 @@
-import { GeoJsonObject } from 'geojson';
import React, { Component, Fragment } from 'react';
-import { AttributionControl, GeoJSON, MapContainer, TileLayer, ZoomControl, useMapEvent } from 'react-leaflet';
+import { AttributionControl, MapContainer, ZoomControl, useMapEvent, Pane } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import './map.css';
import { apiGet } from '../apiHelpers';
import { HelpIcon } from '../components/icons';
+import { categoryMapsConfig } from '../config/category-maps-config';
+import { Category } from '../config/categories-config';
+import { defaultMapPosition, MapTheme } from '../config/map-config';
+import { Building } from '../models/building';
+
+import { CityBaseMapLayer } from './layers/city-base-map-layer';
+import { CityBoundaryLayer } from './layers/city-boundary-layer';
+import { BuildingBaseLayer } from './layers/building-base-layer';
+import { BuildingDataLayer } from './layers/building-data-layer';
+import { BuildingNumbersLayer } from './layers/building-numbers-layer';
+import { BuildingHighlightLayer } from './layers/building-highlight-layer';
import Legend from './legend';
import SearchBox from './search-box';
import ThemeSwitcher from './theme-switcher';
-import { categoryMapsConfig } from '../config/category-maps-config';
-import { Category } from '../config/categories-config';
-import { Building } from '../models/building';
-
-const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
interface ColouringMapProps {
selectedBuildingId: number;
@@ -26,12 +31,12 @@ interface ColouringMapProps {
}
interface ColouringMapState {
- theme: 'light' | 'night';
+ theme: MapTheme;
lat: number;
lng: number;
zoom: number;
- boundary: GeoJsonObject;
}
+
/**
* Map area
*/
@@ -40,10 +45,7 @@ class ColouringMap extends Component {
super(props);
this.state = {
theme: 'night',
- lat: 51.5245255,
- lng: -0.1338422,
- zoom: 16,
- boundary: undefined,
+ ...defaultMapPosition
};
this.handleClick = this.handleClick.bind(this);
this.handleLocate = this.handleLocate.bind(this);
@@ -73,72 +75,12 @@ class ColouringMap extends Component {
this.setState({theme: newTheme});
}
- async getBoundary() {
- const data = await apiGet('/geometries/boundary-detailed.geojson') as GeoJsonObject;
-
- this.setState({
- boundary: data
- });
- }
-
- componentDidMount() {
- this.getBoundary();
- }
-
render() {
const categoryMapDefinition = categoryMapsConfig[this.props.category];
const position: [number, number] = [this.state.lat, this.state.lng];
- // baselayer
- const key = OS_API_KEY;
- const tilematrixSet = 'EPSG:3857';
- const layer = (this.state.theme === 'light')? 'Light 3857' : 'Night 3857';
- const baseUrl = `https://api2.ordnancesurvey.co.uk/mapping_api/v1/service/zxy/${tilematrixSet}/${layer}/{z}/{x}/{y}.png?key=${key}`;
- const attribution = 'Building attribute data is © Colouring London contributors. Maps contain OS data © Crown copyright: OS Maps baselayers and building outlines. OS licence';
- const baseLayer = ;
-
- const buildingsBaseUrl = `/tiles/base_${this.state.theme}/{z}/{x}/{y}{r}.png`;
- const buildingBaseLayer = ;
-
- const boundaryLayer = this.state.boundary &&
- ;
-
const tileset = categoryMapDefinition.mapStyle;
- const dataLayer = tileset != undefined &&
- ;
-
- // highlight
- const highlightLayer = this.props.selectedBuildingId != undefined &&
- ;
-
- const numbersLayer =
const hasSelection = this.props.selectedBuildingId != undefined;
const isEdit = ['edit', 'multi-edit'].includes(this.props.mode);
@@ -155,12 +97,39 @@ class ColouringMap extends Component {
attributionControl={false}
>
- { baseLayer }
- { buildingBaseLayer }
- { boundaryLayer }
- { dataLayer }
- { highlightLayer }
- { numbersLayer }
+
+
+
+
+
+
+ {
+ tileset &&
+
+ }
+
+
+
+
+ {
+ this.props.selectedBuildingId &&
+
+ }
+
+