263 lines
7.4 KiB
JavaScript
263 lines
7.4 KiB
JavaScript
import React, { useState } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import ReactMapboxGl, { ZoomControl } from 'react-mapbox-gl';
|
|
import DrawControl from 'react-mapbox-gl-draw';
|
|
import {
|
|
CursorClickIcon,
|
|
PlusIcon,
|
|
LocationMarkerIcon
|
|
} from '@heroicons/react/outline';
|
|
import CoordinateInput from 'react-coordinate-input';
|
|
import { fire } from '@utils';
|
|
import VisuallyHidden from '@reach/visually-hidden';
|
|
import { useId } from '@reach/auto-id';
|
|
import 'mapbox-gl/dist/mapbox-gl.css';
|
|
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
|
|
|
|
import MapStyleControl, { useMapStyle } from '../shared/mapbox/MapStyleControl';
|
|
import { FlashMessage } from '../shared/FlashMessage';
|
|
|
|
import ComboAdresseSearch from '../ComboAdresseSearch';
|
|
import { useMapboxEditor } from './useMapboxEditor';
|
|
|
|
const Mapbox = ReactMapboxGl({});
|
|
|
|
function MapEditor({ featureCollection, url, options, preview }) {
|
|
const [cadastreEnabled, setCadastreEnabled] = useState(false);
|
|
const [coords, setCoords] = useState([1.7, 46.9]);
|
|
const [zoom, setZoom] = useState([5]);
|
|
const {
|
|
isSupported,
|
|
error,
|
|
inputs,
|
|
onLoad,
|
|
onStyleChange,
|
|
onFileChange,
|
|
drawRef,
|
|
createFeatures,
|
|
updateFeatures,
|
|
deleteFeatures,
|
|
addInputFile,
|
|
removeInputFile
|
|
} = useMapboxEditor(featureCollection, {
|
|
url,
|
|
enabled: !preview,
|
|
cadastreEnabled
|
|
});
|
|
const { style, layers, setStyle, setLayerEnabled, setLayerOpacity } =
|
|
useMapStyle(options.layers, {
|
|
onStyleChange,
|
|
cadastreEnabled
|
|
});
|
|
|
|
if (!isSupported) {
|
|
return (
|
|
<p>
|
|
Nous ne pouvons pas afficher notre éditeur de carte car il est
|
|
imcompatible avec votre navigateur. Nous vous conseillons de le mettre à
|
|
jour ou utiliser les dernières versions de Chrome, Firefox ou Safari
|
|
</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{error && <FlashMessage message={error} level="alert" fixed={true} />}
|
|
<div>
|
|
<p style={{ marginBottom: '20px' }}>
|
|
Besoin d'aide ?
|
|
<a
|
|
href="https://doc.demarches-simplifiees.fr/pour-aller-plus-loin/cartographie"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
>
|
|
consulter les tutoriels video
|
|
</a>
|
|
</p>
|
|
</div>
|
|
<div className="file-import" style={{ marginBottom: '10px' }}>
|
|
<button className="button send primary" onClick={addInputFile}>
|
|
Ajouter un fichier GPX ou KML
|
|
</button>
|
|
<div>
|
|
{inputs.map((input) => (
|
|
<div key={input.id}>
|
|
<input
|
|
title="Choisir un fichier gpx ou kml"
|
|
style={{ marginTop: '15px' }}
|
|
id={input.id}
|
|
type="file"
|
|
accept=".gpx, .kml"
|
|
disabled={input.disabled}
|
|
onChange={(e) => onFileChange(e, input.id)}
|
|
/>
|
|
{input.hasValue && (
|
|
<span
|
|
title="Supprimer le fichier"
|
|
className="icon refuse"
|
|
style={{
|
|
cursor: 'pointer'
|
|
}}
|
|
onClick={(e) => removeInputFile(e, input.id)}
|
|
></span>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div
|
|
style={{
|
|
marginBottom: '10px'
|
|
}}
|
|
>
|
|
<ComboAdresseSearch
|
|
className="no-margin"
|
|
placeholder="Rechercher une adresse : saisissez au moins 2 caractères"
|
|
allowInputValues={false}
|
|
onChange={(_, { geometry: { coordinates } }) => {
|
|
setCoords(coordinates);
|
|
setZoom([17]);
|
|
}}
|
|
/>
|
|
</div>
|
|
<Mapbox
|
|
onStyleLoad={(map) => onLoad(map)}
|
|
center={coords}
|
|
zoom={zoom}
|
|
style={style}
|
|
containerStyle={{ height: '500px' }}
|
|
>
|
|
{!cadastreEnabled && (
|
|
<DrawControl
|
|
ref={drawRef}
|
|
onDrawCreate={createFeatures}
|
|
onDrawUpdate={updateFeatures}
|
|
onDrawDelete={deleteFeatures}
|
|
displayControlsDefault={false}
|
|
controls={{
|
|
point: true,
|
|
line_string: true,
|
|
polygon: true,
|
|
trash: true
|
|
}}
|
|
/>
|
|
)}
|
|
<MapStyleControl
|
|
style={style.id}
|
|
layers={layers}
|
|
setStyle={setStyle}
|
|
setLayerEnabled={setLayerEnabled}
|
|
setLayerOpacity={setLayerOpacity}
|
|
/>
|
|
<ZoomControl />
|
|
{options.layers.includes('cadastres') && (
|
|
<div className="cadastres-selection-control mapboxgl-ctrl-group">
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
setCadastreEnabled((cadastreEnabled) => !cadastreEnabled)
|
|
}
|
|
title="Sélectionner les parcelles cadastrales"
|
|
className={cadastreEnabled ? 'on' : ''}
|
|
>
|
|
<CursorClickIcon className="icon-size" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</Mapbox>
|
|
<PointInput />
|
|
</>
|
|
);
|
|
}
|
|
|
|
function PointInput() {
|
|
const inputId = useId();
|
|
const [value, setValue] = useState('');
|
|
const [feature, setFeature] = useState(null);
|
|
const getCurrentPosition = () => {
|
|
navigator.geolocation &&
|
|
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
|
setValue(
|
|
`${coords.latitude.toPrecision(6)}, ${coords.longitude.toPrecision(
|
|
6
|
|
)}`
|
|
);
|
|
});
|
|
};
|
|
const addPoint = () => {
|
|
if (feature) {
|
|
fire(document, 'map:feature:create', feature);
|
|
setValue('');
|
|
setFeature(null);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<label
|
|
className="areas-title mt-1"
|
|
htmlFor={inputId}
|
|
style={{ fontSize: '16px' }}
|
|
>
|
|
Ajouter un point sur la carte
|
|
</label>
|
|
<div className="flex align-center mt-1 mb-2">
|
|
{navigator.geolocation ? (
|
|
<button
|
|
type="button"
|
|
className="button mr-1"
|
|
onClick={getCurrentPosition}
|
|
title="Localiser votre position"
|
|
>
|
|
<VisuallyHidden>Localiser votre position</VisuallyHidden>
|
|
<LocationMarkerIcon className="icon-size-big" />
|
|
</button>
|
|
) : null}
|
|
<CoordinateInput
|
|
id={inputId}
|
|
className="m-0 mr-1"
|
|
value={value}
|
|
onChange={(value, { dd }) => {
|
|
setValue(value);
|
|
setFeature(
|
|
dd.length
|
|
? {
|
|
type: 'Feature',
|
|
geometry: {
|
|
type: 'Point',
|
|
coordinates: dd.reverse()
|
|
},
|
|
properties: {}
|
|
}
|
|
: null
|
|
);
|
|
}}
|
|
/>
|
|
<button
|
|
type="button"
|
|
className="button"
|
|
onClick={addPoint}
|
|
disabled={!feature}
|
|
title="Ajouter le point avec les coordonnées saisies sur la carte"
|
|
>
|
|
<VisuallyHidden>
|
|
Ajouter le point avec les coordonnées saisies sur la carte
|
|
</VisuallyHidden>
|
|
<PlusIcon className="icon-size-big" />
|
|
</button>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
MapEditor.propTypes = {
|
|
featureCollection: PropTypes.shape({
|
|
bbox: PropTypes.array,
|
|
features: PropTypes.array
|
|
}),
|
|
url: PropTypes.string,
|
|
preview: PropTypes.bool,
|
|
options: PropTypes.shape({ layers: PropTypes.array })
|
|
};
|
|
|
|
export default MapEditor;
|