Skip to content

Instantly share code, notes, and snippets.

@linquize
Last active November 4, 2018 13:02
Show Gist options
  • Save linquize/41e62921379b96d39c6ec1bf0cf695c4 to your computer and use it in GitHub Desktop.
Save linquize/41e62921379b96d39c6ec1bf0cf695c4 to your computer and use it in GitHub Desktop.
OpenStreetMap Filtered Changesets
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Filtered Changesets | OpenStreetMap</title>
<link rel="stylesheet" href="node_modules/leaflet/dist/leaflet.css" />
<script type="text/javascript" src="node_modules/leaflet/dist/leaflet.js"></script>
<script type="text/javascript" src="node_modules/moment/min/moment.min.js"></script>
<style type="text/css">
body {
font-family: 'Helvetica Neue',Arial,sans-serif;
}
#root {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
#sidebar > div {
position: relative;
float: left;
clear: both;
width: 100%;
}
#sidebar h2 {
font-size: 16px;
font-weight: 600;
padding: 0px 20px 0px;
}
#changesets {
padding: 0;
}
#changesets li {
list-style: none;
cursor: pointer;
font-size: 12px;
padding: 15px 20px;
border-bottom: 1px dotted gray;
}
#changesets li:hover {
background-color: lightyellow;
}
#changesets li a {
text-decoration: none;
color: black;
}
#changesets li .time {
text-decoration: underline dotted;
}
#changesets li .user {
text-decoration: underline;
color: blue;
}
#changesets li .count {
float: right;
color: green;
}
#changesets li .id {
font-style: italic;
}
#sidebar {
float: left;
width: 350px;
height: 100%;
position: relative;
overflow-x: hidden;
overflow-y: auto;
}
#mapid {
height: 100%;
overflow: hidden;
}
@media screen and (max-width: 700px) {
#sidebar {
width: 300px;
height: 50%;
}
#changesets li {
padding: 5px 20px;
}
#mapid {
height: 50%;
width: 100%;
}
}
</style>
</head>
<body>
<div id="root">
<div id="sidebar">
<div>
<h2>Changesets</h2>
<ol id="changesets">
</ol>
</div>
</div>
<div id="mapid"></div>
</div>
<template id="changeset-template">
<li>
<div class="comment"><a href="https://www.openstreetmap.org/changeset/"></a></div>
<div><span class="time"></span> by <a class="user" href="https://www.openstreetmap.org/user/"></a><span class="count"></span></div>
<div class="id"></div>
</li>
</template>
<script type="text/javascript">
let mymap = L.map('mapid').setView([22.360000, 114.100000], 11);
let tileUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
L.tileLayer(tileUrl, {
maxZoom: 19,
attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
}).addTo(mymap);
L.control.scale().addTo(mymap);
</script>
<script type="text/javascript">
function pointInPolygon(point, vs) {
// https://github.com/substack/point-in-polygon
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
let x = point[0], y = point[1];
let inside = false;
for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) {
let xi = vs[i][0], yi = vs[i][1];
let xj = vs[j][0], yj = vs[j][1];
let intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
};
</script>
<script type="text/javascript">
const clipPoly = [
[113.865, 22.334],
[113.866, 22.432],
[113.946, 22.476],
[113.993, 22.514],
[114.001, 22.514],
[114.014, 22.505],
[114.026, 22.511],
[114.037, 22.51],
[114.051, 22.506],
[114.053, 22.507],
[114.055, 22.515],
[114.062, 22.52],
[114.071, 22.522],
[114.075, 22.533],
[114.083, 22.536],
[114.086, 22.54],
[114.094, 22.541],
[114.098, 22.538],
[114.105, 22.539],
[114.11, 22.537],
[114.112, 22.534],
[114.113, 22.537],
[114.118, 22.538],
[114.119, 22.541],
[114.129, 22.546],
[114.134, 22.546],
[114.138, 22.548],
[114.145, 22.546],
[114.149, 22.558],
[114.154, 22.559],
[114.16, 22.566],
[114.178, 22.564],
[114.18, 22.563],
[114.181, 22.559],
[114.19, 22.559],
[114.195, 22.561],
[114.204, 22.56],
[114.21, 22.561],
[114.214, 22.559],
[114.219, 22.56],
[114.228, 22.553],
[114.231, 22.548],
[114.234, 22.554],
[114.275, 22.569],
[114.333, 22.572],
[114.436, 22.566],
[114.459, 22.547],
[114.459, 22.47],
[114.506, 22.367],
[114.506, 22.146],
[114.502, 22.144],
[114.285, 22.144],
[114.256, 22.135],
[114.253, 22.135],
[114.235, 22.144],
[113.941, 22.144],
[113.925, 22.133],
[113.894, 22.139],
[113.83, 22.18],
[113.813, 22.215],
[113.813, 22.219],
[113.836, 22.271],
[113.844, 22.276],
[113.865, 22.334]
];
const min_lon = 113.8197, min_lat = 22.1378, max_lon = 114.5023, max_lat = 22.5684;
const changesetUrl = `https://www.openstreetmap.org/api/0.6/changesets?bbox=${min_lon},${min_lat},${max_lon},${max_lat}`;
fetch(changesetUrl)
.then(a => a.text())
.then(a => new DOMParser().parseFromString(a, "application/xml"))
.then(dom => {
let csList = document.getElementById('changesets');
let changesets = dom.querySelectorAll('changeset');
let filtered = [];
for (let changeset of changesets) {
let cs_min_lon = parseFloat(changeset.getAttribute('min_lon'));
let cs_min_lat = parseFloat(changeset.getAttribute('min_lat'));
let cs_max_lon = parseFloat(changeset.getAttribute('max_lon'));
let cs_max_lat = parseFloat(changeset.getAttribute('max_lat'));
if (cs_min_lon < min_lon) continue;
if (cs_min_lat < min_lat) continue;
if (cs_max_lon > max_lon) continue;
if (cs_max_lat > max_lat) continue;
if (!pointInPolygon([cs_min_lon, cs_min_lat], clipPoly)) continue;
if (!pointInPolygon([cs_max_lon, cs_max_lat], clipPoly)) continue;
let template = document.getElementById('changeset-template');
let row = document.importNode(template.content, true);
let li = row.querySelector('li');
let commentTag = changeset.querySelector("tag[k='comment']");
li.querySelector('.comment a').innerText = commentTag ? commentTag.getAttribute('v') : '(No Comment)';
li.querySelector('.comment a').href += changeset.getAttribute('id');
let time = changeset.getAttribute('closed_at');
li.querySelector('.time').innerText = time ? moment(time).format('YYYY-MM-DDTHH:mm:ss') : '(Unknwon Time)';
li.querySelector('.user').innerText = changeset.getAttribute('user');
li.querySelector('.user').href += changeset.getAttribute('user');
li.querySelector('.count').innerText = 'x' + changeset.getAttribute('changes_count');
li.querySelector('.id').innerText = '#' + changeset.getAttribute('id');
li.addEventListener('mouseover', e => e.target.leafletRect = L.rectangle([[cs_min_lat, cs_min_lon], [cs_max_lat, cs_max_lon]], { color: "#ff7800", weight: 2}).addTo(mymap));
li.addEventListener('mouseout', e => e.target.leafletRect ? e.target.leafletRect.removeFrom(mymap) : null);
csList.appendChild(row);
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment