var widths = [12, 13, 13, 13, 13, 13, 13, 13]; var opas = [0.8, 0.6, 0.5, 0.5, 0.4]; var mwidths = [1, 1, 1, 1.5, 2, 3, 5, 6, 6, 4, 3, 2]; var backend = ""; var openGr = -1; var openSt = -1; function marker(stat, z) { if (stat.g.length == 1) { if (z > 15) { return L.circle( stat.g[0], { color: 'black', fillColor: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378', radius: mwidths[23 - z], fillOpacity: 1, weight: z > 17 ? 1.5 : 1, id: stat.i } ); } else { return L.polyline( [stat.g[0], stat.g[0]], { color: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378', fillColor: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378', weight: widths[15 - z], opacity: opas[15 - z], id: stat.i } ); } } else { return L.polygon( stat.g, { color: z > 15 ? 'black': (stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378'), fillColor: stat.e ? 'red' : stat.s ? '#0000c3' : '#78f378', smoothFactor: 0, fillOpacity: 0.75, weight: z > 17 ? 1.5 : 1, id: stat.i } ); } } function poly(group, z) { var col = group.e ? 'red' : group.s ? '#0000c3' : '#85f385'; var style = { color: col, fillColor: col, smoothFactor: 0.4, fillOpacity: 0.2, id: group.i }; if (group.n) { style.color = "#0000c3"; style.fillColor = "#0000c3"; } if (z < 16) { style.weight = 11; style.opacity = 0.5; style.fillOpacity = 0.5; } return L.polygon(group.g, style) } function sugArr(sug, z) { return L.polyline(sug.a, { id: sug.i, color: '#0000c3', smoothFactor: 0.1, weight: 4, opacity: 0.5 }); } function renderStat(stat) { openSt = stat.id; nodeHl(stat.id); var attrrows = {}; var ll = { lat: stat.lat, lon: stat.lon }; var way = stat.osmid < 0; var osmid = Math.abs(stat.osmid); var ident = way ? "Way" : "Node"; var content = document.createElement('div'); content.setAttribute("id", "nav") var suggD = document.createElement('div'); suggD.setAttribute("id", "sugg") content.innerHTML = ident + " " + osmid + ""; if (stat.attrs.name) { content.innerHTML += " (\"" + stat.attrs.name + "\")"; } content.innerHTML += ""; var attrTbl = document.createElement('table'); attrTbl.setAttribute("id", "attr-tbl") content.appendChild(attrTbl); content.appendChild(suggD); var tbody = document.createElement('tbody'); attrTbl.appendChild(tbody); for (var key in stat.attrs) { var row = document.createElement('tr'); var col1 = document.createElement('td'); var col2 = document.createElement('td'); col2.className = "err-wrap"; tbody.appendChild(row); row.appendChild(col1); row.appendChild(col2); col1.innerHTML = "" + key + ""; for (var i = 0; i < stat.attrs[key].length; i++) col2.innerHTML += "" + stat.attrs[key][i] + "" + "
"; attrrows[key] = row; } for (var i = 0; i < stat.attrerrs.length; i++) { var err = stat.attrerrs[i]; var row = attrrows[err.attr[0]]; row.className = "err-" + Math.round(err.conf * 10); var info = document.createElement('div'); if (err.other_grp) { // the mismatch was with a group name if (err.other_osmid > 1) { info.innerHTML = "Does not match " + err.other_attr[0] + " in relation " + Math.abs(err.other_osmid) + ""; } else { info.innerHTML = "Does not match " + err.other_attr[0] + " in relation " + Math.abs(err.other_osmid) + ""; } } else { // the mismatch was with another station if (err.other_osmid != stat.osmid) { var ident = err.other_osmid < 0 ? "way" : "node"; info.innerHTML = "Does not match " + err.other_attr[0] + " in " + ident + " " + Math.abs(err.other_osmid) + ""; } else { info.innerHTML = "Does not match " + err.other_attr[0] + " = '" + err.other_attr[1] + "'"; } } info.className = 'attr-err-info'; row.childNodes[1].appendChild(info); } var suggList = document.createElement('ul'); if (stat.su.length) { var a = document.createElement('span'); a.className = "sugtit"; a.innerHTML = "Suggestions"; suggD.appendChild(a); } suggD.appendChild(suggList); for (var i = 0; i < stat.su.length; i++) { var sugg = stat.su[i]; var suggDiv = document.createElement('li'); if (sugg.type == 1) { suggDiv.innerHTML = "Move into a new relation public_transport=stop_area."; } else if (sugg.type == 2) { suggDiv.innerHTML = "Move into relation " + sugg.target_osm_rel_id + "."; } else if (sugg.type == 3) { suggDiv.innerHTML = "Move from relation " + sugg.orig_osm_rel_id + " into a new relation public_transport=stop_area."; } else if (sugg.type == 4) { suggDiv.innerHTML = "Move from relation " + sugg.orig_osm_rel_id + " into relation " + sugg.target_osm_rel_id + "."; } else if (sugg.type == 5) { suggDiv.innerHTML = "Move out of relation " + sugg.orig_osm_rel_id + ""; } else if (sugg.type == 6) { suggDiv.innerHTML = "Fix attribute " + sugg.attr + "."; } else if (sugg.type == 7) { suggDiv.innerHTML = "Consider adding a name attribute."; } else if (sugg.type == 8) { suggDiv.innerHTML = "Attribute " + sugg.attr + " seems to be a track number. Use ref for this and set " + sugg.attr + " to the station name."; } suggList.appendChild(suggDiv); } if (map.getZoom() < 18) { map.setView(ll, 18, { animate: true }); } L.popup({ opacity: 0.8 }) .setLatLng(ll) .setContent(content) .openOn(map) .on('remove', function() {openSt = -1; nodeUnHl(stat.id);;}); } function openStat(id) { window.xmlhttp = new XMLHttpRequest(); window.xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200 && this == window.xmlhttp) { var content = JSON.parse(this.responseText); renderStat(content); } }; window.xmlhttp.open("GET", backend + "/stat?id=" + id, true); window.xmlhttp.send(); } function renderGroup(grp, ll) { openGr = grp.id; var attrrows = {}; groupHl(grp.id); var content = document.createElement('div'); content.setAttribute("id", "nav"); var newMembers = document.createElement('div'); newMembers.setAttribute("id", "group-stations-new") newMembers.innerHTML = "New Members"; var oldMembers = document.createElement('div'); oldMembers.setAttribute("id", "group-stations-old") oldMembers.innerHTML = "Existing Members"; if (grp.osmid == 1) { content.innerHTML = "New relation public_transport=stop_area"; } else { content.innerHTML = "OSM relation " + grp.osmid + ""; if (grp.attrs.name) { content.innerHTML += " (\"" + grp.attrs.name + "\")"; } content.innerHTML += ""; } var attrTbl = document.createElement('table'); attrTbl.setAttribute("id", "attr-tbl") content.appendChild(attrTbl); var tbody = document.createElement('tbody'); attrTbl.appendChild(tbody); var suggD = document.createElement('div'); suggD.setAttribute("id", "sugg") for (var key in grp.attrs) { var row = document.createElement('tr'); var col1 = document.createElement('td'); var col2 = document.createElement('td'); col2.className = "err-wrap"; tbody.appendChild(row); row.appendChild(col1); row.appendChild(col2); col1.innerHTML = "" + key + ""; for (var i = 0; i < grp.attrs[key].length; i++) col2.innerHTML += "" + grp.attrs[key][i] + "" + "
"; attrrows[key] = row; } for (var i = 0; i < grp.attrerrs.length; i++) { var err = grp.attrerrs[i]; var row = attrrows[err.attr[0]]; row.className = "err-" + Math.round(err.conf * 10); var info = document.createElement('div'); if (err.other_grp) { // the mismatch was with a group name if (err.other_osmid != grp.osmid) { if (err.other_osmid > 1) { info.innerHTML = "Does not match " + err.other_attr[0] + " in relation " + Math.abs(err.other_osmid) + ""; } else { info.innerHTML = "Does not match " + err.other_attr[0] + " in relation " + Math.abs(err.other_osmid) + ""; } } else { info.innerHTML = "Does not match " + err.other_attr[0] + " = '" + err.other_attr[1] + "'"; } } else { // the mismatch was with another station var ident = err.other_osmid < 0 ? "way" : "node"; info.innerHTML = "Does not match " + err.other_attr[0] + " in " + ident + " " + Math.abs(err.other_osmid) + ""; } info.className = 'attr-err-info'; row.childNodes[1].appendChild(info); } content.appendChild(newMembers); if (grp.osmid != 1) content.appendChild(oldMembers); for (var key in grp.stations) { var stat = grp.stations[key]; var row = document.createElement('div'); var ident = stat.osmid < 0 ? "Way" : "Node"; row.innerHTML = ident + " " + Math.abs(stat.osmid) + ""; if (stat.attrs.name) { row.innerHTML += " (\"" + stat.attrs.name + "\")"; } row.style.backgroundColor = stat.e ? '#f58d8d' : stat.s ? '#b6b6e4' : '#c0f7c0'; if (grp.osmid == 1 || stat.orig_group != grp.id) newMembers.appendChild(row); else { oldMembers.appendChild(row); if (stat.group != grp.id) { row.className = "del-stat"; } } } var suggList = document.createElement('ul'); if (grp.su.length) { var a = document.createElement('span'); a.className = "sugtit"; a.innerHTML = "Suggestions"; suggD.appendChild(a); } suggD.appendChild(suggList); for (var i = 0; i < grp.su.length; i++) { var sugg = grp.su[i]; var suggDiv = document.createElement('li'); if (sugg.type == 6) { suggDiv.innerHTML = "Fix attribute " + sugg.attr + "."; } else if (sugg.type == 7) { suggDiv.innerHTML = "Consider adding a name attribute."; } else if (sugg.type == 8) { suggDiv.innerHTML = "Attribute " + sugg.attr + " seems to be a track number. Use ref for this and set " + sugg.attr + " to the station name."; } suggList.appendChild(suggDiv); } content.appendChild(suggD); if (map.getZoom() < 18) { map.setView(ll, 18, { animate: true }); } L.popup({ opacity: 0.8 }) .setLatLng(ll) .setContent(content) .openOn(map) .on('remove', function() {openGr = -1; groupUnHl(grp.id)}); } function openGroup(id, ll) { window.xmlhttp = new XMLHttpRequest(); window.xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200 && this == window.xmlhttp) { var content = JSON.parse(this.responseText); renderGroup(content, ll); } }; window.xmlhttp.open("GET", backend + "/group?id=" + id, true); window.xmlhttp.send(); } function groupHl(id) { if (!document.groupIdx[id]) return; document.groupIdx[id].setStyle({ 'weight': 6, 'color': "#eecc00" }); } function groupUnHl(id) { if (!document.groupIdx[id]) return; document.groupIdx[id].setStyle({ 'weight': 3, 'color': document.groupIdx[id].options["fillColor"] }); } function nodeHl(id) { if (!document.nodeIdx[id]) return; if (map.getZoom() > 15) { document.nodeIdx[id].setStyle({ 'weight': 5, 'color': "#eecc00" }); } else { document.nodeIdx[id].setStyle({ 'color': "#eecc00" }); } } function nodeUnHl(id) { if (!document.nodeIdx[id]) return; if (map.getZoom() > 15) { document.nodeIdx[id].setStyle({ 'weight': 1, 'color': "black" }); } else { document.nodeIdx[id].setStyle({ 'color': document.nodeIdx[id].options["fillColor"] }); } } var map = L.map('map', { renderer: L.canvas(), attributionControl: false, }).setView([47.9965, 7.8469], 13); map.addControl(L.control.attribution({ position: 'bottomright', prefix: '© University of Freiburg, Chair of Algorithms and Data Structures' })); L.tileLayer('http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png', { maxZoom: 20, attribution: '© OpenStreetMap', opacity: 0.8 }).addTo(map); var layer = L.featureGroup().addTo(map); var labelLayer = L.featureGroup().addTo(map); map.on("moveend", function() { render(); }); function render() { window.xmlhttp = new XMLHttpRequest(); if (map.getZoom() < 11) { window.xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200 && this == window.xmlhttp) { var content = JSON.parse(this.responseText); layer.clearLayers(); labelLayer.clearLayers(); var blur = 22 - map.getZoom(); var rad = 25 - map.getZoom(); layer.addLayer(L.heatLayer(content.ok, { max: 500, gradient: { 0: '#cbf7cb', 0.5: '#78f378', 1: '#29c329' }, minOpacity: 0.65, blur: blur, radius: rad })); layer.addLayer(L.heatLayer(content.sugg, { max: 500, gradient: { 0: '#7f7fbd', 0.5: '#4444b3', 1: '#0606c1' }, minOpacity: 0.65, blur: blur - 3, radius: Math.min(12, rad - 3) })); layer.addLayer(L.heatLayer(content.err, { max: 500, gradient: { 0: '#f39191', 0.5: '#ff5656', 1: '#ff0000' }, minOpacity: 0.75, blur: blur - 3, radius: Math.min(10, rad - 3), maxZoom: 15 })); } }; window.xmlhttp.open("GET", backend + "/heatmap?z=" + map.getZoom() + "&bbox=" + [map.getBounds().getSouthWest().lat, map.getBounds().getSouthWest().lng, map.getBounds().getNorthEast().lat, map.getBounds().getNorthEast().lng].join(","), true); window.xmlhttp.send(); } else { window.xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200 && this == window.xmlhttp) { var content = JSON.parse(this.responseText); layer.clearLayers(); labelLayer.clearLayers(); document.groupIdx = {}; document.nodeIdx = {}; var stations = []; var labels = []; for (var i = 0; i < content.stats.length; i++) { stat = content.stats[i]; var ndMarker = marker(stat, map.getZoom()); stations.push(ndMarker); document.nodeIdx[stat.i] = ndMarker; } var groups = []; for (var i = 0; i < content.groups.length; i++) { group = content.groups[i]; var groupPoly = poly(group, map.getZoom()); groups.push(groupPoly); document.groupIdx[group.i] = groupPoly; } var suggs = []; for (var i = 0; i < content.su.length; i++) { sugg = content.su[i]; var a = sugArr(sugg, map.getZoom()); suggs.push(a); } if (map.getZoom() > 13) { layer.addLayer(L.featureGroup(groups).on('click', function(a) { openGroup(a.layer.options.id, a.layer.getBounds().getCenter()); })); } if (map.getZoom() > 15) { labelLayer.addLayer(L.featureGroup(labels)); layer.addLayer(L.featureGroup(suggs).on('click', function(a) { openStat(a.layer.options.id); })); } layer.addLayer(L.featureGroup(stations).on('click', function(a) { openStat(a.layer.options.id); })); groupHl(openGr); nodeHl(openSt); } }; window.xmlhttp.open("GET", backend + "/map?z=" + map.getZoom() + "&bbox=" + [map.getBounds().getSouthWest().lat, map.getBounds().getSouthWest().lng, map.getBounds().getNorthEast().lat, map.getBounds().getNorthEast().lng].join(","), true); window.xmlhttp.send(); } } render();