@performant-software/geospatial 1.1.3-beta.4 → 1.1.3-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/main.css +37 -3
- package/package.json +3 -2
- package/src/components/GeoJsonLayer.js +118 -0
- package/src/components/LayerMenu.css +38 -0
- package/src/components/LayerMenu.js +154 -0
- package/src/components/LayerMenuContainer.js +80 -0
- package/src/components/MapDraw.css +1 -4
- package/src/components/MapDraw.js +40 -24
- package/src/components/RasterLayer.js +36 -0
- package/src/index.js +3 -0
- package/types/components/GeoJsonLayer.js.flow +118 -0
- package/types/components/LayerMenu.js.flow +154 -0
- package/types/components/LayerMenuContainer.js.flow +80 -0
- package/types/components/MapDraw.js.flow +40 -24
- package/types/components/RasterLayer.js.flow +36 -0
- package/types/index.js.flow +3 -0
- package/webpack.config.js +4 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Children,
|
|
5
|
+
cloneElement,
|
|
6
|
+
useEffect,
|
|
7
|
+
useState
|
|
8
|
+
} from 'react';
|
|
9
|
+
import { createPortal } from 'react-dom';
|
|
10
|
+
import { MapboxMap, useControl } from 'react-map-gl';
|
|
11
|
+
import { IControl } from 'maplibre-gl';
|
|
12
|
+
import _ from 'underscore';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Layer control implementation.
|
|
16
|
+
*/
|
|
17
|
+
class LayerControl implements IControl {
|
|
18
|
+
_map: MapboxMap = null;
|
|
19
|
+
_container: HTMLElement;
|
|
20
|
+
_redraw: () => void;
|
|
21
|
+
|
|
22
|
+
constructor(redraw: () => void) {
|
|
23
|
+
this._redraw = redraw;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onAdd(map) {
|
|
27
|
+
this._map = map;
|
|
28
|
+
map.on('move', this._redraw);
|
|
29
|
+
|
|
30
|
+
this._container = document.createElement('div');
|
|
31
|
+
this._container.className = 'maplibregl-ctrl-group maplibregl-ctrl';
|
|
32
|
+
this._redraw();
|
|
33
|
+
return this._container;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onRemove() {
|
|
37
|
+
this._container.remove();
|
|
38
|
+
this._map.off('move', this._redraw);
|
|
39
|
+
this._map = null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getMap() {
|
|
43
|
+
return this._map;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getElement() {
|
|
47
|
+
return this._container;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Layer container.
|
|
53
|
+
*
|
|
54
|
+
* @param props
|
|
55
|
+
*
|
|
56
|
+
* @returns {React$Portal}
|
|
57
|
+
*/
|
|
58
|
+
const LayerMenuContainer = (props) => {
|
|
59
|
+
const [, setVersion] = useState(0);
|
|
60
|
+
|
|
61
|
+
const ctrl = useControl(() => {
|
|
62
|
+
const forceUpdate = () => setVersion((v) => v + 1);
|
|
63
|
+
return new LayerControl(forceUpdate);
|
|
64
|
+
}, { position: props.position });
|
|
65
|
+
|
|
66
|
+
const map = ctrl.getMap();
|
|
67
|
+
|
|
68
|
+
const children = Children.map(_.compact(props.children), (child) => cloneElement(child, { map }));
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (props.mapRef) {
|
|
72
|
+
// eslint-disable-next-line no-param-reassign
|
|
73
|
+
props.mapRef.current = map;
|
|
74
|
+
}
|
|
75
|
+
}, [map, props.mapRef]);
|
|
76
|
+
|
|
77
|
+
return map && createPortal(children, ctrl.getElement());
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default LayerMenuContainer;
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import MapboxDraw from '@mapbox/mapbox-gl-draw';
|
|
4
4
|
import {
|
|
5
5
|
bbox,
|
|
6
|
+
bboxPolygon,
|
|
7
|
+
buffer,
|
|
6
8
|
feature,
|
|
7
9
|
type FeatureCollection,
|
|
8
10
|
type GeometryCollection
|
|
@@ -16,18 +18,22 @@ import React, {
|
|
|
16
18
|
useState,
|
|
17
19
|
type Node
|
|
18
20
|
} from 'react';
|
|
19
|
-
import Map, {
|
|
21
|
+
import Map, { MapRef } from 'react-map-gl';
|
|
20
22
|
import _ from 'underscore';
|
|
21
23
|
import DrawControl from './DrawControl';
|
|
22
24
|
import './MapDraw.css';
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
26
|
+
// Override the MapboxDraw components to use MapLibre styles
|
|
27
|
+
MapboxDraw.constants.classes.CONTROL_BASE = 'maplibregl-ctrl';
|
|
28
|
+
MapboxDraw.constants.classes.CONTROL_PREFIX = 'maplibregl-ctrl-';
|
|
29
|
+
MapboxDraw.constants.classes.CONTROL_GROUP = 'maplibregl-ctrl-group';
|
|
29
30
|
|
|
30
31
|
type Props = {
|
|
32
|
+
/**
|
|
33
|
+
* The number of miles to buffer the GeoJSON data.
|
|
34
|
+
*/
|
|
35
|
+
buffer?: number,
|
|
36
|
+
|
|
31
37
|
/**
|
|
32
38
|
* Additional child nodes to render.
|
|
33
39
|
*/
|
|
@@ -38,8 +44,6 @@ type Props = {
|
|
|
38
44
|
*/
|
|
39
45
|
data: GeometryCollection | FeatureCollection,
|
|
40
46
|
|
|
41
|
-
layers: Array<LayerType>,
|
|
42
|
-
|
|
43
47
|
/**
|
|
44
48
|
* URL of the map style to render. This URL should contain any necessary API keys.
|
|
45
49
|
*/
|
|
@@ -55,9 +59,17 @@ type Props = {
|
|
|
55
59
|
/**
|
|
56
60
|
* Map style object.
|
|
57
61
|
*/
|
|
58
|
-
style?: any
|
|
62
|
+
style?: any,
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The time in milliseconds to zoom into the location.
|
|
66
|
+
*/
|
|
67
|
+
zoomDuration?: number
|
|
59
68
|
};
|
|
60
69
|
|
|
70
|
+
const DEFAULT_BUFFER = 2;
|
|
71
|
+
const DEFAULT_ZOOM_DELAY = 1000;
|
|
72
|
+
|
|
61
73
|
const GeometryTypes = {
|
|
62
74
|
geometryCollection: 'GeometryCollection',
|
|
63
75
|
point: 'Point'
|
|
@@ -73,8 +85,6 @@ const MapDraw = (props: Props) => {
|
|
|
73
85
|
const drawRef = useRef<MapboxDraw>();
|
|
74
86
|
const mapRef = useRef<MapRef>();
|
|
75
87
|
|
|
76
|
-
const geojsonLayers = useMemo(() => _.filter(props.layers, (layer) => !!layer.data), [props.layers]);
|
|
77
|
-
|
|
78
88
|
/**
|
|
79
89
|
* Calls the onChange prop with all of the geometries in the current drawer.
|
|
80
90
|
*
|
|
@@ -96,14 +106,24 @@ const MapDraw = (props: Props) => {
|
|
|
96
106
|
*/
|
|
97
107
|
useEffect(() => {
|
|
98
108
|
if (loaded && props.data) {
|
|
99
|
-
//
|
|
100
|
-
const
|
|
109
|
+
// Convert the GeoJSON into a bounding box
|
|
110
|
+
const box = bbox(props.data);
|
|
111
|
+
|
|
112
|
+
// Convert the bounding box to a polygon
|
|
113
|
+
const polygon = bboxPolygon(box);
|
|
114
|
+
|
|
115
|
+
// Create a buffer around the polygon
|
|
116
|
+
const polygonBuffer = buffer(polygon, props.buffer, { units: 'miles' });
|
|
101
117
|
|
|
118
|
+
// Convert the buffer to a bounding box
|
|
119
|
+
const boundingBox = bbox(polygonBuffer);
|
|
120
|
+
|
|
121
|
+
// Sets the bounding box for the current geometry.
|
|
102
122
|
if (_.every(boundingBox, _.isFinite)) {
|
|
103
123
|
const [minLng, minLat, maxLng, maxLat] = boundingBox;
|
|
104
124
|
const bounds = [[minLng, minLat], [maxLng, maxLat]];
|
|
105
125
|
|
|
106
|
-
mapRef.current.fitBounds(bounds, {
|
|
126
|
+
mapRef.current.fitBounds(bounds, { duration: props.zoomDuration });
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
// Handle special cases for geometry collection (not supported by mabox-gl-draw) and point
|
|
@@ -138,20 +158,16 @@ const MapDraw = (props: Props) => {
|
|
|
138
158
|
onCreate={onChange}
|
|
139
159
|
onUpdate={onChange}
|
|
140
160
|
onDelete={onChange}
|
|
161
|
+
position='bottom-left'
|
|
141
162
|
/>
|
|
142
|
-
{ _.map(geojsonLayers, (layer) => (
|
|
143
|
-
<Source
|
|
144
|
-
type='geojson'
|
|
145
|
-
data={layer.data}
|
|
146
|
-
>
|
|
147
|
-
<Layer
|
|
148
|
-
{..._.omit(layer, 'data')}
|
|
149
|
-
/>
|
|
150
|
-
</Source>
|
|
151
|
-
))}
|
|
152
163
|
{ props.children }
|
|
153
164
|
</Map>
|
|
154
165
|
);
|
|
155
166
|
};
|
|
156
167
|
|
|
168
|
+
MapDraw.defaultProps = {
|
|
169
|
+
buffer: DEFAULT_BUFFER,
|
|
170
|
+
zoomDuration: DEFAULT_ZOOM_DELAY
|
|
171
|
+
};
|
|
172
|
+
|
|
157
173
|
export default MapDraw;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Layer, Source } from 'react-map-gl';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
maxzoom?: number,
|
|
8
|
+
minzoom?: number,
|
|
9
|
+
opacity?: number,
|
|
10
|
+
tileSize?: number,
|
|
11
|
+
url?: string,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const RasterLayer = (props: Props) => (
|
|
15
|
+
<Source
|
|
16
|
+
tileSize={props.tileSize}
|
|
17
|
+
tiles={[props.url]}
|
|
18
|
+
type='raster'
|
|
19
|
+
>
|
|
20
|
+
<Layer
|
|
21
|
+
type='raster'
|
|
22
|
+
paint={{
|
|
23
|
+
'raster-opacity': props.opacity
|
|
24
|
+
}}
|
|
25
|
+
minzoom={props.minzoom}
|
|
26
|
+
maxzoom={props.maxzoom}
|
|
27
|
+
/>
|
|
28
|
+
</Source>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
RasterLayer.defaultProps = {
|
|
32
|
+
opacity: 0.7,
|
|
33
|
+
tileSize: 256
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default RasterLayer;
|
package/types/index.js.flow
CHANGED
|
@@ -2,4 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// Components
|
|
4
4
|
export { default as DrawControl } from './components/DrawControl';
|
|
5
|
+
export { default as GeoJsonLayer } from './components/GeoJsonLayer';
|
|
6
|
+
export { default as LayerMenu } from './components/LayerMenu';
|
|
5
7
|
export { default as MapDraw } from './components/MapDraw';
|
|
8
|
+
export { default as RasterLayer } from './components/RasterLayer';
|
package/webpack.config.js
CHANGED
|
@@ -7,6 +7,10 @@ module.exports = configure(__dirname, {
|
|
|
7
7
|
'./@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css$': path.resolve(
|
|
8
8
|
__dirname,
|
|
9
9
|
'../../node_modules/@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
|
|
10
|
+
),
|
|
11
|
+
'./maplibre-gl/dist/maplibre-gl.css$': path.resolve(
|
|
12
|
+
__dirname,
|
|
13
|
+
'../../node_modules/maplibre-gl/dist/maplibre-gl.css'
|
|
10
14
|
)
|
|
11
15
|
}
|
|
12
16
|
}
|