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();