diff --git a/app/javascript/components/MapEditor/index.js b/app/javascript/components/MapEditor/index.js
index 4c6246a2f..76803b00c 100644
--- a/app/javascript/components/MapEditor/index.js
+++ b/app/javascript/components/MapEditor/index.js
@@ -1,13 +1,15 @@
import React, { useState, useCallback, useRef, useMemo } from 'react';
import PropTypes from 'prop-types';
import mapboxgl from 'mapbox-gl';
-import ReactMapboxGl, { GeoJSONLayer, ZoomControl } from 'react-mapbox-gl';
+import { GeoJSONLayer, ZoomControl } from 'react-mapbox-gl';
import DrawControl from 'react-mapbox-gl-draw';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import { getJSON, ajax, fire } from '@utils';
-import { getMapStyle, SwitchMapStyle } from '../MapStyles';
+import Mapbox from '../shared/mapbox/Mapbox';
+import { getMapStyle } from '../shared/mapbox/styles';
+import SwitchMapStyle from '../shared/mapbox/SwitchMapStyle';
import ComboAdresseSearch from '../ComboAdresseSearch';
import {
@@ -22,9 +24,7 @@ import {
generateId,
useEvent,
findFeature
-} from '../shared/map';
-
-const Map = ReactMapboxGl({});
+} from '../shared/mapbox/utils';
function MapEditor({ featureCollection, url, preview, options }) {
const drawControl = useRef(null);
@@ -38,10 +38,11 @@ function MapEditor({ featureCollection, url, preview, options }) {
const [cadastresFeatureCollection, setCadastresFeatureCollection] = useState(
filterFeatureCollection(featureCollection, 'cadastre')
);
- const mapStyle = useMemo(
- () => getMapStyle(style, options.cadastres, options.mnhn),
- [style, options]
- );
+ const mapStyle = useMemo(() => getMapStyle(style, options.layers), [
+ style,
+ options
+ ]);
+ const hasCadastres = useMemo(() => options.layers.includes('cadastres'));
const translations = [
['.mapbox-gl-draw_line', 'Tracer une ligne'],
@@ -288,7 +289,7 @@ function MapEditor({ featureCollection, url, preview, options }) {
}}
/>
-
+
>
);
}
@@ -335,8 +336,7 @@ MapEditor.propTypes = {
url: PropTypes.string,
preview: PropTypes.bool,
options: PropTypes.shape({
- cadastres: PropTypes.bool,
- mnhn: PropTypes.bool,
+ layers: PropTypes.array,
ign: PropTypes.bool
})
};
diff --git a/app/javascript/components/MapReader/index.js b/app/javascript/components/MapReader/index.js
index 48c86a7ef..9a14202cb 100644
--- a/app/javascript/components/MapReader/index.js
+++ b/app/javascript/components/MapReader/index.js
@@ -1,10 +1,11 @@
import React, { useState, useCallback, useMemo } from 'react';
-import ReactMapboxGl, { ZoomControl, GeoJSONLayer } from 'react-mapbox-gl';
+import { ZoomControl, GeoJSONLayer } from 'react-mapbox-gl';
import mapboxgl, { Popup } from 'mapbox-gl';
import PropTypes from 'prop-types';
-import { getMapStyle, SwitchMapStyle } from '../MapStyles';
-
+import Mapbox from '../shared/mapbox/Mapbox';
+import { getMapStyle } from '../shared/mapbox/styles';
+import SwitchMapStyle from '../shared/mapbox/SwitchMapStyle';
import {
filterFeatureCollection,
filterFeatureCollectionByGeometryType,
@@ -12,9 +13,7 @@ import {
findFeature,
fitBounds,
getCenter
-} from '../shared/map';
-
-const Map = ReactMapboxGl({});
+} from '../shared/mapbox/utils';
const MapReader = ({ featureCollection, options }) => {
const [currentMap, setCurrentMap] = useState(null);
@@ -51,11 +50,11 @@ const MapReader = ({ featureCollection, options }) => {
),
[selectionsUtilisateurFeatureCollection]
);
- const hasCadastres = !!cadastresFeatureCollection.length;
- const mapStyle = useMemo(
- () => getMapStyle(style, hasCadastres, options.mnhn),
- [style, options, cadastresFeatureCollection]
- );
+ const hasCadastres = useMemo(() => options.layers.includes('cadastres'));
+ const mapStyle = useMemo(() => getMapStyle(style, options.layers), [
+ style,
+ options
+ ]);
const popup = useMemo(
() =>
new Popup({
@@ -147,7 +146,7 @@ const MapReader = ({ featureCollection, options }) => {
}
return (
-
+
);
};
@@ -197,8 +196,8 @@ MapReader.propTypes = {
features: PropTypes.array
}),
options: PropTypes.shape({
- ign: PropTypes.bool,
- mnhn: PropTypes.bool
+ layers: PropTypes.array,
+ ign: PropTypes.bool
})
};
diff --git a/app/javascript/components/MapStyles/base-style.js b/app/javascript/components/MapStyles/base-style.js
deleted file mode 100644
index 1443e4500..000000000
--- a/app/javascript/components/MapStyles/base-style.js
+++ /dev/null
@@ -1,96 +0,0 @@
-const IGN_TOKEN = 'rc1egnbeoss72hxvd143tbyk';
-
-function ignServiceURL(layer, format = 'image/png') {
- const url = `https://wxs.ign.fr/${IGN_TOKEN}/geoportail/wmts`;
- const query =
- 'service=WMTS&request=GetTile&version=1.0.0&tilematrixset=PM&tilematrix={z}&tilecol={x}&tilerow={y}&style=normal';
-
- return `${url}?${query}&layer=${layer}&format=${format}`;
-}
-
-function rasterSource(tiles, attribution) {
- return {
- type: 'raster',
- tiles,
- tileSize: 256,
- attribution,
- minzoom: 0,
- maxzoom: 18
- };
-}
-
-export default {
- version: 8,
- metadat: {
- 'mapbox:autocomposite': false,
- 'mapbox:groups': {
- 1444849242106.713: { collapsed: false, name: 'Places' },
- 1444849334699.1902: { collapsed: true, name: 'Bridges' },
- 1444849345966.4436: { collapsed: false, name: 'Roads' },
- 1444849354174.1904: { collapsed: true, name: 'Tunnels' },
- 1444849364238.8171: { collapsed: false, name: 'Buildings' },
- 1444849382550.77: { collapsed: false, name: 'Water' },
- 1444849388993.3071: { collapsed: false, name: 'Land' }
- },
- 'mapbox:type': 'template',
- 'openmaptiles:mapbox:owner': 'openmaptiles',
- 'openmaptiles:mapbox:source:url': 'mapbox://openmaptiles.4qljc88t',
- 'openmaptiles:version': '3.x',
- 'maputnik:renderer': 'mbgljs'
- },
- center: [0, 0],
- zoom: 1,
- bearing: 0,
- pitch: 0,
- sources: {
- 'decoupage-administratif': {
- type: 'vector',
- url:
- 'https://openmaptiles.geo.data.gouv.fr/data/decoupage-administratif.json'
- },
- openmaptiles: {
- type: 'vector',
- url: 'https://openmaptiles.geo.data.gouv.fr/data/france-vector.json'
- },
- 'photographies-aeriennes': {
- type: 'raster',
- tiles: [
- 'https://tiles.geo.api.gouv.fr/photographies-aeriennes/tiles/{z}/{x}/{y}'
- ],
- tileSize: 256,
- attribution: 'Images aériennes © IGN',
- minzoom: 0,
- maxzoom: 19
- },
- cadastre: {
- type: 'vector',
- url: 'https://openmaptiles.geo.data.gouv.fr/data/cadastre.json'
- },
- 'plan-ign': rasterSource(
- [ignServiceURL('GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2')],
- 'IGN-F/Géoportail'
- ),
- 'protectedareas-gp': rasterSource(
- [ignServiceURL('PROTECTEDAREAS.GP')],
- 'IGN-F/Géoportail/MNHN'
- ),
- 'protectedareas-pn': rasterSource(
- [ignServiceURL('PROTECTEDAREAS.PN')],
- 'IGN-F/Géoportail/MNHN'
- ),
- 'protectedareas-pnr': rasterSource(
- [ignServiceURL('PROTECTEDAREAS.PNR')],
- 'IGN-F/Géoportail/MNHN'
- ),
- 'protectedareas-sic': rasterSource(
- [ignServiceURL('PROTECTEDAREAS.SIC')],
- 'IGN-F/Géoportail/MNHN'
- ),
- 'protectedareas-zps': rasterSource(
- [ignServiceURL('PROTECTEDAREAS.ZPS')],
- 'IGN-F/Géoportail/MNHN'
- )
- },
- sprite: 'https://openmaptiles.github.io/osm-bright-gl-style/sprite',
- glyphs: 'https://openmaptiles.geo.data.gouv.fr/fonts/{fontstack}/{range}.pbf'
-};
diff --git a/app/javascript/components/MapStyles/index.js b/app/javascript/components/MapStyles/index.js
deleted file mode 100644
index 4122f0ef5..000000000
--- a/app/javascript/components/MapStyles/index.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import baseStyle from './base-style';
-import cadastre from './cadastre';
-import orthoStyle from './ortho-style';
-import vectorStyle from './vector-style';
-
-function rasterStyle(source) {
- return {
- id: source,
- source,
- type: 'raster',
- paint: { 'raster-resampling': 'linear' }
- };
-}
-
-export function getMapStyle(style, hasCadastres, hasMNHN) {
- const mapStyle = { ...baseStyle };
-
- switch (style) {
- case 'ortho':
- mapStyle.layers = orthoStyle;
- mapStyle.id = 'ortho';
- mapStyle.name = 'Photographies aériennes';
- break;
- case 'vector':
- mapStyle.layers = vectorStyle;
- mapStyle.id = 'vector';
- mapStyle.name = 'Carte OSM';
- break;
- case 'ign':
- mapStyle.layers = [rasterStyle('plan-ign')];
- mapStyle.id = 'ign';
- mapStyle.name = 'Carte IGN';
- break;
- }
-
- if (hasCadastres) {
- mapStyle.layers = mapStyle.layers.concat(cadastre);
- mapStyle.id += '-cadastre';
- }
-
- if (hasMNHN) {
- mapStyle.layers = mapStyle.layers.concat([
- rasterStyle('protectedareas-gp'),
- rasterStyle('protectedareas-pn'),
- rasterStyle('protectedareas-pnr'),
- rasterStyle('protectedareas-sic'),
- rasterStyle('protectedareas-zps')
- ]);
- mapStyle.id += '-mnhn';
- }
-
- return mapStyle;
-}
-
-export { SwitchMapStyle } from './SwitchMapStyle';
diff --git a/app/javascript/components/shared/mapbox/Mapbox.js b/app/javascript/components/shared/mapbox/Mapbox.js
new file mode 100644
index 000000000..3acc17e94
--- /dev/null
+++ b/app/javascript/components/shared/mapbox/Mapbox.js
@@ -0,0 +1,3 @@
+import ReactMapboxGl from 'react-mapbox-gl';
+
+export default ReactMapboxGl({});
diff --git a/app/javascript/components/MapStyles/SwitchMapStyle.js b/app/javascript/components/shared/mapbox/SwitchMapStyle.js
similarity index 87%
rename from app/javascript/components/MapStyles/SwitchMapStyle.js
rename to app/javascript/components/shared/mapbox/SwitchMapStyle.js
index 074b344f5..d971219f9 100644
--- a/app/javascript/components/MapStyles/SwitchMapStyle.js
+++ b/app/javascript/components/shared/mapbox/SwitchMapStyle.js
@@ -1,8 +1,9 @@
import React from 'react';
-import ortho from './images/preview-ortho.png';
-import vector from './images/preview-vector.png';
import PropTypes from 'prop-types';
+import ortho from './styles/images/preview-ortho.png';
+import vector from './styles/images/preview-vector.png';
+
const STYLES = {
ortho: {
title: 'Satellite',
@@ -34,7 +35,7 @@ function getNextStyle(style, ign) {
return styles[index];
}
-export const SwitchMapStyle = ({ style, setStyle, ign }) => {
+function SwitchMapStyle({ style, setStyle, ign }) {
const nextStyle = getNextStyle(style, ign);
const { title, preview, color } = (ign ? IGN_STYLES : STYLES)[nextStyle];
@@ -69,10 +70,12 @@ export const SwitchMapStyle = ({ style, setStyle, ign }) => {
);
-};
+}
SwitchMapStyle.propTypes = {
style: PropTypes.string,
setStyle: PropTypes.func,
ign: PropTypes.bool
};
+
+export default SwitchMapStyle;
diff --git a/app/javascript/components/shared/mapbox/styles/base.js b/app/javascript/components/shared/mapbox/styles/base.js
new file mode 100644
index 000000000..cce2d7f40
--- /dev/null
+++ b/app/javascript/components/shared/mapbox/styles/base.js
@@ -0,0 +1,213 @@
+import cadastreLayers from './cadastre-layers';
+
+const IGN_TOKEN = 'rc1egnbeoss72hxvd143tbyk';
+
+function ignServiceURL(layer, format = 'image/png') {
+ const url = `https://wxs.ign.fr/${IGN_TOKEN}/geoportail/wmts`;
+ const query =
+ 'service=WMTS&request=GetTile&version=1.0.0&tilematrixset=PM&tilematrix={z}&tilecol={x}&tilerow={y}&style=normal';
+
+ return `${url}?${query}&layer=${layer}&format=${format}`;
+}
+
+const OPTIONAL_LAYERS = [
+ {
+ label: 'UNESCO',
+ id: 'unesco',
+ layers: [
+ ['Aires protégées Géoparcs', 'PROTECTEDAREAS.GP'],
+ ['Réserves de biosphère', 'PROTECTEDAREAS.BIOS']
+ ]
+ },
+ {
+ label: 'Arrêtés de protection',
+ id: 'arretes_protection',
+ layers: [
+ ['Arrêtés de protection de biotope', 'PROTECTEDAREAS.APB'],
+ ['Arrêtés de protection de géotope', 'PROTECTEDAREAS.APG']
+ ]
+ },
+ {
+ label: 'Conservatoire du Littoral',
+ id: 'conservatoire_littoral',
+ layers: [
+ [
+ 'Conservatoire du littoral : parcelles protégées',
+ 'PROTECTEDAREAS.MNHN.CDL.PARCELS'
+ ],
+ [
+ 'Conservatoire du littoral : périmètres d’intervention',
+ 'PROTECTEDAREAS.MNHN.CDL.PERIMETER'
+ ]
+ ]
+ },
+ {
+ label: 'Réserves nationales de chasse et de faune sauvage',
+ id: 'reserves_chasse_faune_sauvage',
+ layers: [
+ [
+ 'Réserves nationales de chasse et de faune sauvage',
+ 'PROTECTEDAREAS.RNCF'
+ ]
+ ]
+ },
+ {
+ label: 'Réserves biologiques',
+ id: 'reserves_biologiques',
+ layers: [['Réserves biologiques', 'PROTECTEDAREAS.RB']]
+ },
+ {
+ label: 'Réserves naturelles',
+ id: 'reserves_naturelles',
+ layers: [
+ ['Réserves naturelles nationales', 'PROTECTEDAREAS.RN'],
+ [
+ 'Périmètres de protection de réserves naturelles',
+ 'PROTECTEDAREAS.MNHN.RN.PERIMETER'
+ ],
+ ['Réserves naturelles de Corse', 'PROTECTEDAREAS.RNC'],
+ [
+ 'Réserves naturelles régionales',
+ 'PROTECTEDSITES.MNHN.RESERVES-REGIONALES'
+ ]
+ ]
+ },
+ {
+ label: 'Natura 2000',
+ id: 'natura_2000',
+ layers: [
+ ['Sites Natura 2000 (Directive Habitats)', 'PROTECTEDAREAS.SIC'],
+ ['Sites Natura 2000 (Directive Oiseaux)', 'PROTECTEDAREAS.ZPS']
+ ]
+ },
+ {
+ label: 'Zones humides d’importance internationale',
+ id: 'zones_humides',
+ layers: [
+ ['Zones humides d’importance internationale', 'PROTECTEDAREAS.RAMSAR']
+ ]
+ },
+ {
+ label: 'ZNIEFF',
+ id: 'znieff',
+ layers: [
+ [
+ 'Zones naturelles d’intérêt écologique faunistique et floristique de type 1 (ZNIEFF 1 mer)',
+ 'PROTECTEDAREAS.ZNIEFF1.SEA'
+ ],
+ [
+ 'Zones naturelles d’intérêt écologique faunistique et floristique de type 1 (ZNIEFF 1)',
+ 'PROTECTEDAREAS.ZNIEFF1'
+ ],
+ [
+ 'Zones naturelles d’intérêt écologique faunistique et floristique de type 2 (ZNIEFF 2 mer)',
+ 'PROTECTEDAREAS.ZNIEFF2.SEA'
+ ],
+ [
+ 'Zones naturelles d’intérêt écologique faunistique et floristique de type 2 (ZNIEFF 2)',
+ 'PROTECTEDAREAS.ZNIEFF2'
+ ]
+ ]
+ },
+ {
+ label: 'Cadastre',
+ id: 'cadastres',
+ layers: [['Cadastre', 'CADASTRE']]
+ }
+];
+
+function buildSources() {
+ return Object.fromEntries(
+ OPTIONAL_LAYERS.flatMap(({ layers }) => layers).map(([, code]) => [
+ code.toLowerCase().replace(/\./g, '-'),
+ rasterSource([ignServiceURL(code)], 'IGN-F/Géoportail/MNHN')
+ ])
+ );
+}
+
+function rasterSource(tiles, attribution) {
+ return {
+ type: 'raster',
+ tiles,
+ tileSize: 256,
+ attribution,
+ minzoom: 0,
+ maxzoom: 18
+ };
+}
+
+export function buildLayers(ids) {
+ return OPTIONAL_LAYERS.filter(({ id }) => ids.includes(id))
+ .flatMap(({ layers }) => layers)
+ .map(([, code]) =>
+ code === 'CADASTRE'
+ ? cadastreLayers
+ : rasterLayer(code.toLowerCase().replace(/\./g, '-'))
+ );
+}
+
+export function rasterLayer(source) {
+ return {
+ id: source,
+ source,
+ type: 'raster',
+ paint: { 'raster-resampling': 'linear' }
+ };
+}
+
+export default {
+ version: 8,
+ metadat: {
+ 'mapbox:autocomposite': false,
+ 'mapbox:groups': {
+ 1444849242106.713: { collapsed: false, name: 'Places' },
+ 1444849334699.1902: { collapsed: true, name: 'Bridges' },
+ 1444849345966.4436: { collapsed: false, name: 'Roads' },
+ 1444849354174.1904: { collapsed: true, name: 'Tunnels' },
+ 1444849364238.8171: { collapsed: false, name: 'Buildings' },
+ 1444849382550.77: { collapsed: false, name: 'Water' },
+ 1444849388993.3071: { collapsed: false, name: 'Land' }
+ },
+ 'mapbox:type': 'template',
+ 'openmaptiles:mapbox:owner': 'openmaptiles',
+ 'openmaptiles:mapbox:source:url': 'mapbox://openmaptiles.4qljc88t',
+ 'openmaptiles:version': '3.x',
+ 'maputnik:renderer': 'mbgljs'
+ },
+ center: [0, 0],
+ zoom: 1,
+ bearing: 0,
+ pitch: 0,
+ sources: {
+ 'decoupage-administratif': {
+ type: 'vector',
+ url:
+ 'https://openmaptiles.geo.data.gouv.fr/data/decoupage-administratif.json'
+ },
+ openmaptiles: {
+ type: 'vector',
+ url: 'https://openmaptiles.geo.data.gouv.fr/data/france-vector.json'
+ },
+ 'photographies-aeriennes': {
+ type: 'raster',
+ tiles: [
+ 'https://tiles.geo.api.gouv.fr/photographies-aeriennes/tiles/{z}/{x}/{y}'
+ ],
+ tileSize: 256,
+ attribution: 'Images aériennes © IGN',
+ minzoom: 0,
+ maxzoom: 19
+ },
+ cadastre: {
+ type: 'vector',
+ url: 'https://openmaptiles.geo.data.gouv.fr/data/cadastre.json'
+ },
+ 'plan-ign': rasterSource(
+ [ignServiceURL('GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2')],
+ 'IGN-F/Géoportail'
+ ),
+ ...buildSources()
+ },
+ sprite: 'https://openmaptiles.github.io/osm-bright-gl-style/sprite',
+ glyphs: 'https://openmaptiles.geo.data.gouv.fr/fonts/{fontstack}/{range}.pbf'
+};
diff --git a/app/javascript/components/MapStyles/cadastre.js b/app/javascript/components/shared/mapbox/styles/cadastre-layers.js
similarity index 100%
rename from app/javascript/components/MapStyles/cadastre.js
rename to app/javascript/components/shared/mapbox/styles/cadastre-layers.js
diff --git a/app/javascript/components/MapStyles/images/preview-ortho.png b/app/javascript/components/shared/mapbox/styles/images/preview-ortho.png
similarity index 100%
rename from app/javascript/components/MapStyles/images/preview-ortho.png
rename to app/javascript/components/shared/mapbox/styles/images/preview-ortho.png
diff --git a/app/javascript/components/MapStyles/images/preview-vector.png b/app/javascript/components/shared/mapbox/styles/images/preview-vector.png
similarity index 100%
rename from app/javascript/components/MapStyles/images/preview-vector.png
rename to app/javascript/components/shared/mapbox/styles/images/preview-vector.png
diff --git a/app/javascript/components/shared/mapbox/styles/index.js b/app/javascript/components/shared/mapbox/styles/index.js
new file mode 100644
index 000000000..97257b33f
--- /dev/null
+++ b/app/javascript/components/shared/mapbox/styles/index.js
@@ -0,0 +1,29 @@
+import baseStyle, { rasterLayer, buildLayers } from './base';
+import orthoStyle from './ortho-style';
+import vectorStyle from './vector-style';
+
+export function getMapStyle(style, optionalLayers) {
+ const mapStyle = { ...baseStyle };
+
+ switch (style) {
+ case 'ortho':
+ mapStyle.layers = orthoStyle;
+ mapStyle.id = 'ortho';
+ mapStyle.name = 'Photographies aériennes';
+ break;
+ case 'vector':
+ mapStyle.layers = vectorStyle;
+ mapStyle.id = 'vector';
+ mapStyle.name = 'Carte OSM';
+ break;
+ case 'ign':
+ mapStyle.layers = [rasterLayer('plan-ign')];
+ mapStyle.id = 'ign';
+ mapStyle.name = 'Carte IGN';
+ break;
+ }
+
+ mapStyle.layers = mapStyle.layers.concat(buildLayers(optionalLayers));
+
+ return mapStyle;
+}
diff --git a/app/javascript/components/MapStyles/ortho-style.js b/app/javascript/components/shared/mapbox/styles/ortho-style.js
similarity index 100%
rename from app/javascript/components/MapStyles/ortho-style.js
rename to app/javascript/components/shared/mapbox/styles/ortho-style.js
diff --git a/app/javascript/components/MapStyles/vector-style.js b/app/javascript/components/shared/mapbox/styles/vector-style.js
similarity index 100%
rename from app/javascript/components/MapStyles/vector-style.js
rename to app/javascript/components/shared/mapbox/styles/vector-style.js
diff --git a/app/javascript/components/shared/map.js b/app/javascript/components/shared/mapbox/utils.js
similarity index 100%
rename from app/javascript/components/shared/map.js
rename to app/javascript/components/shared/mapbox/utils.js