@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.
@@ -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, { Layer, MapRef, Source } from 'react-map-gl';
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
- type LayerType = {
25
- id?: string | number,
26
- type: string,
27
- data: any
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
- // Sets the bounding box for the current geometry.
100
- const boundingBox = bbox(props.data);
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, { padding: 40, duration: 1000 });
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;
@@ -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
  }