-
-
Notifications
You must be signed in to change notification settings - Fork 21
/
measure.js
168 lines (147 loc) · 4.81 KB
/
measure.js
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import Overlay from 'ol/Overlay';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';
import { unByKey } from 'ol/Observable';
import { measureGeometry } from '../utils/measure';
// Store measurement overlays keyed by the ID of the feature they measure.
const measures = {};
// Store measurement listeners for changes to shapes.
const measureListeners = [];
// Remember the map, drawing layer, and system of measurement.
// See attach().
let map;
let layer;
let units;
// Maintain a counter for feature IDs.
let featureId = 0;
/**
* Get the feature ID. If no ID is set, generate one.
* @param {ol.Feature} feature The feature.
*/
function getFeatureId(feature) {
let id = feature.getId();
if (!id) {
id = `measureFeature${featureId}`;
feature.setId(id);
featureId += 1;
}
return id;
}
/**
* Update a measurement, given a geometry.
* @param {ol.Overlay} measure The measure overlay.
* @param {ol.SimpleGeometry} geom The geomtery to measure (Polygon or
* LineString).
*/
function updateMeasure(measure, geom) {
const measurement = measureGeometry(geom, units);
let coordinates;
if (geom instanceof Polygon) {
coordinates = geom.getInteriorPoint().getCoordinates();
}
if (geom instanceof LineString) {
coordinates = geom.getLastCoordinate();
}
measure.setPosition(coordinates);
const element = measure.getElement();
element.innerHTML = measurement;
}
/**
* Create a measurement overlay for a feature.
* @param {ol.Feature} feature The feature to create a measurement for.
*/
function createMeasure(feature) {
const id = getFeatureId(feature);
const element = document.createElement('div');
element.className = 'ol-tooltip ol-tooltip-measure';
measures[id] = new Overlay({
element,
offset: [0, -15],
positioning: 'bottom-center',
stopEvent: false,
});
map.addOverlay(measures[id]);
updateMeasure(measures[id], feature.getGeometry());
}
/**
* Start measuring.
* @param {ol.Feature} feature The feature being measured.
*/
export function startMeasure(feature) {
// Get the feature ID.
const id = getFeatureId(feature);
// If a measurement overlay does not exist for the feature, create it.
if (!measures[id]) {
createMeasure(feature);
}
// Listen for changes to the feature, and update its measurement.
measureListeners[id] = feature.getGeometry().on('change', e => updateMeasure(measures[id], e.target));
}
/**
* Stop measuring.
* @param {ol.Feature|bool} feature Optionally provide a feature which will be
* included in the cleanup comparison code. This is used when the `drawend`
* event fires, because the new feature is not yet added to the source layer.
*/
export function stopMeasure(feature = false) {
// Stop listening for measurement changes.
measureListeners.forEach(listener => unByKey(listener));
// Remove any overlays that no longer correspond to drawn features.
Object.keys(measures).forEach((id) => {
// If a feature with this ID exists in the source, skip it.
if (layer.getSource().getFeatureById(id)) {
return;
}
// If a feature was passed into this function, and its ID matches, skip it.
if (feature && feature.getId() === id) {
return;
}
// Remove the overlay.
map.removeOverlay(measures[id]);
delete measures[id];
});
}
// Measure behavior.
export default {
attach(instance, options = {}) {
// Save the map, drawing layer, and system of measurement for later use.
({ map, units } = instance);
({ layer } = options);
// If the drawing layer contains any features, add measurements for each.
layer.getSource().getFeatures().forEach((feature) => {
createMeasure(feature);
});
// If the instance has an Edit control, attach listeners to the map
// interactions so that we can apply measurements to the features.
if (instance.edit) {
instance.edit.addInteractionListener('drawstart', (geojson, event) => {
startMeasure(event.feature);
});
instance.edit.addInteractionListener('drawend', (geojson, event) => {
stopMeasure(event.feature);
});
instance.edit.addInteractionListener('modifystart', (geojson, event) => {
event.features.forEach((feature) => {
startMeasure(feature);
});
});
instance.edit.addInteractionListener('modifyend', () => {
stopMeasure();
});
instance.edit.addInteractionListener('translatestart', (geojson, event) => {
event.features.forEach((feature) => {
startMeasure(feature);
});
});
instance.edit.addInteractionListener('translateend', () => {
stopMeasure();
});
instance.edit.addInteractionListener('delete', () => {
stopMeasure();
});
instance.edit.addInteractionListener('disable', () => {
stopMeasure();
});
}
},
};