Use IGN plan v2 and add MNHN data sources

This commit is contained in:
Paul Chavard 2020-09-02 12:56:45 +02:00
parent f6c4158d9c
commit bbcdff0ccf
10 changed files with 130 additions and 65 deletions

View file

@ -56,30 +56,27 @@ module NewAdministrateur
:updated_at :updated_at
], ],
methods: [ methods: [
:cadastres,
:drop_down_list_value, :drop_down_list_value,
:parcelles_agricoles,
:piece_justificative_template_filename, :piece_justificative_template_filename,
:piece_justificative_template_url, :piece_justificative_template_url,
:quartiers_prioritaires :cadastres,
:mnhn
] ]
) )
} }
end end
def type_de_champ_create_params def type_de_champ_create_params
type_de_champ_params = params.required(:type_de_champ).permit(:cadastres, type_de_champ_params = params.required(:type_de_champ).permit(:type_champ,
:description,
:drop_down_list_value,
:libelle, :libelle,
:description,
:mandatory, :mandatory,
:order_place,
:parcelles_agricoles,
:parent_id, :parent_id,
:piece_justificative_template,
:private, :private,
:quartiers_prioritaires, :drop_down_list_value,
:type_champ) :piece_justificative_template,
:cadastres,
:mnhn)
if type_de_champ_params[:parent_id].present? if type_de_champ_params[:parent_id].present?
type_de_champ_params[:parent_id] = TypeDeChamp.to_stable_id(type_de_champ_params[:parent_id]) type_de_champ_params[:parent_id] = TypeDeChamp.to_stable_id(type_de_champ_params[:parent_id])
@ -89,15 +86,14 @@ module NewAdministrateur
end end
def type_de_champ_update_params def type_de_champ_update_params
params.required(:type_de_champ).permit(:cadastres, params.required(:type_de_champ).permit(:type_champ,
:description,
:drop_down_list_value,
:libelle, :libelle,
:description,
:mandatory, :mandatory,
:parcelles_agricoles, :drop_down_list_value,
:piece_justificative_template, :piece_justificative_template,
:quartiers_prioritaires, :cadastres,
:type_champ) :mnhn)
end end
end end
end end

View file

@ -23,7 +23,7 @@ import {
const Map = ReactMapboxGl({}); const Map = ReactMapboxGl({});
function MapEditor({ featureCollection, url, preview, hasCadastres, ign }) { function MapEditor({ featureCollection, url, preview, options }) {
const drawControl = useRef(null); const drawControl = useRef(null);
const [currentMap, setCurrentMap] = useState(null); const [currentMap, setCurrentMap] = useState(null);
@ -35,10 +35,10 @@ function MapEditor({ featureCollection, url, preview, hasCadastres, ign }) {
const [cadastresFeatureCollection, setCadastresFeatureCollection] = useState( const [cadastresFeatureCollection, setCadastresFeatureCollection] = useState(
filterFeatureCollection(featureCollection, 'cadastre') filterFeatureCollection(featureCollection, 'cadastre')
); );
const mapStyle = useMemo(() => getMapStyle(style, hasCadastres), [ const mapStyle = useMemo(
style, () => getMapStyle(style, options.cadastres, options.mnhn),
hasCadastres [style, options]
]); );
const translations = [ const translations = [
['.mapbox-gl-draw_line', 'Tracer une ligne'], ['.mapbox-gl-draw_line', 'Tracer une ligne'],
@ -306,7 +306,7 @@ function MapEditor({ featureCollection, url, preview, hasCadastres, ign }) {
height: '500px' height: '500px'
}} }}
> >
{hasCadastres ? ( {options.cadastres ? (
<GeoJSONLayer <GeoJSONLayer
data={cadastresFeatureCollection} data={cadastresFeatureCollection}
fillPaint={polygonCadastresFill} fillPaint={polygonCadastresFill}
@ -326,7 +326,7 @@ function MapEditor({ featureCollection, url, preview, hasCadastres, ign }) {
trash: true trash: true
}} }}
/> />
<SwitchMapStyle style={style} setStyle={setStyle} ign={ign} /> <SwitchMapStyle style={style} setStyle={setStyle} ign={options.ign} />
<ZoomControl /> <ZoomControl />
</Map> </Map>
</> </>
@ -341,8 +341,11 @@ MapEditor.propTypes = {
}), }),
url: PropTypes.string, url: PropTypes.string,
preview: PropTypes.bool, preview: PropTypes.bool,
hasCadastres: PropTypes.bool, options: PropTypes.shape({
ign: PropTypes.bool cadastres: PropTypes.bool,
mnhn: PropTypes.bool,
ign: PropTypes.bool
})
}; };
export default MapEditor; export default MapEditor;

View file

@ -16,7 +16,7 @@ import {
const Map = ReactMapboxGl({}); const Map = ReactMapboxGl({});
const MapReader = ({ featureCollection, ign }) => { const MapReader = ({ featureCollection, options }) => {
const [currentMap, setCurrentMap] = useState(null); const [currentMap, setCurrentMap] = useState(null);
const [style, setStyle] = useState('ortho'); const [style, setStyle] = useState('ortho');
const cadastresFeatureCollection = useMemo( const cadastresFeatureCollection = useMemo(
@ -52,10 +52,10 @@ const MapReader = ({ featureCollection, ign }) => {
[selectionsUtilisateurFeatureCollection] [selectionsUtilisateurFeatureCollection]
); );
const hasCadastres = !!cadastresFeatureCollection.length; const hasCadastres = !!cadastresFeatureCollection.length;
const mapStyle = useMemo(() => getMapStyle(style, hasCadastres), [ const mapStyle = useMemo(
style, () => getMapStyle(style, hasCadastres, options.mnhn),
cadastresFeatureCollection [style, options, cadastresFeatureCollection]
]); );
const popup = useMemo( const popup = useMemo(
() => () =>
new Popup({ new Popup({
@ -184,7 +184,7 @@ const MapReader = ({ featureCollection, ign }) => {
/> />
) : null} ) : null}
<SwitchMapStyle style={style} setStyle={setStyle} ign={ign} /> <SwitchMapStyle style={style} setStyle={setStyle} ign={options.ign} />
<ZoomControl /> <ZoomControl />
</Map> </Map>
); );
@ -196,7 +196,10 @@ MapReader.propTypes = {
bbox: PropTypes.array, bbox: PropTypes.array,
features: PropTypes.array features: PropTypes.array
}), }),
ign: PropTypes.bool options: PropTypes.shape({
ign: PropTypes.bool,
mnhn: PropTypes.bool
})
}; };
export default MapReader; export default MapReader;

View file

@ -1,15 +1,36 @@
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 { export default {
version: 8, version: 8,
metadat: { metadat: {
'mapbox:autocomposite': false, 'mapbox:autocomposite': false,
'mapbox:groups': { 'mapbox:groups': {
'1444849242106.713': { collapsed: false, name: 'Places' }, 1444849242106.713: { collapsed: false, name: 'Places' },
'1444849334699.1902': { collapsed: true, name: 'Bridges' }, 1444849334699.1902: { collapsed: true, name: 'Bridges' },
'1444849345966.4436': { collapsed: false, name: 'Roads' }, 1444849345966.4436: { collapsed: false, name: 'Roads' },
'1444849354174.1904': { collapsed: true, name: 'Tunnels' }, 1444849354174.1904: { collapsed: true, name: 'Tunnels' },
'1444849364238.8171': { collapsed: false, name: 'Buildings' }, 1444849364238.8171: { collapsed: false, name: 'Buildings' },
'1444849382550.77': { collapsed: false, name: 'Water' }, 1444849382550.77: { collapsed: false, name: 'Water' },
'1444849388993.3071': { collapsed: false, name: 'Land' } 1444849388993.3071: { collapsed: false, name: 'Land' }
}, },
'mapbox:type': 'template', 'mapbox:type': 'template',
'openmaptiles:mapbox:owner': 'openmaptiles', 'openmaptiles:mapbox:owner': 'openmaptiles',
@ -41,20 +62,34 @@ export default {
minzoom: 0, minzoom: 0,
maxzoom: 19 maxzoom: 19
}, },
'carte-ign': {
type: 'raster',
tiles: [
'https://wxs.ign.fr/rc1egnbeoss72hxvd143tbyk/geoportail/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=PM&tilematrix={z}&tilecol={x}&tilerow={y}&layer=GEOGRAPHICALGRIDSYSTEMS.MAPS&format=image/jpeg&style=normal'
],
tileSize: 256,
attribution: 'IGN-F/Géoportail',
minzoom: 0,
maxzoom: 18
},
cadastre: { cadastre: {
type: 'vector', type: 'vector',
url: 'https://openmaptiles.geo.data.gouv.fr/data/cadastre.json' 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', sprite: 'https://openmaptiles.github.io/osm-bright-gl-style/sprite',
glyphs: 'https://openmaptiles.geo.data.gouv.fr/fonts/{fontstack}/{range}.pbf' glyphs: 'https://openmaptiles.geo.data.gouv.fr/fonts/{fontstack}/{range}.pbf'

View file

@ -3,16 +3,16 @@ import cadastre from './cadastre';
import orthoStyle from './ortho-style'; import orthoStyle from './ortho-style';
import vectorStyle from './vector-style'; import vectorStyle from './vector-style';
const ignStyle = [ function rasterStyle(source) {
{ return {
id: 'carte-ign', id: source,
source,
type: 'raster', type: 'raster',
source: 'carte-ign',
paint: { 'raster-resampling': 'linear' } paint: { 'raster-resampling': 'linear' }
} };
]; }
export function getMapStyle(style, hasCadastres) { export function getMapStyle(style, hasCadastres, hasMNHN) {
const mapStyle = { ...baseStyle }; const mapStyle = { ...baseStyle };
switch (style) { switch (style) {
@ -27,7 +27,7 @@ export function getMapStyle(style, hasCadastres) {
mapStyle.name = 'Carte OSM'; mapStyle.name = 'Carte OSM';
break; break;
case 'ign': case 'ign':
mapStyle.layers = ignStyle; mapStyle.layers = [rasterStyle('plan-ign')];
mapStyle.id = 'ign'; mapStyle.id = 'ign';
mapStyle.name = 'Carte IGN'; mapStyle.name = 'Carte IGN';
break; break;
@ -38,6 +38,17 @@ export function getMapStyle(style, hasCadastres) {
mapStyle.id += '-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; return mapStyle;
} }

View file

@ -140,6 +140,10 @@ const TypeDeChamp = sortableElement(
label="Cadastres" label="Cadastres"
handler={updateHandlers.cadastres} handler={updateHandlers.cadastres}
/> />
<TypeDeChampCarteOption
label="Zones naturelles protégées"
handler={updateHandlers.mnhn}
/>
</TypeDeChampCarteOptions> </TypeDeChampCarteOptions>
<TypeDeChampRepetitionOptions <TypeDeChampRepetitionOptions
isVisible={isRepetition} isVisible={isRepetition}
@ -206,6 +210,7 @@ function createUpdateHandlers(dispatch, typeDeChamp, index, prefix) {
export const FIELDS = [ export const FIELDS = [
'cadastres', 'cadastres',
'mnhn',
'description', 'description',
'drop_down_list_value', 'drop_down_list_value',
'libelle', 'libelle',

View file

@ -57,6 +57,18 @@ class Champs::CarteChamp < Champ
type_de_champ&.parcelles_agricoles && type_de_champ.parcelles_agricoles != '0' type_de_champ&.parcelles_agricoles && type_de_champ.parcelles_agricoles != '0'
end end
def mnhn?
type_de_champ&.mnhn && type_de_champ.mnhn != '0'
end
def render_options
{
ign: Flipper.enabled?(:carte_ign, procedure),
mnhn: mnhn?,
cadastres: cadastres?
}
end
def position def position
if dossier.present? if dossier.present?
dossier.geo_position dossier.geo_position

View file

@ -56,7 +56,7 @@ class TypeDeChamp < ApplicationRecord
belongs_to :parent, class_name: 'TypeDeChamp', optional: true belongs_to :parent, class_name: 'TypeDeChamp', optional: true
has_many :types_de_champ, -> { ordered }, foreign_key: :parent_id, class_name: 'TypeDeChamp', inverse_of: :parent, dependent: :destroy has_many :types_de_champ, -> { ordered }, foreign_key: :parent_id, class_name: 'TypeDeChamp', inverse_of: :parent, dependent: :destroy
store_accessor :options, :cadastres, :quartiers_prioritaires, :parcelles_agricoles, :old_pj, :drop_down_options, :skip_pj_validation store_accessor :options, :cadastres, :quartiers_prioritaires, :parcelles_agricoles, :mnhn, :old_pj, :drop_down_options, :skip_pj_validation
has_many :revision_types_de_champ, class_name: 'ProcedureRevisionTypeDeChamp', dependent: :destroy, inverse_of: :type_de_champ has_many :revision_types_de_champ, class_name: 'ProcedureRevisionTypeDeChamp', dependent: :destroy, inverse_of: :type_de_champ
delegate :tags_for_template, to: :dynamic_type delegate :tags_for_template, to: :dynamic_type
@ -286,12 +286,11 @@ class TypeDeChamp < ApplicationRecord
:updated_at :updated_at
], ],
methods: [ methods: [
:cadastres,
:drop_down_list_value, :drop_down_list_value,
:parcelles_agricoles,
:piece_justificative_template_filename, :piece_justificative_template_filename,
:piece_justificative_template_url, :piece_justificative_template_url,
:quartiers_prioritaires :cadastres,
:mnhn
] ]
} }
TYPES_DE_CHAMP = TYPES_DE_CHAMP_BASE TYPES_DE_CHAMP = TYPES_DE_CHAMP_BASE

View file

@ -1,4 +1,4 @@
- if champ.geometry? - if champ.geometry?
= react_component("MapReader", { featureCollection: champ.to_feature_collection, ign: feature_enabled_for?(:carte_ign, champ.procedure) } ) = react_component("MapReader", { featureCollection: champ.to_feature_collection, options: champ.render_options } )
.geo-areas .geo-areas
= render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, editing: false } = render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, editing: false }

View file

@ -1,5 +1,6 @@
- preview = !champ.persisted? - preview = !champ.persisted?
= react_component("MapEditor", { featureCollection: champ.to_feature_collection, url: champs_carte_features_path(preview ? 'preview' : champ), preview: preview, hasCadastres: !!champ.cadastres?, ign: feature_enabled_for?(:carte_ign, champ.procedure) }, class: "carte-#{champ.id}") - url = champs_carte_features_path(preview ? 'preview' : champ)
= react_component("MapEditor", { featureCollection: champ.to_feature_collection, url: url, preview: preview, options: champ.render_options }, class: "carte-#{champ.id}")
.geo-areas .geo-areas
= render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, editing: true } = render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, editing: true }