script.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. var widths = [12, 13, 13, 13, 13, 13, 13, 13];
  2. var opas = [0.8, 0.6, 0.5, 0.5, 0.4];
  3. var mwidths = [1, 1, 1, 1.5, 2, 3, 5, 6, 6, 4, 3, 2];
  4. var stCols = ['#78f378', '#0000c3', 'red'];
  5. var osmUrl = "//www.openstreetmap.org/";
  6. var grIdx, mGrIdx, stIdx, selectedRes, prevSearch, delayTimer;
  7. var reqs = {};
  8. var openedGr = -1;
  9. var openedMGr = -1;
  10. var openedSt = -1;
  11. // station sugg messages
  12. var sgMvOrNew = "Move into a <span class='grouplink' onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})'>new relation</span> <tt>public_transport=stop_area</tt>.";
  13. var sgMvOrEx = "Move into relation <a onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>.";
  14. var sgMvRelNew = "Move from relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a> into a <span class='grouplink' onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})'>new relation</span> <tt>public_transport=stop_area</tt>.";
  15. var sgMvRelRel = "Move from relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a> into relation <a onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>.";
  16. var sgMvOutRel = "Move out of relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a>";
  17. var sgFixAttr = "Fix attribute <tt>${attr}</tt>.";
  18. var sgAddName = "Consider adding a <tt><a target='_blank' href='https://wiki.openstreetmap.org/wiki/Key:name'>name</a></tt> attribute.";
  19. var sgAttrTr = "Attribute <tt>${attr}</tt> seems to be a track/platform number. Use <tt>ref</tt> for this and set <tt>${attr}</tt> to the station name.";
  20. var sgMergeRel = "Merge parent relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a> with relation <a onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>, or move them into a new relation <tt>public_transport=stop_area_group</tt>";
  21. var sgMergeMetaGr = "Move parent relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a> into meta <tt>public_transport=stop_area_group</tt> relation <a onmouseover='mGrHl(${tid})' onmouseout='mGrUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>";
  22. var suggsMsg = [sgMvOrNew, sgMvOrEx, sgMvRelNew, sgMvRelRel, sgMvOutRel, sgFixAttr, sgAddName, sgAttrTr, sgMergeRel, sgMergeMetaGr];
  23. // group sugg messages
  24. var sgGrFixAttr = "Fix attribute <tt>${attr}</tt>.";
  25. var sgGrAddName = "Consider adding a <tt><a target='_blank' href='https://wiki.openstreetmap.org/wiki/Key:name'>name</a></tt> attribute.";
  26. var sgGrAttrTr = "Attribute <tt>${attr}</tt> seems to be a track/platform number. Use <tt>ref</tt> for this and set <tt>${attr}</tt> to the station name.";
  27. var sgGrMergeRel = "Merge with relation <a onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>, or move both into a new relation <tt>public_transport=stop_area_group</tt>";
  28. var sgGrMergeMeta = "Move relation into <tt>public_transport=stop_area_group</tt> relation <a onmouseover='mGrHl(${tid})' onmouseout='mGrUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>";
  29. var groupSuggMsg = [sgGrFixAttr, sgGrAddName, sgGrAttrTr, sgGrMergeRel, sgGrMergeMeta];
  30. function $(a){return a[0] == "#" ? document.getElementById(a.substr(1)) : a[0] == "." ? document.getElementsByClassName(a.substr(1)) : document.getElementsByTagName(a)}
  31. function $$(t){return document.createElement(t) }
  32. function ll(g){return {"lat" : g[0], "lng" : g[1]}}
  33. function hasCl(e, c){return e.className.split(" ").indexOf(c) != -1}
  34. function addCl(e, c){if (!hasCl(e, c)) e.className += " " + c;e.className = e.className.trim()}
  35. function delCl(e, c){var a = e.className.split(" "); delete a[a.indexOf(c)]; e.className = a.join(" ").trim()}
  36. function stCol(s){return s.e ? stCols[2] : s.s ? stCols[1] : stCols[0]}
  37. function tmpl(s, r){for (var p in r) s = s.replace(new RegExp("\\${" + p + "}", "g"), r[p]); return s}
  38. function req(id, u, cb) {
  39. if (reqs[id]) reqs[id].abort();
  40. reqs[id] = new XMLHttpRequest();
  41. reqs[id].onreadystatechange = function() { if (this.readyState == 4 && this.status == 200 && this == reqs[id]) cb(JSON.parse(this.responseText))};
  42. reqs[id].open("GET", u, 1);
  43. reqs[id].send();
  44. }
  45. function marker(stat, z) {
  46. if (stat.g.length == 1) {
  47. if (z > 15) {
  48. return L.circle(
  49. stat.g[0], {
  50. color: '#000',
  51. fillColor: stCol(stat),
  52. radius: mwidths[23 - z],
  53. fillOpacity: 1,
  54. weight: z > 17 ? 1.5 : 1,
  55. id: stat.i
  56. }
  57. );
  58. } else {
  59. return L.polyline(
  60. [stat.g[0], stat.g[0]], {
  61. color: stCol(stat),
  62. fillColor: stCol(stat),
  63. weight: widths[15 - z],
  64. opacity: opas[15 - z],
  65. id: stat.i
  66. }
  67. );
  68. }
  69. } else {
  70. return L.polygon(
  71. stat.g, {
  72. color: z > 15 ? '#000': stCol(stat),
  73. fillColor: stCol(stat),
  74. smoothFactor: 0,
  75. fillOpacity: 0.75,
  76. weight: z > 17 ? 1.5 : 1,
  77. id: stat.i
  78. }
  79. );
  80. }
  81. }
  82. function poly(group, z, dotted) {
  83. var col = group.e ? 'red' : group.s ? '#0000c3' : '#85f385';
  84. var style = {
  85. color: col,
  86. fillColor: col,
  87. smoothFactor: 0.4,
  88. fillOpacity: 0.2,
  89. id: group.i
  90. };
  91. if (dotted) {
  92. var col = group.e ? 'red' : group.s ? '#0000c3' : '#a3b750';
  93. style.dashArray = '8, 8';
  94. style.fillOpacity = 0.15;
  95. style.color = col;
  96. style.fillColor = col;
  97. style.weight = 2;
  98. }
  99. if (z < 16) {
  100. style.weight = 11;
  101. style.opacity = 0.5;
  102. style.fillOpacity = 0.5;
  103. }
  104. return L.polygon(group.g, style)
  105. }
  106. function sugArr(sug, z) {
  107. return L.polyline(sug.a, {
  108. id: sug.i,
  109. color: '#0000c3',
  110. smoothFactor: 0.1,
  111. weight: 4,
  112. opacity: 0.5
  113. });
  114. }
  115. function rndrSt(stat) {
  116. openedSt = stat.id;
  117. stHl(stat.id);
  118. var attrrows = {};
  119. var way = stat.osmid < 0;
  120. var osmid = Math.abs(stat.osmid);
  121. var ident = way ? "Way" : "Node";
  122. var con = $$('div');
  123. con.setAttribute("id", "nav")
  124. var suggD = $$('div');
  125. suggD.setAttribute("id", "sugg")
  126. con.innerHTML = ident + " <a target='_blank' href='" + osmUrl + ident.toLowerCase()+"/" + osmid + "'>" + osmid + "</a>";
  127. if (stat.attrs.name) con.innerHTML += " (<b>\"" + stat.attrs.name + "\"</b>)";
  128. con.innerHTML += "<a class='ebut' target='_blank' href='" + osmUrl + "edit?" + ident.toLowerCase() + "=" + osmid +"'>&#9998;</a>";
  129. var attrTbl = $$('table');
  130. attrTbl.setAttribute("id", "attr-tbl")
  131. con.appendChild(attrTbl);
  132. con.appendChild(suggD);
  133. var tbody = $$('tbody');
  134. attrTbl.appendChild(tbody);
  135. for (var key in stat.attrs) {
  136. var row = $$('tr');
  137. var col1 = $$('td');
  138. var col2 = $$('td');
  139. addCl(col2, "err-wrap");
  140. tbody.appendChild(row);
  141. row.appendChild(col1);
  142. row.appendChild(col2);
  143. col1.innerHTML = "<a href=\"https://wiki.openstreetmap.org/wiki/Key:" + key + "\" target=\"_blank\"><tt>" + key + "</tt></a>";
  144. for (var i = 0; i < stat.attrs[key].length; i++) col2.innerHTML += "<span class='attrval'>" + stat.attrs[key][i] + "</span>" + "<br>";
  145. attrrows[key] = row;
  146. }
  147. for (var i = 0; i < stat.attrerrs.length; i++) {
  148. var err = stat.attrerrs[i];
  149. var row = attrrows[err.attr[0]];
  150. addCl(row, "err-" + Math.round(err.conf * 10));
  151. var info = $$('div');
  152. if (err.other_grp) {
  153. // the mismatch was with a group name
  154. if (err.other_osmid > 1) info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <a onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")' target=\"_blank\" href=\"" + osmUrl + "relation/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  155. else info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <span onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")'>" + Math.abs(err.other_osmid) + "</span>";
  156. } else {
  157. // the mismatch was with another station
  158. if (err.other_osmid != stat.osmid) {
  159. var lident = err.other_osmid < 0 ? "way" : "node";
  160. info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in " + lident + " <a onmouseover='stHl( " + err.other + ")' onmouseout='stUnHl( " + err.other + ")' target=\"_blank\" href=\"" + osmUrl + lident+"/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  161. } else {
  162. info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> = '" + err.other_attr[1] + "'";
  163. }
  164. }
  165. addCl(info, 'attr-err-info');
  166. row.childNodes[1].appendChild(info);
  167. }
  168. var suggList = $$('ul');
  169. if (stat.su.length) {
  170. var a = $$('span');
  171. addCl(a, "sugtit");
  172. a.innerHTML = "Suggestions";
  173. suggD.appendChild(a);
  174. }
  175. suggD.appendChild(suggList);
  176. for (var i = 0; i < stat.su.length; i++) {
  177. var sg = stat.su[i];
  178. var sgDiv = $$('li');
  179. sgDiv.innerHTML = tmpl(suggsMsg[sg.type - 1], {"attr" : sg.attr, "tid" : sg.target_gid, "ooid" : sg.orig_osm_rel_id, "toid" : sg.target_osm_rel_id, "oid" : sg.orig_gid});
  180. suggList.appendChild(sgDiv);
  181. }
  182. L.popup({opacity: 0.8})
  183. .setLatLng(stat)
  184. .setContent(con)
  185. .openOn(map)
  186. .on('remove', function() {if (openedSt == stat.id) {openedSt = -1; stUnHl(stat.id)}});
  187. }
  188. function openSt(id) {req("s", "/stat?id=" + id, function(c) {rndrSt(c)});}
  189. function rndrGr(grp) {
  190. openedGr = grp.id;
  191. var attrrows = {};
  192. grHl(grp.id);
  193. var con = $$('div');
  194. con.setAttribute("id", "nav");
  195. var newMembers = $$('div');
  196. newMembers.setAttribute("id", "group-stations-new")
  197. newMembers.innerHTML = "<span class='newmemberstit'>New Members</span>";
  198. var oldMembers = $$('div');
  199. oldMembers.setAttribute("id", "group-stations-old")
  200. oldMembers.innerHTML = "<span class='oldmemberstit'>Existing Members</span>";
  201. if (grp.osmid == 1) {
  202. con.innerHTML = "<span class='grouplink'>New relation</span> <tt>public_transport=stop_area</tt>";
  203. } else {
  204. con.innerHTML = "OSM relation <a target='_blank' href='https://www.openstreetmap.org/relation/" + grp.osmid + "'>" + grp.osmid + "</a>";
  205. if (grp.attrs.name) con.innerHTML += " (<b>\"" + grp.attrs.name + "\"</b>)";
  206. con.innerHTML += "<a class='ebut' target='_blank' href='" + osmUrl + "edit?relation=" + grp.osmid +"'>&#9998;</a>";
  207. }
  208. var attrTbl = $$('table');
  209. attrTbl.setAttribute("id", "attr-tbl")
  210. con.appendChild(attrTbl);
  211. var tbody = $$('tbody');
  212. attrTbl.appendChild(tbody);
  213. var suggD = $$('div');
  214. suggD.setAttribute("id", "sugg")
  215. for (var key in grp.attrs) {
  216. var row = $$('tr');
  217. var col1 = $$('td');
  218. var col2 = $$('td');
  219. addCl(col2, "err-wrap");
  220. tbody.appendChild(row);
  221. row.appendChild(col1);
  222. row.appendChild(col2);
  223. col1.innerHTML = "<a href=\"https://wiki.openstreetmap.org/wiki/Key:" + key + "\" target=\"_blank\"><tt>" + key + "</tt></a>";
  224. for (var i = 0; i < grp.attrs[key].length; i++) col2.innerHTML += "<span class='attrval'>" + grp.attrs[key][i] + "</span>" + "<br>";
  225. attrrows[key] = row;
  226. }
  227. for (var i = 0; i < grp.attrerrs.length; i++) {
  228. var err = grp.attrerrs[i];
  229. var row = attrrows[err.attr[0]];
  230. addCl(row, "err-" + Math.round(err.conf * 10));
  231. var info = $$('div');
  232. if (err.other_grp) {
  233. // the mismatch was with a group name
  234. if (err.other_osmid != grp.osmid) {
  235. if (err.other_osmid > 1) info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <a onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")' target=\"_blank\" href=\"" + osmUrl + "relation/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  236. else info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <span onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")'>" + Math.abs(err.other_osmid) + "</span>";
  237. } else info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> = '" + err.other_attr[1] + "'";
  238. } else {
  239. // the mismatch was with another station
  240. var ident = err.other_osmid < 0 ? "way" : "node";
  241. info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in " + ident + " <a onmouseover='stHl( " + err.other + ")' onmouseout='stUnHl( " + err.other + ")' target=\"_blank\" href=\"" + osmUrl + ident+"/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  242. }
  243. addCl(info, 'attr-err-info');
  244. row.childNodes[1].appendChild(info);
  245. }
  246. var suggList = $$('ul');
  247. if (grp.su.length) {
  248. var a = $$('span');
  249. addCl(a, "sugtit");
  250. a.innerHTML = "Suggestions";
  251. suggD.appendChild(a);
  252. }
  253. suggD.appendChild(suggList);
  254. var mergeGroup = false;
  255. for (var i = 0; i < grp.su.length; i++) {
  256. var sg = grp.su[i];
  257. var sgDiv = $$('li');
  258. if (sg.type == 9 || sg.type == 10) mergeGroup = true;
  259. sgDiv.innerHTML = tmpl(groupSuggMsg[sg.type - 6], {"attr" : sg.attr, "tid" : sg.target_gid, "ooid" : sg.orig_osm_rel_id, "toid" : sg.target_osm_rel_id, "oid" : sg.orig_gid});
  260. suggList.appendChild(sgDiv);
  261. }
  262. con.appendChild(newMembers);
  263. if (grp.osmid != 1) con.appendChild(oldMembers);
  264. for (var key in grp.stations) {
  265. var stat = grp.stations[key];
  266. var row = $$('div');
  267. var ident = stat.osmid < 0 ? "Way" : "Node";
  268. row.innerHTML = ident + " <a onmouseover='stHl( " + stat.id + ")' onmouseout='stUnHl( " + stat.id + ")' target='_blank' href='" + osmUrl + ident.toLowerCase() + "/" + Math.abs(stat.osmid) + "'>" + Math.abs(stat.osmid) + "</a>";
  269. if (stat.attrs.name) row.innerHTML += " (<b>\"" + stat.attrs.name + "\"</b>)";
  270. row.style.backgroundColor = stat.e ? '#f58d8d' : stat.s ? '#b6b6e4' : '#c0f7c0';
  271. if (grp.osmid == 1 || stat.orig_group != grp.id) newMembers.appendChild(row);
  272. else {
  273. oldMembers.appendChild(row);
  274. if (stat.group != grp.id && !mergeGroup) addCl(row, "del-stat");
  275. }
  276. }
  277. con.appendChild(suggD);
  278. L.popup({opacity: 0.8})
  279. .setLatLng(grp)
  280. .setContent(con)
  281. .openOn(map)
  282. .on('remove', function() {if (openedGr == grp.id) {openedGr = -1; grUnHl(grp.id)}});
  283. }
  284. function rndrMGr(grp) {
  285. openedMGr = grp.id;
  286. mGrHl(grp.id);
  287. var con = $$('div');
  288. con.setAttribute("id", "nav");
  289. var oldMembers = $$('div');
  290. oldMembers.setAttribute("id", "group-stations-old")
  291. oldMembers.innerHTML = "<span class='oldmemberstit'>Members</span>";
  292. con.innerHTML = "OSM relation <a target='_blank' href='https://www.openstreetmap.org/relation/" + grp.osmid + "'>" + grp.osmid + " </a> (<tt>public_transport=stop_area_group</tt>)";
  293. con.innerHTML += "<a class='ebut' target='_blank' href='" + osmUrl + "edit?relation=" + grp.osmid +"'>&#9998;</a>";
  294. con.appendChild(oldMembers);
  295. for (var key in grp.groups) {
  296. var gr = grp.groups[key];
  297. var row = $$('div');
  298. row.innerHTML = "Relation <a onmouseover='grHl( " + gr.id + ")' onmouseout='grUnHl( " + gr.id + ")' target='_blank' href='" + osmUrl + "relation/" + gr.osmid + "'>" + gr.osmid + "</a>";
  299. if (gr.attrs.name) row.innerHTML += " (<b>\"" + gr.attrs.name[0] + "\"</b>)";
  300. row.style.backgroundColor = gr.e ? '#f58d8d' : gr.s ? '#b6b6e4' : '#c0f7c0';
  301. oldMembers.appendChild(row);
  302. }
  303. L.popup({opacity: 0.8})
  304. .setLatLng(grp)
  305. .setContent(con)
  306. .openOn(map)
  307. .on('remove', function() {if (openedMGr == grp.id) {openedMGr = -1; mGrUnHl(grp.id)}});
  308. }
  309. function openGr(id) {
  310. req("g", "/group?id=" + id, function(c) {rndrGr(c)});
  311. }
  312. function openMGr(id) {
  313. req("g", "/mgroup?id=" + id, function(c) {rndrMGr(c)});
  314. }
  315. function mGrHl(id) {
  316. !mGrIdx[id] || mGrIdx[id].setStyle({'weight': 6, 'color': "#eecc00"});
  317. }
  318. function mGrUnHl(id) {
  319. !mGrIdx[id] || mGrIdx[id].setStyle({
  320. 'weight': 2,
  321. 'color': mGrIdx[id].options["fillColor"]
  322. });
  323. }
  324. function grHl(id) {
  325. !grIdx[id] || grIdx[id].setStyle({'weight': 6, 'color': "#eecc00"});
  326. }
  327. function grUnHl(id) {
  328. !grIdx[id] || grIdx[id].setStyle({
  329. 'weight': 3,
  330. 'color': grIdx[id].options["fillColor"]
  331. });
  332. }
  333. function stHl(id) {
  334. if (!stIdx[id]) return;
  335. if (map.getZoom() > 15) {
  336. stIdx[id].setStyle({
  337. 'weight': 5,
  338. 'color': "#eecc00"
  339. });
  340. } else {
  341. stIdx[id].setStyle({
  342. 'color': "#eecc00"
  343. });
  344. }
  345. }
  346. function stUnHl(id) {
  347. if (!stIdx[id]) return;
  348. if (map.getZoom() > 15) {
  349. stIdx[id].setStyle({
  350. 'weight': map.getZoom() > 17 ? 1.5 : 1,
  351. 'color': "#000"
  352. });
  353. } else {
  354. stIdx[id].setStyle({
  355. 'color': stIdx[id].options["fillColor"]
  356. });
  357. }
  358. }
  359. var map = L.map('m', {renderer: L.canvas(), attributionControl: false}).setView([47.9965, 7.8469], 13);
  360. map.addControl(L.control.attribution({
  361. position: 'bottomright',
  362. prefix: '&copy; <a target="_blank" href="https://ad.cs.uni-freiburg.de">University of Freiburg, Chair of Algorithms and Data Structures</a>'
  363. }));
  364. map.on('popupopen', function(e) {
  365. var z = Math.max(map.getZoom(), 16);
  366. var px = map.project(e.target._popup._latlng, z);
  367. px.x += e.target._popup._container.clientWidth/2 - 20;
  368. px.y -= e.target._popup._container.clientHeight/2;
  369. map.setView(map.unproject(px, z), z, {animate: true});
  370. s();
  371. });
  372. L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png', {
  373. maxZoom: 20,
  374. attribution: '&copy; <a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
  375. opacity: 0.8
  376. }).addTo(map);
  377. var l = L.featureGroup().addTo(map);
  378. map.on("moveend", function() {render();});
  379. map.on("click", function() {s()});
  380. map.on("zoomend", function() {$("main")[0].className = '';addCl($("main")[0], "z"+map.getZoom())});
  381. function render() {
  382. if (map.getZoom() < 11) {
  383. var b = map.getBounds();
  384. var sw = b.getSouthWest();
  385. var ne = b.getNorthEast();
  386. req("m", "/heatmap?z=" + map.getZoom() + "&bbox=" + [sw.lat, sw.lng, ne.lat, ne.lng].join(","),
  387. function(re) {
  388. l.clearLayers();
  389. var blur = 22 - map.getZoom();
  390. var rad = 25 - map.getZoom();
  391. l.addLayer(L.heatLayer(re.ok, {
  392. max: 500,
  393. gradient: {
  394. 0: '#cbf7cb',
  395. 0.5: '#78f378',
  396. 1: '#29c329'
  397. },
  398. minOpacity: 0.65,
  399. blur: blur,
  400. radius: rad
  401. }));
  402. l.addLayer(L.heatLayer(re.sugg, {
  403. max: 500,
  404. gradient: {
  405. 0: '#7f7fbd',
  406. 0.5: '#4444b3',
  407. 1: '#0606c1'
  408. },
  409. minOpacity: 0.65,
  410. blur: blur - 3,
  411. radius: Math.min(12, rad - 3)
  412. }));
  413. l.addLayer(L.heatLayer(re.err, {
  414. max: 500,
  415. gradient: {
  416. 0: '#f39191',
  417. 0.5: '#ff5656',
  418. 1: '#ff0000'
  419. },
  420. minOpacity: 0.75,
  421. blur: blur - 3,
  422. radius: Math.min(10, rad - 3),
  423. maxZoom: 15
  424. }));
  425. }
  426. )
  427. } else {
  428. req("m", "/map?z=" + map.getZoom() + "&bbox=" + [map.getBounds().getSouthWest().lat, map.getBounds().getSouthWest().lng, map.getBounds().getNorthEast().lat, map.getBounds().getNorthEast().lng].join(","),
  429. function(re) {
  430. l.clearLayers();
  431. grIdx = {};
  432. mGrIdx = {};
  433. stIdx = {};
  434. var stats = [];
  435. for (var i = 0; i < re.stats.length; i++) {
  436. stIdx[re.stats[i].i] = stats[stats.push(marker(re.stats[i], map.getZoom())) - 1];
  437. }
  438. var groups = [];
  439. for (var i = 0; i < re.groups.length; i++) {
  440. grIdx[re.groups[i].i] = groups[groups.push(poly(re.groups[i], map.getZoom())) - 1];
  441. }
  442. var mgroups = [];
  443. for (var i = 0; i < re.mgroups.length; i++) {
  444. mGrIdx[re.mgroups[i].i] = mgroups[mgroups.push(poly(re.mgroups[i], map.getZoom(), 1)) - 1];
  445. }
  446. var suggs = [];
  447. for (var i = 0; i < re.su.length; i++) {
  448. suggs.push(sugArr(re.su[i], map.getZoom()));
  449. }
  450. if (map.getZoom() > 13) {
  451. l.addLayer(L.featureGroup(mgroups).on('click', function(a) {
  452. openMGr(a.layer.options.id, a.layer.getBounds().getCenter());
  453. }));
  454. l.addLayer(L.featureGroup(groups).on('click', function(a) {
  455. openGr(a.layer.options.id, a.layer.getBounds().getCenter());
  456. }));
  457. }
  458. l.addLayer(L.featureGroup(stats).on('click', function(a) {
  459. openSt(a.layer.options.id);
  460. }));
  461. if (map.getZoom() > 15) {
  462. l.addLayer(L.featureGroup(suggs).on('click', function(a) {
  463. if (a.layer.options.id < 0) openGr(-a.layer.options.id);
  464. else openSt(a.layer.options.id);
  465. }));
  466. }
  467. grHl(openedGr);
  468. mGrHl(openedMGr);
  469. stHl(openedSt);
  470. }
  471. )
  472. };
  473. }
  474. function rowClick(row) {
  475. if (!isSearchOpen()) return;
  476. if (row.stat) openSt(row.stat.i, ll(row.stat.g[0]));
  477. else openGr(row.group.i, ll(row.group.g[0]));
  478. }
  479. function select(row) {
  480. if (!row) return;
  481. if (!isSearchOpen()) return;
  482. unselect(selectedRes);
  483. selectedRes = row;
  484. addCl(row, "selres");
  485. if (row.stat) stHl(row.stat.i);
  486. if (row.group) grHl(row.group.i);
  487. }
  488. function unselect(row) {
  489. selectedRes = undefined;
  490. if (!row) return;
  491. delCl(row, "selres");
  492. if (row.stat && row.stat.i != openedSt) stUnHl(row.stat.i);
  493. if (row.group && row.group.i != openedGr) grUnHl(row.group.i);
  494. }
  495. function isSearchOpen() {
  496. return $("#sres").className == "res-open";
  497. }
  498. function s(q) {
  499. var delay = 0;
  500. if (q == prevSearch) return;
  501. clearTimeout(delayTimer);
  502. prevSearch = q;
  503. //unselect(selectedRes);
  504. if (!q) {
  505. $('#si').value = "";
  506. $("#sres").className = "";
  507. $("#sres").innerHTML = "";
  508. return;
  509. }
  510. delayTimer = setTimeout(function() {
  511. req("sr", "/search?q=" + encodeURIComponent(q.substring(0, 100)), function(c) {
  512. var res = $("#sres");
  513. addCl(res, "res-open");
  514. res.innerHTML = "";
  515. for (var i = 0; i < c.length; i++) {
  516. var e = c[i];
  517. var row = $$('span');
  518. addCl(row, "sres");
  519. row.innerHTML = e.n;
  520. if (e.w) addCl(row, "res-way");
  521. if (e.s) {
  522. row.stat = e.s;
  523. addCl(row, "res-stat");
  524. if (e.s.s) addCl(row, "res-sugg");
  525. if (e.s.e) addCl(row, "res-err");
  526. } else {
  527. row.group = e.g;
  528. addCl(row, "res-group");
  529. if (e.g.s) addCl(row, "res-sugg");
  530. if (e.g.e) addCl(row, "res-err");
  531. }
  532. row.onmouseover = function(){select(this)};
  533. row.onclick = function(){rowClick(this)};
  534. var dist = $$('span');
  535. addCl(dist, "dist");
  536. dist.innerHTML = dstr(e.s ? e.s.g : e.g.g);
  537. row.appendChild(dist);
  538. if (e.v && e.v != e.name) {
  539. var via = $$('span');
  540. addCl(via, "via");
  541. via.innerHTML = e.v;
  542. row.appendChild(via);
  543. }
  544. res.appendChild(row);
  545. }
  546. if ($('.sres').length > 0) select($('.sres')[0]);
  547. }
  548. )}, delay);
  549. }
  550. function dstr(s) {
  551. if (map.getBounds().contains(ll(s[0]))) return "";
  552. var d = map.distance(map.getCenter(), ll(s[0]));
  553. if (d < 500) return Math.round(d).toFixed(0) + "m";
  554. if (d < 5000) return (d / 1000.0).toFixed(1) + "km";
  555. return Math.round(d / 1000.0).toFixed(0) + "km";
  556. }
  557. function kp(e) {
  558. if (e.keyCode == 40 || (!e.shiftKey && e.keyCode == 9)) {
  559. var sels = $('.selres')
  560. if (sels.length) select(sels[0].nextSibling);
  561. else select($('.sres')[0]);
  562. e.preventDefault();
  563. } else if (e.keyCode == 38 || (e.shiftKey && e.keyCode == 9)) {
  564. var sels = $('.selres')
  565. if (sels.length) {
  566. if (sels[0].previousSibling) select(sels[0].previousSibling);
  567. else unselect(sels[0]);
  568. e.preventDefault();
  569. }
  570. }
  571. if (e.keyCode == 13) {
  572. var sels = $('.selres');
  573. if (sels.length) rowClick(sels[0]);
  574. }
  575. }
  576. $('#del').onclick = function() {s();$("#si").focus()}
  577. render();