From ec2517a12a3367b6defd90416e6cbc28bb4fb82d Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Fri, 20 Mar 2015 09:46:24 +0100 Subject: [PATCH] initial commit --- Leaflet.MakiMarkers.js | 108 +++++++++++++++++++++++++++ history.html | 106 +++++++++++++++++++++++++++ history.js | 163 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 Leaflet.MakiMarkers.js create mode 100644 history.html create mode 100644 history.js diff --git a/Leaflet.MakiMarkers.js b/Leaflet.MakiMarkers.js new file mode 100644 index 0000000..a3a496c --- /dev/null +++ b/Leaflet.MakiMarkers.js @@ -0,0 +1,108 @@ +/* + * Leaflet plugin to create map icons using Maki Icons from MapBox. + * + * References: + * Maki Icons: https://www.mapbox.com/maki/ + * MapBox Marker API: https://www.mapbox.com/developers/api/static/#markers + * + * Usage: + * var icon = L.MakiMarkers.icon({icon: "rocket", color: "#b0b", size: "m"}); + * + * License: + * MIT: http://jseppi.mit-license.org/ + */ + /*global L:false */ +(function () { + "use strict"; + L.MakiMarkers = { + // Available Maki Icons + icons: ["airfield","airport","alcohol-shop","america-football","art-gallery","bakery","bank","bar", + "baseball","basketball","beer","bicycle","building","bus","cafe","camera","campsite","car", + "cemetery","chemist","cinema","circle-stroked","circle","city","clothing-store","college", + "commercial","cricket","cross","dam","danger","disability","dog-park","embassy", + "emergency-telephone","entrance","farm","fast-food","ferry","fire-station","fuel","garden", + "golf","grocery","hairdresser","harbor","heart","heliport","hospital","industrial", + "land-use","laundry","library","lighthouse","lodging","logging","london-underground", + "marker-stroked","marker","minefield","mobilephone","monument","museum","music","oil-well", + "park2","park","parking-garage","parking","pharmacy","pitch","place-of-worship", + "playground","police","polling-place","post","prison","rail-above","rail-light", + "rail-metro","rail-underground","rail","religious-christian","religious-jewish", + "religious-muslim","restaurant","roadblock","rocket","school","scooter","shop","skiing", + "slaughterhouse","soccer","square-stroked","square","star-stroked","star","suitcase", + "swimming","telephone","tennis","theatre","toilets","town-hall","town","triangle-stroked", + "triangle","village","warehouse","waste-basket","water","wetland","zoo" + ], + defaultColor: "#0a0", + defaultIcon: "circle-stroked", + defaultSize: "m", + apiUrl: "https://api.tiles.mapbox.com/v3/marker/", + smallOptions: { + iconSize: [20, 50], + popupAnchor: [0,-20] + }, + mediumOptions: { + iconSize: [30,70], + popupAnchor: [0,-30] + }, + largeOptions: { + iconSize: [36,90], + popupAnchor: [0,-40] + } + }; + + L.MakiMarkers.Icon = L.Icon.extend({ + options: { + //Maki icon: any from https://www.mapbox.com/maki/ (ref: L.MakiMarkers.icons) + icon: L.MakiMarkers.defaultIcon, + //Marker color: short or long form hex color code + color: L.MakiMarkers.defaultColor, + //Marker size: "s" (small), "m" (medium), or "l" (large) + size: L.MakiMarkers.defaultSize, + shadowAnchor: null, + shadowSize: null, + shadowUrl: null, + className: "maki-marker" + }, + + initialize: function(options) { + var pin; + + options = L.setOptions(this, options); + + switch (options.size) { + case "s": + L.extend(options, L.MakiMarkers.smallOptions); + break; + case "l": + L.extend(options, L.MakiMarkers.largeOptions); + break; + default: + options.size = "m"; + L.extend(options, L.MakiMarkers.mediumOptions); + break; + } + + + pin = "pin-" + options.size; + + if (options.icon !== null) { + pin += "-" + options.icon; + } + + if (options.color !== null) { + if (options.color.charAt(0) === "#") { + options.color = options.color.substr(1); + } + + pin += "+" + options.color; + } + + options.iconUrl = "" + L.MakiMarkers.apiUrl + pin + ".png"; + options.iconRetinaUrl = L.MakiMarkers.apiUrl + pin + "@2x.png"; + } + }); + + L.MakiMarkers.icon = function(options) { + return new L.MakiMarkers.Icon(options); + }; +})(); diff --git a/history.html b/history.html new file mode 100644 index 0000000..3913ad9 --- /dev/null +++ b/history.html @@ -0,0 +1,106 @@ + + + + + Neue und verschwundene Knoten + + + + + + + + + +
+
+
+
+

+ Zeigt Knoten an, die in den letzten 14 Tagen dazu gekommen oder verschwunden sind. + Funktioniert nur in wirklich modernen Browsern. +

+ +

Neue Knoten

+ + + +
+ +

Verschwundene Knoten

+ + + +
+
+
+ + diff --git a/history.js b/history.js new file mode 100644 index 0000000..d4f9ae5 --- /dev/null +++ b/history.js @@ -0,0 +1,163 @@ +document.addEventListener('DOMContentLoaded', main) + +function get(url) { + return new Promise(function(resolve, reject) { + var req = new XMLHttpRequest(); + req.open('GET', url); + + req.onload = function() { + if (req.status == 200) { + resolve(req.response); + } + else { + reject(Error(req.statusText)); + } + }; + + req.onerror = function() { + reject(Error("Network Error")); + }; + + req.send(); + }); +} + +function getJSON(url) { + return get(url).then(JSON.parse) +} + +function main() { + getJSON('nodes.json').then(handle_data) +} + +function sort(key, d) { + return d.slice().sort( function (a, b) { + return a[key] - b[key] + }).reverse() +} + +function limit(key, m, d) { + return d.filter( function (d) { + return d[key].isAfter(m) + }) +} + +function offline(d) { + return !d.flags.online +} + +function online(d) { + return d.flags.online +} + +function has_location(d) { + return "location" in d.nodeinfo +} + +function subtract(a, b) { + var ids = {} + + b.forEach( function (d) { + ids[d.nodeinfo.node_id] = true + }) + + return a.filter( function (d) { + return !(d.nodeinfo.node_id in ids) + }) +} + +function handle_data(data) { + var nodes = Object.keys(data.nodes).map(function (key) { return data.nodes[key] }) + + nodes = nodes.filter( function (d) { + return "firstseen" in d && "lastseen" in d + }) + + nodes.forEach( function(node) { + node.firstseen = moment(node.firstseen) + node.lastseen = moment(node.lastseen) + }) + + var age = moment().subtract(14, 'days') + + var newnodes = limit("firstseen", age, sort("firstseen", nodes).filter(online)) + var lostnodes = limit("lastseen", age, sort("lastseen", nodes).filter(offline)) + + var onlinenodes = subtract(nodes.filter(online).filter(has_location), newnodes) + + addToList(document.getElementById("newnodes"), "firstseen", newnodes) + addToList(document.getElementById("lostnodes"), "lastseen", lostnodes) + + mkmap(document.getElementById("map"), newnodes, lostnodes, onlinenodes) +} + +function mkmap(el, newnodes, lostnodes, onlinenodes) { + var map = L.map(el) + + L.tileLayer("http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg", { + subdomains: "1234", + type: "osm", + attribution: "Map data Tiles © MapQuest , Map data © OpenStreetMap contributors, CC-BY-SA", + maxZoom: 18 + }).addTo(map) + + var nodes = newnodes.concat(lostnodes).filter( function (d) { + return "location" in d.nodeinfo + }) + + var markers = nodes.map( function (d) { + var icon = L.MakiMarkers.icon({ color: d.flags.online ? "#0A905D" : "#E42426" }) + + var opt = { icon: icon } + + var m = L.marker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], opt) + + m.bindPopup(d.nodeinfo.hostname) + + return m + }) + + var onlinemarkers = onlinenodes.map( function (d) { + var opt = { color: "#0A905D", + fillColor: "#0A905D", + radius: 5 + } + + var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], opt) + + m.bindPopup(d.nodeinfo.hostname) + + return m + }) + + var group = L.featureGroup(markers).addTo(map) + var group_online = L.featureGroup(onlinemarkers).addTo(map) + + map.fitBounds(group.getBounds()) +} + +function addToList(el, tf, list) { + list.forEach( function (d) { + var time = moment(d[tf]).fromNow() + + var row = document.createElement("tr") + var td1 = document.createElement("td") + var span = document.createElement("span") + span.classList.add("hostname") + span.classList.add(d.flags.online ? "online" : "offline") + span.textContent = d.nodeinfo.hostname + td1.appendChild(span) + + if ("owner" in d.nodeinfo) { + var contact = d.nodeinfo.owner.contact + td1.appendChild(document.createTextNode(" – " + contact + "")) + } + + var td2 = document.createElement("td") + td2.textContent = time + + row.appendChild(td1) + row.appendChild(td2) + el.appendChild(row) + }) +}