Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino
Annotation of /trunk/extras/find-fixme/web/www/fixme.leaflet.js
Parent Directory
|
Revision Log
Revision 1994 -
(hide annotations)
(download)
(as text)
Thu Apr 25 18:02:22 2019 UTC (5 years, 10 months ago) by amb
File MIME type: application/javascript
File size: 13106 byte(s)
Thu Apr 25 18:02:22 2019 UTC (5 years, 10 months ago) by amb
File MIME type: application/javascript
File size: 13106 byte(s)
Fix bug with regexp generating link to node/way/relation on openstreetmap.org.
1 | amb | 1491 | // |
2 | // Routino (extras) fixme web page Javascript | ||
3 | // | ||
4 | // Part of the Routino routing software. | ||
5 | // | ||
6 | amb | 1994 | // This file Copyright 2008-2014, 2019 Andrew M. Bishop |
7 | amb | 1491 | // |
8 | // This program is free software: you can redistribute it and/or modify | ||
9 | // it under the terms of the GNU Affero General Public License as published by | ||
10 | // the Free Software Foundation, either version 3 of the License, or | ||
11 | // (at your option) any later version. | ||
12 | // | ||
13 | // This program is distributed in the hope that it will be useful, | ||
14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | // GNU Affero General Public License for more details. | ||
17 | // | ||
18 | // You should have received a copy of the GNU Affero General Public License | ||
19 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | // | ||
21 | |||
22 | |||
23 | //////////////////////////////////////////////////////////////////////////////// | ||
24 | /////////////////////////////// Initialisation ///////////////////////////////// | ||
25 | //////////////////////////////////////////////////////////////////////////////// | ||
26 | |||
27 | // Process the URL query string and extract the arguments | ||
28 | |||
29 | var legal={"^lon" : "^[-0-9.]+$", | ||
30 | "^lat" : "^[-0-9.]+$", | ||
31 | "^zoom" : "^[0-9]+$"}; | ||
32 | |||
33 | var args={}; | ||
34 | |||
35 | if(location.search.length>1) | ||
36 | { | ||
37 | var query,queries; | ||
38 | |||
39 | query=location.search.replace(/^\?/,""); | ||
40 | query=query.replace(/;/g,"&"); | ||
41 | queries=query.split("&"); | ||
42 | |||
43 | for(var i=0;i<queries.length;i++) | ||
44 | { | ||
45 | queries[i].match(/^([^=]+)(=(.*))?$/); | ||
46 | |||
47 | var k=RegExp.$1; | ||
48 | var v=decodeURIComponent(RegExp.$3); | ||
49 | |||
50 | for(var l in legal) | ||
51 | { | ||
52 | if(k.match(RegExp(l)) && v.match(RegExp(legal[l]))) | ||
53 | args[k]=v; | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | |||
59 | //////////////////////////////////////////////////////////////////////////////// | ||
60 | ///////////////////////////////// Map handling ///////////////////////////////// | ||
61 | //////////////////////////////////////////////////////////////////////////////// | ||
62 | |||
63 | var map; | ||
64 | var layerMap=[], layerHighlights, layerVectors, layerBoxes; | ||
65 | |||
66 | var box; | ||
67 | |||
68 | // | ||
69 | // Initialise the 'map' object | ||
70 | // | ||
71 | |||
72 | function map_init() // called from fixme.html | ||
73 | { | ||
74 | // Create the map (Map URLs and limits are in mapprops.js) | ||
75 | |||
76 | map = L.map("map", | ||
77 | { | ||
78 | attributionControl: false, | ||
79 | zoomControl: false, | ||
80 | |||
81 | minZoom: mapprops.zoomout, | ||
82 | maxZoom: mapprops.zoomin, | ||
83 | |||
84 | maxBounds: L.latLngBounds(L.latLng(mapprops.southedge,mapprops.westedge),L.latLng(mapprops.northedge,mapprops.eastedge)) | ||
85 | }); | ||
86 | |||
87 | // Add map tile layers | ||
88 | |||
89 | var baselayers={}; | ||
90 | |||
91 | for(var l=0; l<mapprops.mapdata.length; l++) | ||
92 | { | ||
93 | var urls=mapprops.mapdata[l].tiles.url.replace(/\${/g,"{"); | ||
94 | |||
95 | if(mapprops.mapdata[l].tiles.subdomains===undefined) | ||
96 | layerMap[l] = L.tileLayer(urls); | ||
97 | else | ||
98 | layerMap[l] = L.tileLayer(urls, {subdomains: mapprops.mapdata[l].tiles.subdomains}); | ||
99 | |||
100 | baselayers[mapprops.mapdata[l].label]=layerMap[l]; | ||
101 | |||
102 | if(l===0) | ||
103 | map.addLayer(layerMap[l]); | ||
104 | } | ||
105 | |||
106 | // Add the controls | ||
107 | |||
108 | map.addControl(L.control.zoom()); | ||
109 | map.addControl(L.control.scale()); | ||
110 | map.addControl(L.control.layers(baselayers)); | ||
111 | |||
112 | // Update the attribution if the layer changes | ||
113 | |||
114 | function change_attribution_event(event) | ||
115 | { | ||
116 | for(var l=0; l<mapprops.mapdata.length; l++) | ||
117 | if(layerMap[l] == event.layer) | ||
118 | change_attribution(l); | ||
119 | } | ||
120 | |||
121 | map.on("baselayerchange",change_attribution_event); | ||
122 | |||
123 | function change_attribution(l) | ||
124 | { | ||
125 | var data_url =mapprops.mapdata[l].attribution.data_url; | ||
126 | var data_text=mapprops.mapdata[l].attribution.data_text; | ||
127 | var tile_url =mapprops.mapdata[l].attribution.tile_url; | ||
128 | var tile_text=mapprops.mapdata[l].attribution.tile_text; | ||
129 | |||
130 | document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>"; | ||
131 | document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>"; | ||
132 | } | ||
133 | |||
134 | change_attribution(0); | ||
135 | |||
136 | // Add two vectors layers (one for highlights that display behind the vectors) | ||
137 | |||
138 | layerVectors = L.layerGroup(); | ||
139 | map.addLayer(layerVectors); | ||
140 | |||
141 | layerHighlights = L.layerGroup(); | ||
142 | map.addLayer(layerHighlights); | ||
143 | |||
144 | // Handle popup | ||
145 | |||
146 | createPopup(); | ||
147 | |||
148 | // Add a boxes layer | ||
149 | |||
150 | layerBoxes = L.rectangle(map.options.maxBounds,{stroke: false, color: "#f00", weight: 1, opacity: 1.0, | ||
151 | fill: false}); | ||
152 | |||
153 | map.addLayer(layerBoxes); | ||
154 | |||
155 | box=false; | ||
156 | |||
157 | // Move the map | ||
158 | |||
159 | map.on("moveend", updateURLs); | ||
160 | |||
161 | var lon =args["lon"]; | ||
162 | var lat =args["lat"]; | ||
163 | var zoom=args["zoom"]; | ||
164 | |||
165 | amb | 1493 | if(lon !== undefined && lat !== undefined && zoom !== undefined) |
166 | amb | 1491 | { |
167 | if(lon<mapprops.westedge) lon=mapprops.westedge; | ||
168 | if(lon>mapprops.eastedge) lon=mapprops.eastedge; | ||
169 | |||
170 | if(lat<mapprops.southedge) lat=mapprops.southedge; | ||
171 | if(lat>mapprops.northedge) lat=mapprops.northedge; | ||
172 | |||
173 | if(zoom<mapprops.zoomout) zoom=mapprops.zoomout; | ||
174 | if(zoom>mapprops.zoomin) zoom=mapprops.zoomin; | ||
175 | |||
176 | map.setView(L.latLng(lat,lon),zoom); | ||
177 | } | ||
178 | else | ||
179 | map.fitBounds(map.options.maxBounds); | ||
180 | |||
181 | // Unhide editing URL if variable set | ||
182 | |||
183 | amb | 1493 | if(mapprops.editurl !== undefined && mapprops.editurl !== "") |
184 | amb | 1491 | { |
185 | var edit_url=document.getElementById("edit_url"); | ||
186 | |||
187 | edit_url.style.display=""; | ||
188 | edit_url.href=mapprops.editurl; | ||
189 | } | ||
190 | |||
191 | updateURLs(); | ||
192 | } | ||
193 | |||
194 | |||
195 | // | ||
196 | // Format a number in printf("%.5f") format. | ||
197 | // | ||
198 | |||
199 | function format5f(number) | ||
200 | { | ||
201 | var newnumber=Math.floor(number*100000+0.5); | ||
202 | var delta=0; | ||
203 | |||
204 | if(newnumber>=0 && newnumber<100000) delta= 100000; | ||
205 | if(newnumber<0 && newnumber>-100000) delta=-100000; | ||
206 | |||
207 | var string=String(newnumber+delta); | ||
208 | |||
209 | var intpart =string.substring(0,string.length-5); | ||
210 | var fracpart=string.substring(string.length-5,string.length); | ||
211 | |||
212 | if(delta>0) intpart="0"; | ||
213 | if(delta<0) intpart="-0"; | ||
214 | |||
215 | return(intpart + "." + fracpart); | ||
216 | } | ||
217 | |||
218 | |||
219 | // | ||
220 | // Build a set of URL arguments for the map location | ||
221 | // | ||
222 | |||
223 | function buildMapArguments() | ||
224 | { | ||
225 | var lonlat = map.getCenter(); | ||
226 | |||
227 | var zoom = map.getZoom(); | ||
228 | |||
229 | return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lng) + ";zoom=" + zoom; | ||
230 | } | ||
231 | |||
232 | |||
233 | // | ||
234 | // Update the URLs | ||
235 | // | ||
236 | |||
237 | function updateURLs() | ||
238 | { | ||
239 | var mapargs=buildMapArguments(); | ||
240 | |||
241 | var links=document.getElementsByTagName("a"); | ||
242 | |||
243 | for(var i=0; i<links.length; i++) | ||
244 | { | ||
245 | var element=links[i]; | ||
246 | |||
247 | if(element.id == "permalink_url") | ||
248 | element.href=location.pathname + "?" + mapargs; | ||
249 | |||
250 | if(element.id == "edit_url") | ||
251 | element.href=mapprops.editurl + "?" + mapargs; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | |||
256 | //////////////////////////////////////////////////////////////////////////////// | ||
257 | ///////////////////////// Popup and selection handling ///////////////////////// | ||
258 | //////////////////////////////////////////////////////////////////////////////// | ||
259 | |||
260 | var popup=null; | ||
261 | |||
262 | // | ||
263 | // Create a popup - independent of map because want it fixed on screen not fixed on map. | ||
264 | // | ||
265 | |||
266 | function createPopup() | ||
267 | { | ||
268 | popup=document.createElement("div"); | ||
269 | |||
270 | popup.className = "popup"; | ||
271 | |||
272 | popup.innerHTML = "<span></span>"; | ||
273 | |||
274 | popup.style.display = "none"; | ||
275 | |||
276 | popup.style.position = "fixed"; | ||
277 | popup.style.top = "-4000px"; | ||
278 | popup.style.left = "-4000px"; | ||
279 | popup.style.zIndex = "100"; | ||
280 | |||
281 | popup.style.padding = "5px"; | ||
282 | |||
283 | popup.style.opacity=0.85; | ||
284 | popup.style.backgroundColor="#C0C0C0"; | ||
285 | popup.style.border="4px solid #404040"; | ||
286 | |||
287 | document.body.appendChild(popup); | ||
288 | } | ||
289 | |||
290 | |||
291 | // | ||
292 | // Draw a popup - independent of map because want it fixed on screen not fixed on map. | ||
293 | // | ||
294 | |||
295 | function drawPopup(html) | ||
296 | { | ||
297 | amb | 1493 | if(html===null) |
298 | amb | 1491 | { |
299 | popup.style.display="none"; | ||
300 | return; | ||
301 | } | ||
302 | |||
303 | if(popup.style.display=="none") | ||
304 | { | ||
305 | var map_div=document.getElementById("map"); | ||
306 | |||
307 | popup.style.left =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px"; | ||
308 | popup.style.top = map_div.offsetTop +30 + "px"; | ||
309 | amb | 1499 | popup.style.width =map_div.clientWidth-120 + "px"; |
310 | amb | 1491 | |
311 | popup.style.display=""; | ||
312 | } | ||
313 | |||
314 | amb | 1499 | var close="<span style='float: right; cursor: pointer;' onclick='drawPopup(null)'>X</span>"; |
315 | |||
316 | popup.innerHTML=close+html; | ||
317 | amb | 1491 | } |
318 | |||
319 | |||
320 | // | ||
321 | // Select a feature | ||
322 | // | ||
323 | |||
324 | function selectCircleMarkerFeature(feature,dump,event) | ||
325 | { | ||
326 | if(dump) | ||
327 | ajaxGET("fixme.cgi?dump=" + dump, runDumpSuccess); | ||
328 | |||
329 | layerHighlights.clearLayers(); | ||
330 | |||
331 | var highlight = L.circleMarker(feature.getLatLng(),{radius: 2*feature.getRadius(), fill: true, fillColor: "#F0F000", fillOpacity: 1.0, | ||
332 | stroke: false}); | ||
333 | |||
334 | layerHighlights.addLayer(highlight); | ||
335 | |||
336 | highlight.bringToBack(); | ||
337 | } | ||
338 | |||
339 | |||
340 | // | ||
341 | // Un-select a feature | ||
342 | // | ||
343 | |||
344 | function unselectFeature(feature) | ||
345 | { | ||
346 | layerHighlights.clearLayers(); | ||
347 | |||
348 | drawPopup(null); | ||
349 | } | ||
350 | |||
351 | |||
352 | // | ||
353 | // Display the dump data | ||
354 | // | ||
355 | |||
356 | function runDumpSuccess(response) | ||
357 | { | ||
358 | var string=response.responseText; | ||
359 | |||
360 | amb | 1493 | if(mapprops.editurl !== undefined && mapprops.editurl !== "") |
361 | amb | 1491 | { |
362 | var types=["node", "way", "relation"]; | ||
363 | |||
364 | for(var t in types) | ||
365 | { | ||
366 | var type=types[t]; | ||
367 | |||
368 | amb | 1994 | var regexp=RegExp(type + " id='([0-9]+)'"); |
369 | amb | 1491 | |
370 | var match=string.match(regexp); | ||
371 | |||
372 | amb | 1493 | if(match !== null) |
373 | amb | 1491 | { |
374 | amb | 1994 | var id=match[1]; |
375 | amb | 1491 | |
376 | amb | 1506 | string=string.replace(regexp,type + " id='<a href='" + mapprops.browseurl + "/" + type + "/" + id + "' target='" + type + id + "'>" + id + "</a>'"); |
377 | amb | 1491 | } |
378 | } | ||
379 | } | ||
380 | |||
381 | drawPopup(string.split("><").join("><br><").split("<br><tag").join("<br> <tag")); | ||
382 | } | ||
383 | |||
384 | |||
385 | //////////////////////////////////////////////////////////////////////////////// | ||
386 | /////////////////////////////// Server handling //////////////////////////////// | ||
387 | //////////////////////////////////////////////////////////////////////////////// | ||
388 | |||
389 | // | ||
390 | // Define an AJAX request object | ||
391 | // | ||
392 | |||
393 | function ajaxGET(url,success,failure,state) | ||
394 | { | ||
395 | var ajaxRequest=new XMLHttpRequest(); | ||
396 | |||
397 | function ajaxGOT(options) { | ||
398 | if(this.readyState==4) | ||
399 | if(this.status==200) | ||
400 | { if(typeof(options.success)=="function") options.success(this,options.state); } | ||
401 | else | ||
402 | { if(typeof(options.failure)=="function") options.failure(this,options.state); } | ||
403 | } | ||
404 | |||
405 | ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); }; | ||
406 | ajaxRequest.open("GET", url, true); | ||
407 | ajaxRequest.send(null); | ||
408 | } | ||
409 | |||
410 | |||
411 | // | ||
412 | // Display the status | ||
413 | // | ||
414 | |||
415 | function displayStatus(type,subtype,content) | ||
416 | { | ||
417 | var child=document.getElementById("result_status").firstChild; | ||
418 | |||
419 | do | ||
420 | { | ||
421 | amb | 1493 | if(child.id !== undefined) |
422 | amb | 1491 | child.style.display="none"; |
423 | |||
424 | child=child.nextSibling; | ||
425 | } | ||
426 | amb | 1496 | while(child !== null); |
427 | amb | 1491 | |
428 | var chosen_status=document.getElementById("result_status_" + type); | ||
429 | |||
430 | chosen_status.style.display=""; | ||
431 | |||
432 | amb | 1496 | if(subtype !== undefined) |
433 | amb | 1491 | { |
434 | var format_status=document.getElementById("result_status_" + subtype).innerHTML; | ||
435 | |||
436 | chosen_status.innerHTML=format_status.replace("#",String(content)); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | |||
441 | // | ||
442 | // Display data statistics | ||
443 | // | ||
444 | |||
445 | function displayStatistics() | ||
446 | { | ||
447 | // Use AJAX to get the statistics | ||
448 | |||
449 | ajaxGET("fixme.cgi?statistics=yes", runStatisticsSuccess); | ||
450 | } | ||
451 | |||
452 | |||
453 | // | ||
454 | // Success in running data statistics generation. | ||
455 | // | ||
456 | |||
457 | function runStatisticsSuccess(response) | ||
458 | { | ||
459 | document.getElementById("statistics_data").innerHTML="<pre>" + response.responseText + "</pre>"; | ||
460 | document.getElementById("statistics_link").style.display="none"; | ||
461 | } | ||
462 | |||
463 | |||
464 | // | ||
465 | // Get the requested data | ||
466 | // | ||
467 | |||
468 | function displayData(datatype) // called from fixme.html | ||
469 | { | ||
470 | // Delete the old data | ||
471 | |||
472 | unselectFeature(); | ||
473 | |||
474 | layerVectors.clearLayers(); | ||
475 | layerHighlights.clearLayers(); | ||
476 | |||
477 | layerBoxes.setStyle({stroke:false}); | ||
478 | box=false; | ||
479 | |||
480 | // Print the status | ||
481 | |||
482 | displayStatus("no_data"); | ||
483 | |||
484 | // Return if just here to clear the data | ||
485 | |||
486 | amb | 1493 | if(datatype === "") |
487 | amb | 1491 | return; |
488 | |||
489 | // Get the new data | ||
490 | |||
491 | var mapbounds=map.getBounds(); | ||
492 | |||
493 | var url="fixme.cgi"; | ||
494 | |||
495 | url=url + "?lonmin=" + format5f(mapbounds.getWest()); | ||
496 | url=url + ";latmin=" + format5f(mapbounds.getSouth()); | ||
497 | url=url + ";lonmax=" + format5f(mapbounds.getEast()); | ||
498 | url=url + ";latmax=" + format5f(mapbounds.getNorth()); | ||
499 | url=url + ";data=" + datatype; | ||
500 | |||
501 | // Use AJAX to get the data | ||
502 | |||
503 | ajaxGET(url, runFixmeSuccess, runFailure); | ||
504 | } | ||
505 | |||
506 | |||
507 | // | ||
508 | // Success in getting the error log data | ||
509 | // | ||
510 | |||
511 | function runFixmeSuccess(response) | ||
512 | { | ||
513 | var lines=response.responseText.split("\n"); | ||
514 | |||
515 | for(var line=0;line<lines.length;line++) | ||
516 | { | ||
517 | var words=lines[line].split(" "); | ||
518 | |||
519 | amb | 1493 | if(line === 0) |
520 | amb | 1491 | { |
521 | var lat1=words[0]; | ||
522 | var lon1=words[1]; | ||
523 | var lat2=words[2]; | ||
524 | var lon2=words[3]; | ||
525 | |||
526 | var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2)); | ||
527 | |||
528 | layerBoxes.setBounds(bounds); | ||
529 | |||
530 | layerBoxes.setStyle({stroke: true}); | ||
531 | box=true; | ||
532 | } | ||
533 | amb | 1493 | else if(words[0] !== "") |
534 | amb | 1491 | { |
535 | var dump=words[0]; | ||
536 | var lat=words[1]; | ||
537 | var lon=words[2]; | ||
538 | |||
539 | var lonlat = L.latLng(lat,lon); | ||
540 | |||
541 | var feature = L.circleMarker(lonlat,{radius: 3, fill: true, fillColor: "#FF0000", fillOpacity: 1.0, | ||
542 | stroke: false}); | ||
543 | |||
544 | feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump))); | ||
545 | |||
546 | layerVectors.addLayer(feature); | ||
547 | } | ||
548 | } | ||
549 | |||
550 | displayStatus("data","fixme",lines.length-2); | ||
551 | } | ||
552 | |||
553 | |||
554 | // | ||
555 | // Failure in getting data. | ||
556 | // | ||
557 | |||
558 | function runFailure(response) | ||
559 | { | ||
560 | displayStatus("failed"); | ||
561 | } |