Improuve mapbox utilis and shared components
This commit is contained in:
parent
3b85ade440
commit
19440afebf
14 changed files with 168 additions and 136 deletions
|
@ -1,3 +1,5 @@
|
|||
@import "colors";
|
||||
|
||||
.areas-title {
|
||||
font-weight: bold;
|
||||
margin-top: 5px;
|
||||
|
@ -15,3 +17,38 @@
|
|||
.form [data-react-class='MapEditor'] [data-reach-combobox-input] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.map-style-control {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
left: 10px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cadastres-selection-control {
|
||||
position: absolute;
|
||||
top: 135px;
|
||||
left: 10px;
|
||||
|
||||
button {
|
||||
&.on,
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
input[type=text]:not([data-address='true']),
|
||||
input[type=text],
|
||||
input[type=email],
|
||||
input[type=password],
|
||||
input[type=date],
|
||||
|
@ -178,6 +178,10 @@
|
|||
&.small-margin {
|
||||
margin-bottom: $default-spacer;
|
||||
}
|
||||
|
||||
&.no-margin {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.add-row {
|
||||
|
@ -475,7 +479,7 @@
|
|||
}
|
||||
|
||||
[data-react-class]:not([data-react-class="ComboMultipleDropdownList"]) {
|
||||
[data-reach-combobox-input] {
|
||||
[data-reach-combobox-input]:not(.no-margin) {
|
||||
margin-bottom: $default-fields-spacer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,15 @@ function ComboAdresseSearch({
|
|||
hiddenFieldId,
|
||||
onChange,
|
||||
transformResult = ({ properties: { label } }) => [label, label],
|
||||
allowInputValues = true
|
||||
allowInputValues = true,
|
||||
className
|
||||
}) {
|
||||
const transformResults = useCallback((_, { features }) => features);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ComboSearch
|
||||
className={className}
|
||||
placeholder={placeholder}
|
||||
required={mandatory}
|
||||
hiddenFieldId={hiddenFieldId}
|
||||
|
@ -33,6 +35,7 @@ function ComboAdresseSearch({
|
|||
}
|
||||
|
||||
ComboAdresseSearch.propTypes = {
|
||||
className: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
mandatory: PropTypes.bool,
|
||||
hiddenFieldId: PropTypes.string,
|
||||
|
|
|
@ -25,7 +25,8 @@ function ComboSearch({
|
|||
minimumInputLength,
|
||||
transformResult,
|
||||
allowInputValues = false,
|
||||
transformResults = defaultTransformResults
|
||||
transformResults = defaultTransformResults,
|
||||
className
|
||||
}) {
|
||||
const label = scope;
|
||||
const hiddenValueField = useMemo(
|
||||
|
@ -93,6 +94,7 @@ function ComboSearch({
|
|||
return (
|
||||
<Combobox aria-label={label} onSelect={handleOnSelect}>
|
||||
<ComboboxInput
|
||||
className={className}
|
||||
placeholder={placeholder}
|
||||
onChange={handleOnChange}
|
||||
value={value}
|
||||
|
@ -134,7 +136,8 @@ ComboSearch.propTypes = {
|
|||
transformResult: PropTypes.func,
|
||||
transformResults: PropTypes.func,
|
||||
allowInputValues: PropTypes.bool,
|
||||
onChange: PropTypes.func
|
||||
onChange: PropTypes.func,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
export default ComboSearch;
|
||||
|
|
69
app/javascript/components/shared/mapbox/MapStyleControl.jsx
Normal file
69
app/javascript/components/shared/mapbox/MapStyleControl.jsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import React, { useMemo, useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getMapStyle } from './styles';
|
||||
import ortho from './styles/images/preview-ortho.png';
|
||||
import vector from './styles/images/preview-vector.png';
|
||||
|
||||
const STYLES = {
|
||||
ortho: {
|
||||
title: 'Satellite',
|
||||
preview: ortho,
|
||||
color: '#fff'
|
||||
},
|
||||
vector: {
|
||||
title: 'Vectoriel',
|
||||
preview: vector,
|
||||
color: '#000'
|
||||
},
|
||||
ign: {
|
||||
title: 'Carte IGN',
|
||||
preview: vector,
|
||||
color: '#000'
|
||||
}
|
||||
};
|
||||
|
||||
function getNextStyle(style) {
|
||||
const styleNames = Object.keys(STYLES);
|
||||
const index = styleNames.indexOf(style) + 1;
|
||||
if (index === styleNames.length) {
|
||||
return styleNames[0];
|
||||
}
|
||||
return styleNames[index];
|
||||
}
|
||||
|
||||
export function useMapStyle(
|
||||
optionalLayers,
|
||||
{ onStyleChange, cadastreEnabled }
|
||||
) {
|
||||
const [styleId, setStyle] = useState('ortho');
|
||||
const style = useMemo(() => getMapStyle(styleId, optionalLayers), [
|
||||
styleId,
|
||||
optionalLayers
|
||||
]);
|
||||
|
||||
useEffect(() => onStyleChange(), [styleId, cadastreEnabled]);
|
||||
|
||||
return [style, setStyle];
|
||||
}
|
||||
|
||||
function MapStyleControl({ style, setStyle }) {
|
||||
const nextStyle = getNextStyle(style);
|
||||
const { title, preview, color } = STYLES[nextStyle];
|
||||
|
||||
return (
|
||||
<div className="map-style-control">
|
||||
<button type="button" onClick={() => setStyle(nextStyle)}>
|
||||
<img alt={title} src={preview} />
|
||||
<div style={{ color }}>{title}</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
MapStyleControl.propTypes = {
|
||||
style: PropTypes.string,
|
||||
setStyle: PropTypes.func
|
||||
};
|
||||
|
||||
export default MapStyleControl;
|
|
@ -1,3 +0,0 @@
|
|||
import ReactMapboxGl from 'react-mapbox-gl';
|
||||
|
||||
export default ReactMapboxGl({});
|
|
@ -1,81 +0,0 @@
|
|||
import React from 'react';
|
||||
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',
|
||||
preview: ortho,
|
||||
color: '#fff'
|
||||
},
|
||||
vector: {
|
||||
title: 'Vectoriel',
|
||||
preview: vector,
|
||||
color: '#000'
|
||||
}
|
||||
};
|
||||
|
||||
const IGN_STYLES = {
|
||||
...STYLES,
|
||||
ign: {
|
||||
title: 'Carte IGN',
|
||||
preview: vector,
|
||||
color: '#000'
|
||||
}
|
||||
};
|
||||
|
||||
function getNextStyle(style, ign) {
|
||||
const styles = Object.keys(ign ? IGN_STYLES : STYLES);
|
||||
let index = styles.indexOf(style) + 1;
|
||||
if (index === styles.length) {
|
||||
return styles[0];
|
||||
}
|
||||
return styles[index];
|
||||
}
|
||||
|
||||
function SwitchMapStyle({ style, setStyle, ign }) {
|
||||
const nextStyle = getNextStyle(style, ign);
|
||||
const { title, preview, color } = (ign ? IGN_STYLES : STYLES)[nextStyle];
|
||||
|
||||
const imgStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
cursor: 'pointer'
|
||||
};
|
||||
|
||||
const textStyle = {
|
||||
position: 'relative',
|
||||
bottom: '26px',
|
||||
left: '4px',
|
||||
color
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="style-switch"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0
|
||||
}}
|
||||
onClick={() => setStyle(nextStyle)}
|
||||
>
|
||||
<div className="switch-style mapboxgl-ctrl-swith-map-style">
|
||||
<img alt={title} style={imgStyle} src={preview} />
|
||||
<div className="text" style={textStyle}>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SwitchMapStyle.propTypes = {
|
||||
style: PropTypes.string,
|
||||
setStyle: PropTypes.func,
|
||||
ign: PropTypes.bool
|
||||
};
|
||||
|
||||
export default SwitchMapStyle;
|
|
@ -1,4 +1,4 @@
|
|||
import cadastreLayers from './cadastre-layers';
|
||||
import cadastreLayers from './layers/cadastre';
|
||||
|
||||
const IGN_TOKEN = 'rc1egnbeoss72hxvd143tbyk';
|
||||
|
||||
|
@ -138,7 +138,16 @@ function rasterSource(tiles, attribution) {
|
|||
};
|
||||
}
|
||||
|
||||
export function buildLayers(ids) {
|
||||
function rasterLayer(source) {
|
||||
return {
|
||||
id: source,
|
||||
source,
|
||||
type: 'raster',
|
||||
paint: { 'raster-resampling': 'linear' }
|
||||
};
|
||||
}
|
||||
|
||||
export function buildOptionalLayers(ids) {
|
||||
return OPTIONAL_LAYERS.filter(({ id }) => ids.includes(id))
|
||||
.flatMap(({ layers }) => layers)
|
||||
.flatMap(([, code]) =>
|
||||
|
@ -148,15 +157,6 @@ export function buildLayers(ids) {
|
|||
);
|
||||
}
|
||||
|
||||
export function rasterLayer(source) {
|
||||
return {
|
||||
id: source,
|
||||
source,
|
||||
type: 'raster',
|
||||
paint: { 'raster-resampling': 'linear' }
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
version: 8,
|
||||
metadat: {
|
||||
|
|
|
@ -1,29 +1,27 @@
|
|||
import baseStyle, { rasterLayer, buildLayers } from './base';
|
||||
import orthoStyle from './ortho-style';
|
||||
import vectorStyle from './vector-style';
|
||||
import baseStyle, { buildOptionalLayers } from './base';
|
||||
import orthoStyle from './layers/ortho';
|
||||
import vectorStyle from './layers/vector';
|
||||
import ignLayers from './layers/ign';
|
||||
|
||||
export function getMapStyle(style, optionalLayers) {
|
||||
const mapStyle = { ...baseStyle };
|
||||
export function getMapStyle(id, optionalLayers) {
|
||||
const style = { ...baseStyle, id };
|
||||
|
||||
switch (style) {
|
||||
switch (id) {
|
||||
case 'ortho':
|
||||
mapStyle.layers = orthoStyle;
|
||||
mapStyle.id = 'ortho';
|
||||
mapStyle.name = 'Photographies aériennes';
|
||||
style.layers = orthoStyle;
|
||||
style.name = 'Photographies aériennes';
|
||||
break;
|
||||
case 'vector':
|
||||
mapStyle.layers = vectorStyle;
|
||||
mapStyle.id = 'vector';
|
||||
mapStyle.name = 'Carte OSM';
|
||||
style.layers = vectorStyle;
|
||||
style.name = 'Carte OSM';
|
||||
break;
|
||||
case 'ign':
|
||||
mapStyle.layers = [rasterLayer('plan-ign')];
|
||||
mapStyle.id = 'ign';
|
||||
mapStyle.name = 'Carte IGN';
|
||||
style.layers = ignLayers;
|
||||
style.name = 'Carte IGN';
|
||||
break;
|
||||
}
|
||||
|
||||
mapStyle.layers = mapStyle.layers.concat(buildLayers(optionalLayers));
|
||||
style.layers = style.layers.concat(buildOptionalLayers(optionalLayers));
|
||||
|
||||
return mapStyle;
|
||||
return style;
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ export default [
|
|||
type: 'fill',
|
||||
source: 'cadastre',
|
||||
'source-layer': 'parcelles',
|
||||
filter: ['==', 'id', ''],
|
||||
filter: ['in', 'id', ''],
|
||||
paint: {
|
||||
'fill-color': 'rgba(1, 129, 0, 1)',
|
||||
'fill-opacity': 0.7
|
|
@ -0,0 +1,8 @@
|
|||
export default [
|
||||
{
|
||||
id: 'ign',
|
||||
source: 'plan-ign',
|
||||
type: 'raster',
|
||||
paint: { 'raster-resampling': 'linear' }
|
||||
}
|
||||
];
|
|
@ -1,5 +1,4 @@
|
|||
import { LngLatBounds } from 'mapbox-gl';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function getBounds(geometry) {
|
||||
const bbox = new LngLatBounds();
|
||||
|
@ -18,15 +17,9 @@ export function getBounds(geometry) {
|
|||
return bbox;
|
||||
}
|
||||
|
||||
export function fitBounds(map, feature) {
|
||||
if (map) {
|
||||
map.fitBounds(getBounds(feature.geometry), { padding: 100 });
|
||||
}
|
||||
}
|
||||
|
||||
export function findFeature(featureCollection, id) {
|
||||
export function findFeature(featureCollection, value, property = 'id') {
|
||||
return featureCollection.features.find(
|
||||
(feature) => feature.properties.id === id
|
||||
(feature) => feature.properties[property] === value
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -48,19 +41,10 @@ export function filterFeatureCollectionByGeometryType(featureCollection, type) {
|
|||
};
|
||||
}
|
||||
|
||||
export function noop() {}
|
||||
|
||||
export function generateId() {
|
||||
return Math.random().toString(20).substr(2, 6);
|
||||
}
|
||||
|
||||
export function useEvent(eventName, callback) {
|
||||
return useEffect(() => {
|
||||
addEventListener(eventName, callback);
|
||||
return () => removeEventListener(eventName, callback);
|
||||
}, [eventName, callback]);
|
||||
}
|
||||
|
||||
export function getCenter(geometry, lngLat) {
|
||||
const bbox = new LngLatBounds();
|
||||
|
||||
|
@ -76,3 +60,13 @@ export function getCenter(geometry, lngLat) {
|
|||
return bbox.getCenter();
|
||||
}
|
||||
}
|
||||
|
||||
export function defer() {
|
||||
const deferred = {};
|
||||
const promise = new Promise(function (resolve, reject) {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
deferred.promise = promise;
|
||||
return deferred;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue