CINXE.COM

osm tag history

<html> <head> <title>osm tag history</title> <script src="bower_components/d3/d3.min.js"></script> <script src="bower_components/vega/vega.js"></script> <script src="bower_components/es6-promise/es6-promise.auto.min.js"></script> <script src="bower_components/fetch/fetch.js"></script> </head> <body> <h1>OSM Tag History</h1> <p><a href="http://www.openstreetmap.org/user/tyr_asd/diary/39402">Read more background information</a> about this tool. Displays data from taghistory's own <a href="https://github.com/tyrasd/taghistory#api">API</a> as well as <a href="https://taginfo.openstreetmap.org/taginfo/apidoc">taginfo's chronology and stats API</a>.</p> <form id="form"> <select id="type"> <option value="***" selected>any type</option> <option value="node">nodes only</option> <option value="way">ways only</option> <option value="relation">relations only</option> </select> <input type="text" id="key" placeholder="key" maxlength="255" size="30" /> <input type="text" id="value" placeholder="value (leave empty for any value)" maxlength="255" size="30" /> <input type="submit" value="add tag" /> </form> <div id="chart" class="view"></div> <script> var today = +(new Date()); var dataUpdateTags = +(new Date("2023-05-07T23:59:59Z")); var dataUpdateTagKeys = +(new Date("2023-05-07T23:59:59Z")); var osmBirth = +(new Date("2007-10-07T00:00:00Z")); var permalinkTagSeparator = "&"; var permalinkRequestSeparator = "/"; var spec = { "width": 1000, "height": 600, "padding": {"top": 10, "left": 10, "bottom": 30, "right": 70}, "data": [ { "name": "tags", "values": [], "transform": [{"type": "facet", "groupby": ["tag"]}] }, { "name": "taginfo_chronology", "values": [], "transform": [{"type": "facet", "groupby": ["tag"]}] }, { "name": "taginfo_stats", "values": [], "transform": [{"type": "facet", "groupby": ["tag"]}] } ], "signals": [ { "name": "reset", "verbose": true, "streams": [{ "type": "@reset:click", "expr": "random()" }] }, { "name": "resetable", "init": 0, "verbose": true, "streams": [ {"type": "reset", "expr": 0}, {"type": "brush_end, delta, zoom", "expr": 1} ] }, { "name": "shift", "init": false, "verbose": true, "streams": [ {"type": "window:keydown, window:keyup", "expr": "event.shiftKey"} ] }, { "name": "brush_start", "init": {}, "streams": [{ "type": "mousedown[shift]", "expr": "{x: iscale('x', clamp(eventX(), 0, width)), y: iscale('y', clamp(eventY(), 0, height))}" }] }, { "name": "brush", "init": {}, "streams": [{ "type": "mousedown[shift], [mousedown[shift], window:mouseup] > window:mousemove", "expr": "{x: iscale('x', clamp(eventX(), 0, width)), y: iscale('y', clamp(eventY(), 0, height))}" }] }, { "name": "brush_end", "init": 0, "streams": [{ "type": "[mousedown[shift], mousedown[!shift]] > window:mouseup", "expr": "random()" }] }, { "name": "brush1", "init": {}, "streams": [ {"type": "brush_start", "expr": "brush_start"}, {"type": "brush_end", "expr": "{x: 0, y: 0}"} ] }, { "name": "brush2", "init": {}, "streams": [ {"type": "brush", "expr": "brush"}, {"type": "brush_end", "expr": "{x: 0, y: 0}"} ] }, { "name": "delta_start", "init": 0, "streams": [{ "type": "mousedown[!shift]", "expr": "{x: eventX(), y: eventY()}" }] }, { "name": "delta", "init": 0, "streams": [{ "type": "[mousedown[!shift], window:mouseup] > window:mousemove", "expr": "{x: delta_start.x - eventX(), y: eventY() - delta_start.y}" }] }, { "name": "xAnchor", "init": 0, "streams": [{ "type": "mousemove", "expr": "+datetime(iscale('x', clamp(eventX(), 0, width)))" }] }, { "name": "yAnchor", "init": 0, "streams": [{ "type": "mousemove", "expr": "eventY()", "scale": {"name":"y", "invert":true} }] }, { "name": "zoom", "init": 1.0, "verbose": true, "streams": [ {"type": "wheel", "expr": "pow(1.01, event.deltaY*pow(16, event.deltaMode))"} ] }, { "name": "xs", "streams": [{ "type": "mousedown, mouseup, wheel", "expr": "{min: xMin, max: xMax}"} ] }, { "name": "ys", "streams": [{ "type": "mousedown, mouseup, wheel", "expr": "{min: yMin, max: yMax}" }] }, { "name": "globalMaxY", "init": 10, "verbose": true, "streams": [] }, { "name": "xMin", "init": osmBirth, "streams": [ {"type": "delta", "expr": "max(xs.min + (xs.max-xs.min)*delta.x/width, "+osmBirth+")"}, {"type": "zoom", "expr": "max((xs.min-xAnchor)*zoom + xAnchor, "+osmBirth+")"}, {"type": "brush_end", "expr": "min(brush_start.x, brush.x)"}, {"type": "reset", "expr": osmBirth} ] }, { "name": "xMax", "init": today, "streams": [ {"type": "delta", "expr": "min(xs.max + (xs.max-xs.min)*delta.x/width, "+today+")"}, {"type": "zoom", "expr": "min((xs.max-xAnchor)*zoom + xAnchor, "+today+")"}, {"type": "brush_end", "expr": "max(brush_start.x, brush.x)"}, {"type": "reset", "expr": today} ] }, { "name": "yMin", "init": 0, "streams": [ {"type": "delta", "expr": "max(0, ys.min + (ys.max-ys.min)*delta.y/height)"}, {"type": "zoom", "expr": "max(0, (ys.min-yAnchor)*zoom + yAnchor)"}, {"type": "brush_end", "expr": "min(brush_start.y, brush.y)"}, {"type": "reset", "expr": 0} ] }, { "name": "yMax", "init": 10, // ??? "streams": [ {"type": "delta", "expr": "ys.max + (ys.max-ys.min)*delta.y/height"}, {"type": "zoom", "expr": "(ys.max-yAnchor)*zoom + yAnchor"}, {"type": "brush_end", "expr": "max(brush_start.y, brush.y)"}, {"type": "reset", "expr": "globalMaxY"} ] } ], "scales": [ { "name": "x", "type": "utc", "range": "width", "zero": false, "domainMin": {"signal": "xMin"}, "domainMax": {"signal": "xMax"} }, { "name": "y", "type": "linear", //"type": "log", "range": "height", "nice": !true, "zero": false, //"domain": {"data": "tags", "field": "count"} "domainMin": {"signal": "yMin"}, "domainMax": {"signal": "yMax"} }, { "name": "color", "type": "ordinal", "domain": {"data": "tags", "field": "tag"}, "range": "category10" } ], "axes": [ { "type": "x", "scale": "x", "grid": true, "ticks": 12, properties: { "labels": { "align": { "value": "left" }, "dx": { "value": 3 } } } }, { "type": "y", "scale": "y", "grid": true, "orient": "right" } ], "marks": [ { "type": "group", "properties": { "enter": { "x": {"value": 0}, "width": {"field": {"group": "width"}}, "y": {"value": 0}, "height": {"field": {"group": "height"}}, "clip": {"value": true} } }, "from": { "data": "tags" }, "marks": [ { "type": "line", "properties": { "enter": { "interpolate": {"value": "step-after"}, "strokeWidth": 12 }, "update": { "x": {"scale": "x", "field": "date"}, "y": {"scale": "y", "field": "count"}, "stroke": {"scale": "color", "field": "tag"} } } } ] }, { "type": "group", "properties": { "enter": { "x": {"value": 0}, "width": {"field": {"group": "width"}}, "y": {"value": 0}, "height": {"field": {"group": "height"}}, "clip": {"value": true} } }, "from": { "data": "taginfo_chronology" }, "marks": [ { "type": "line", "properties": { "enter": { "interpolate": {"value": "step-after"}, "strokeWidth": 12 }, "update": { "x": {"scale": "x", "field": "date"}, "y": {"scale": "y", "field": "count"}, "stroke": {"scale": "color", "field": "tag"} } } } ] }, { "type": "group", "properties": { "enter": { "x": {"value": 0}, "width": {"field": {"group": "width"}}, "y": {"value": 0}, "height": {"field": {"group": "height"}}, "clip": {"value": true} } }, "from": { "data": "taginfo_stats" }, "marks": [ { "type": "line", "properties": { "enter": { "interpolate": {"value": "linear"}, "strokeWidth": 12 }, "update": { "x": {"scale": "x", "field": "date"}, "y": {"scale": "y", "field": "count"}, "stroke": {"scale": "color", "field": "tag"}, "strokeDash": {"value": [4,4]} } } } ] }, { "type": "rect", "properties": { "enter": { "fill": {"value": "grey"}, "fillOpacity": {"value": 0.05}, "stroke": {"value": "grey"}, "strokeOpacity": {"value": 0.6}, "strokeDash": {"value": [4,6]}, }, "update": { "x": {"scale": "x", "signal": "brush1.x"}, "x2": {"scale": "x", "signal": "brush2.x"}, "y": {"scale": "y", "signal": "brush1.y"}, "y2": {"scale": "y", "signal": "brush2.y"} } } }, { "type": "text", "name": "reset", "properties": { "update": { "text": {"value": "(reset zoom)"}, "x": {"value": 30}, "y": {"value": 16}, "fill": {"value": "grey"}, "fillOpacity": {"signal": "resetable"}, "fontSize": {"value": 14} } } }, ], "legends": [ { "fill": "color", "properties": { "legend": { "x": {"value": 16}, "y": {"value": 30} }, "labels": { "fontSize": {"value": 14} }, "symbols": { "stroke": {"value": "transparent"} } } } ] }; var view; function getTaghistoryAPILink(type, key, value, format) { var keyPart = encodeURIComponent(key.replace(/\//g, '|||')) || '||@||'; var link = "https://taghistory.raifer.tech/"+encodeURIComponent(type)+"/"+keyPart+"/"+encodeURIComponent(value.replace(/\//g, '|||')); if (format !== undefined) { link += '?format='+format; } return link; } function getTaginfoStatsAPILink(key, value) { var link; if (value) link = "https://taginfo.openstreetmap.org/api/4/tag/stats?key="+encodeURIComponent(key)+"&value="+encodeURIComponent(value); else link = "https://taginfo.openstreetmap.org/api/4/key/stats?key="+encodeURIComponent(key); return link; } function getTaginfoChronologyAPILink(key, value) { var link; if (value) link = "https://taginfo.openstreetmap.org/api/4/tag/chronology?key="+encodeURIComponent(key)+"&value="+encodeURIComponent(value); else link = "https://taginfo.openstreetmap.org/api/4/key/chronology?key="+encodeURIComponent(key); return link; } function addToPlot(type, key, value, doneCallback) { var tagDescription = (type === '***' ? '' : type + ' ') + key + (value ? '=' + value : ''); fetch(getTaghistoryAPILink(type, key, value)) .then(function(d) { return d.json(); }) .then(function(d) { var permalinkPart = type + permalinkRequestSeparator + encodeURIComponent(key) + permalinkRequestSeparator + encodeURIComponent(value); d.forEach(function(r) { r.tag = tagDescription; r.date = +(new Date(r.date)); }); d.unshift({ tag: tagDescription, permalinkPart: permalinkPart, date: osmBirth, delta: 0, count: 0 }); var lastCount = d[d.length-1].count; var lastDay = d[d.length-1].date; d.push({ tag: tagDescription, date: value ? dataUpdateTags : dataUpdateTagKeys, delta: 0, count: lastCount }); view.data('tags').insert(d); var newMax = d.reduce(function(acc, r) { return Math.max(acc,r.count); }, 0); view.signal('globalMaxY', Math.max(newMax*1.05, view.signal('globalMaxY'))); // trigger chart reset if it hasn't been zoomed yet if (view.signal('resetable') == 0) view.signal('reset', Math.random()); view.update(); // update permalink var permalinkHash = view.data().tags.map(function(d) { return d.values[0].permalinkPart; }).join(permalinkTagSeparator); var permalinkElement = document.getElementById('permalink'); permalinkElement.href = permalinkElement.href.substring(0, permalinkElement.href.indexOf('#')) + '#' + permalinkHash; window.history.replaceState(null, "", permalinkElement.href); // trigger doneCallback if (doneCallback !== undefined) { doneCallback.call(); } var taginfoDataUntil; var taginfoHasChronology = true; fetch(getTaginfoChronologyAPILink(key, value)) .then(function(d) { return d.json(); }) .then(function(d) { taginfoDataUntil = +(new Date(d.data_until)); if (d.total === 0) { taginfoHasChronology = false; fetch(getTaginfoStatsAPILink(key, value)) .then(function(d) { return d.json(); }) .then(function(d) { return d.data.filter(function(e) { return e.type === (type === '***' ? 'all' : type + 's'); })[0].count; }) .then(function(taginfoCount) { var d = [{ tag: tagDescription, date: value ? dataUpdateTags : dataUpdateTagKeys, count: lastCount }, { tag: tagDescription, date: taginfoDataUntil, count: taginfoCount }]; view.data('taginfo_stats').insert(d); view.signal('globalMaxY', Math.max(taginfoCount*1.05, view.signal('globalMaxY'))); // trigger chart reset if it hasn't been zoomed yet if (view.signal('resetable') == 0) view.signal('reset', Math.random()); view.update(); }); } return d; }) .then(function(d) { return d.data.map(function(e) { if (type !== '***') return { tag: tagDescription, date: +(new Date(e.date + "T00:00:00.000Z")), delta: e[type + 's'] }; else return { tag: tagDescription, date: +(new Date(e.date + "T00:00:00.000Z")), delta: e.nodes + e.ways + e.relations }; }); }) .then(function(d) { var count = 0; d.forEach(function(e) { count += e.delta; e.count = count; }); return d; }) .then(function(d) { return d.filter(function(e) { return e.date > lastDay; }); }) .then(function(taginfoChronology) { if (!taginfoHasChronology) return; taginfoChronology.unshift({ tag: tagDescription, date: lastDay, delta: 0, count: lastCount }); taginfoChronology.push({ tag: tagDescription, date: taginfoDataUntil, delta: 0, count: taginfoChronology[taginfoChronology.length-1].count }); view.data('taginfo_chronology').insert(taginfoChronology); var newMax = taginfoChronology.reduce(function(acc, r) { return Math.max(acc,r.count); }, 0); view.signal('globalMaxY', Math.max(newMax*1.05, view.signal('globalMaxY'))); // trigger chart reset if it hasn't been zoomed yet if (view.signal('resetable') == 0) view.signal('reset', Math.random()); view.update(); }) .catch(function(error) { console.error(error); }); }) .catch(function(error) { console.error(error); }); } function exportImage(format) { var resetableValue = view.signal('resetable'); if (resetableValue != 0) { view.signal('resetable', 0); view.update(); } var imageResult = view.toImageURL(format); if (resetableValue != 0) { view.signal('resetable', resetableValue); view.update(); } return imageResult; } function loadFromHash(hash) { if (hash == "") { return; } var tagsFromHash = hash.split(permalinkTagSeparator); var tkv = tagsFromHash.shift().split(permalinkRequestSeparator); // treat empty type as "***" var type = tkv[0] == "" ? "***" : tkv[0]; // key must be given and is used as-is var key = decodeURIComponent(tkv[1]); // treat missing value as no value var value = tkv.length == 3 ? decodeURIComponent(tkv[2]) : ""; addToPlot(type, key, value, function() { // recursively call addToPlot for further tags, if any loadFromHash(tagsFromHash.join(permalinkTagSeparator)); }); } function loadGraphs() { vg.parse.spec(spec, function(chart) { view = chart({el:"#chart", renderer: 'svg', data:[]}); view.update(); loadFromHash(document.location.hash.substring(1)); document.getElementById('form').addEventListener('submit', function(e) { e.preventDefault(); var type = document.getElementById('type').value; var key = document.getElementById('key').value; var value = document.getElementById('value').value; addToPlot(type, key, value); }); }); } document.addEventListener("DOMContentLoaded", loadGraphs); window.addEventListener("hashchange", loadGraphs); </script> <p> download graph as <a href="#" download="taghistory.svg" onmousedown="this.href=exportImage('svg')">svg</a> / <a href="#" download="taghistory.png" onmousedown="this.href=exportImage('png')">png</a> </p> <p> <a href="#" id="permalink">permalink</a> </p> <p> Data 漏 <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> (<a href="http://opendatacommons.org/licenses/odbl/1.0/">ODbL</a>). </p>

Pages: 1 2 3 4 5 6 7 8 9 10