Skip to content

Commit 585e05f

Browse files
authored
feat: add ruler to measure distances (#279)
2 parents df5bfe2 + 0a7f72c commit 585e05f

File tree

10 files changed

+134
-6
lines changed

10 files changed

+134
-6
lines changed

assets/fonts/meshviewer.ttf

464 Bytes
Binary file not shown.

assets/fonts/meshviewer.woff

2.02 KB
Binary file not shown.

assets/fonts/meshviewer.woff2

816 Bytes
Binary file not shown.

assets/icons/icon.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ $cache-breaker: string.unique-id();
2626
font-variant: normal;
2727
font-weight: normal;
2828
line-height: 1;
29-
speak: none;
3029
text-rendering: auto;
3130
text-transform: none;
3231
vertical-align: 0;
@@ -56,3 +55,4 @@ $cache-breaker: string.unique-id();
5655
@include icon.icon("arrow-right-c", "\f10b");
5756
@include icon.icon("full-enter", "\e901");
5857
@include icon.icon("full-exit", "\e900");
58+
@include icon.icon("ruler", "\f333");

lib/map/button.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ let CoordsPickerButton = ButtonBase.extend({
6363
},
6464
});
6565

66+
let RulerButton = ButtonBase.extend({
67+
onAdd: function () {
68+
let button = L.DomUtil.create("button", "ion-ruler");
69+
button.setAttribute("aria-label", _.t("button.ruler"));
70+
L.DomEvent.disableClickPropagation(button);
71+
L.DomEvent.addListener(button, "click", this.onClick, this);
72+
73+
this.button = button;
74+
75+
return button;
76+
},
77+
78+
onClick: function (e) {
79+
L.DomEvent.stopPropagation(e);
80+
this.f(!this.active);
81+
},
82+
});
83+
6684
export const Button = function (map, buttons) {
6785
let userLocation;
6886
const self = {
@@ -103,6 +121,99 @@ export const Button = function (map, buttons) {
103121
}
104122
});
105123

124+
// Ruler / measure distances button
125+
let rulerButton = new RulerButton(function (activate) {
126+
if (activate) {
127+
enableRuler();
128+
} else {
129+
disableRuler();
130+
}
131+
});
132+
133+
let rulerLayerGroup = null;
134+
let rulerMarkers = [];
135+
let rulerLines = null;
136+
let rulerLinesBg = null;
137+
let rulerDistancePopups = [];
138+
139+
function formatDistance(m) {
140+
if (m >= 1000) {
141+
return (m / 1000).toFixed(2) + " km";
142+
}
143+
return Math.round(m) + " m";
144+
}
145+
146+
function enableRuler() {
147+
// create layer group for ruler markers/lines
148+
rulerLayerGroup = L.layerGroup().addTo(map);
149+
rulerLinesBg = L.polyline([], {
150+
color: "#fff",
151+
weight: 3,
152+
}).addTo(rulerLayerGroup);
153+
rulerLines = L.polyline([], { color: "#000", weight: 2, dashArray: "4" }).addTo(rulerLayerGroup);
154+
rulerMarkers = [];
155+
rulerDistancePopups = [];
156+
157+
map.getContainer().classList.add("measure-active");
158+
map.on("click", onRulerClick);
159+
rulerButton.set(true);
160+
}
161+
162+
function disableRuler() {
163+
map.getContainer().classList.remove("measure-active");
164+
map.off("click", onRulerClick);
165+
166+
if (rulerLayerGroup) {
167+
map.removeLayer(rulerLayerGroup);
168+
rulerLayerGroup = null;
169+
}
170+
rulerMarkers = [];
171+
rulerLines = null;
172+
rulerLinesBg = null;
173+
rulerDistancePopups = [];
174+
175+
rulerButton.set(false);
176+
}
177+
178+
function onRulerClick(e) {
179+
let latlng = e.latlng;
180+
let config = window.config;
181+
let marker = L.circleMarker(latlng, {
182+
radius: 5,
183+
color: config.map && config.map.labelNewColor ? config.map.labelNewColor : "#333",
184+
}).addTo(rulerLayerGroup);
185+
rulerMarkers.push(marker);
186+
187+
// add to polyline
188+
let latlngs = rulerLines.getLatLngs();
189+
latlngs.push(latlng);
190+
rulerLines.setLatLngs(latlngs);
191+
rulerLinesBg.setLatLngs(latlngs);
192+
193+
// compute segment distance and total distance
194+
let last = null;
195+
if (latlngs.length >= 2) {
196+
last = latlngs[latlngs.length - 2];
197+
let segDist = latlng.distanceTo(latlngs[latlngs.length - 2]);
198+
// show popup at midpoint for segment
199+
let midLat = (latlng.lat + last.lat) / 2;
200+
let midLng = (latlng.lng + last.lng) / 2;
201+
202+
// compute total
203+
let total = 0;
204+
for (let i = 1; i < latlngs.length; i++) {
205+
total += latlngs[i].distanceTo(latlngs[i - 1]);
206+
}
207+
208+
let popup = L.popup({ closeButton: false, autoClose: false, className: "ruler-popup" })
209+
.setLatLng([midLat, midLng])
210+
.setContent("<strong>" + formatDistance(segDist) + "</strong><br/>" + formatDistance(total))
211+
.addTo(rulerLayerGroup);
212+
213+
rulerDistancePopups.push(popup);
214+
}
215+
}
216+
106217
function enableTracking() {
107218
map.locate({
108219
watch: true,
@@ -154,6 +265,7 @@ export const Button = function (map, buttons) {
154265
self.init = function init() {
155266
addButton(locateUserButton);
156267
addButton(showCoordsPickerButton);
268+
addButton(rulerButton);
157269
};
158270

159271
return self;

public/locale/de.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@
6363
},
6464
"button": {
6565
"switchView": "Ansicht wechseln",
66-
"location": "Koordinaten wählen",
66+
"fullscreen": "Vollbildmodus wechseln",
6767
"tracking": "Lokalisierung",
68-
"fullscreen": "Vollbildmodus wechseln"
68+
"location": "Koordinaten wählen",
69+
"ruler": "Lineal"
6970
},
7071
"momentjs": {
7172
"calendar": {

public/locale/en.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@
6363
},
6464
"button": {
6565
"switchView": "Switch view",
66-
"location": "Pick coordinates",
66+
"fullscreen": "Toggle fullscreen",
6767
"tracking": "Localisation",
68-
"fullscreen": "Toggle fullscreen"
68+
"location": "Pick coordinates",
69+
"ruler": "Ruler"
6970
},
7071
"momentjs": {
7172
"calendar": {

public/locale/fr.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@
6262
},
6363
"button": {
6464
"switchView": "Basculer l’affichage",
65+
"fullscreen": "Vollbildmodus wechseln",
66+
"tracking": "Localisation",
6567
"location": "Choisir les coordonnées",
66-
"tracking": "Localisation"
68+
"ruler": "Règle"
6769
},
6870
"momentjs": {
6971
"calendar": {

scss/modules/_leaflet.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,3 +535,9 @@
535535
}
536536
}
537537
}
538+
539+
.leaflet-popup-content-wrapper {
540+
background: variables.$color-white;
541+
border-radius: 5px;
542+
text-align: center;
543+
}

scss/night.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ variables.$color-online: color.adjust(variables.$color-online, $lightness: 25%);
6262
}
6363
}
6464

65+
.leaflet-popup-content-wrapper {
66+
background: color.adjust(variables.$color-white, $lightness: 10%);
67+
border-radius: 5px;
68+
text-align: center;
69+
}
70+
6571
.leaflet-control-layers {
6672
&.leaflet-control {
6773
opacity: 0.9;

0 commit comments

Comments
 (0)