-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathMapWrapper.js
More file actions
133 lines (100 loc) · 3.39 KB
/
MapWrapper.js
File metadata and controls
133 lines (100 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// react
import React, { useState, useEffect, useRef } from 'react';
// openlayers
import Map from 'ol/Map'
import View from 'ol/View'
import TileLayer from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import XYZ from 'ol/source/XYZ'
import {transform} from 'ol/proj'
import {toStringXY} from 'ol/coordinate';
function MapWrapper(props) {
// set intial state
const [ map, setMap ] = useState()
const [ featuresLayer, setFeaturesLayer ] = useState()
const [ selectedCoord , setSelectedCoord ] = useState()
// pull refs
const mapElement = useRef()
// create state ref that can be accessed in OpenLayers onclick callback function
// https://stackoverflow.com/a/60643670
const mapRef = useRef()
mapRef.current = map
// initialize map on first render - logic formerly put into componentDidMount()
useEffect( () => {
// create and add vector source layer
const initalFeaturesLayer = new VectorLayer({
source: new VectorSource()
})
// create map
const initialMap = new Map({
target: mapElement.current,
layers: [
// USGS Topo
new TileLayer({
source: new XYZ({
url: 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
})
}),
// Google Maps Terrain
/* new TileLayer({
source: new XYZ({
url: 'http://mt0.google.com/vt/lyrs=p&hl=en&x={x}&y={y}&z={z}',
})
}), */
initalFeaturesLayer
],
view: new View({
projection: 'EPSG:3857',
center: [0, 0],
zoom: 2
}),
controls: []
})
// set map onclick handler
initialMap.on('click', handleMapClick)
// save map and vector layer references to state
setMap(initialMap)
setFeaturesLayer(initalFeaturesLayer)
// optional - safely reset map on unmount to prevent flickering issues - logic formerly put into componentWillUnmount()
return () => {
initialMap.setTarget(null)
setMap(null)
}
},[])
// update map if features prop changes - logic formerly put into componentDidUpdate()
useEffect( () => {
if (props.features.length) { // may be null on first render
// set features to map
featuresLayer.setSource(
new VectorSource({
features: props.features // make sure features is an array
})
)
// fit map to feature extent (with 100px of padding)
map.getView().fit(featuresLayer.getSource().getExtent(), {
padding: [100,100,100,100]
})
}
},[props.features])
// map click handler
const handleMapClick = (event) => {
// get clicked coordinate using mapRef to access current React state inside OpenLayers callback
// https://stackoverflow.com/a/60643670
const clickedCoord = mapRef.current.getCoordinateFromPixel(event.pixel);
// transform coord to EPSG 4326 standard Lat Long
const transormedCoord = transform(clickedCoord, 'EPSG:3857', 'EPSG:4326')
// set React state
setSelectedCoord( transormedCoord )
}
// render component
return (
<div>
<div ref={mapElement} className="map-container"></div>
<div className="clicked-coord-label">
<p>{ (selectedCoord) ? toStringXY(selectedCoord, 5) : '' }</p>
</div>
</div>
)
}
export default MapWrapper