Initial version of subway render with leaflet js library
This commit is contained in:
parent
56f9a9586c
commit
3b6655f259
5 changed files with 431 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
|||
__pycache__/
|
||||
tmp_html/
|
||||
html/
|
||||
.idea
|
||||
.DS_Store
|
||||
*.log
|
||||
*.json
|
||||
*.geojson
|
||||
|
|
6
render/cities.txt.example
Normal file
6
render/cities.txt.example
Normal file
|
@ -0,0 +1,6 @@
|
|||
Abuja, Nigeria
|
||||
Addis Ababa, Ethiopia
|
||||
Berlin S-Bahn, Germany
|
||||
Moscow, Russia
|
||||
Zhengzhou, China
|
||||
İzmir, Turkey
|
250
render/js/lib/xhr.js
Normal file
250
render/js/lib/xhr.js
Normal file
|
@ -0,0 +1,250 @@
|
|||
/**
|
||||
* XHR.js - A vanilla javascript wrapper for the XMLHttpRequest object.
|
||||
* @version 0.1.1
|
||||
* @author George Raptis (https://github.com/georapbox)
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 George Raptis
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
(function (name, context, definition) {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = definition();
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(definition);
|
||||
} else {
|
||||
context[name] = definition();
|
||||
}
|
||||
}('XHR', this, function () {
|
||||
var helpers = {
|
||||
extend: function() {
|
||||
'use strict';
|
||||
for (var i = 1, l = arguments.length; i < l; i += 1) {
|
||||
for (var key in arguments[i]) {
|
||||
if (arguments[i].hasOwnProperty(key)) {
|
||||
if (arguments[i][key] && arguments[i][key].constructor && arguments[i][key].constructor === Object) {
|
||||
arguments[0][key] = arguments[0][key] || {};
|
||||
helpers.extend(arguments[0][key], arguments[i][key]);
|
||||
} else {
|
||||
arguments[0][key] = arguments[i][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return arguments[0];
|
||||
},
|
||||
|
||||
encodeUrl: function (url) {
|
||||
var domain = url.substring(0, url.indexOf('?') + 1),
|
||||
search = url.substring(url.indexOf('?') + 1),
|
||||
vars = search ? search.split('&') : [],
|
||||
varsLen = vars.length,
|
||||
encodedUrl = domain,
|
||||
pair,
|
||||
i;
|
||||
|
||||
for (i = 0; i < varsLen; i += 1) {
|
||||
pair = vars[i].split('=');
|
||||
encodedUrl += encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]) + '&';
|
||||
}
|
||||
|
||||
encodedUrl = encodedUrl.substring(0, encodedUrl.length - 1);
|
||||
return encodedUrl;
|
||||
},
|
||||
|
||||
serialize: function (form) {
|
||||
var parts = [],
|
||||
field = null,
|
||||
i,
|
||||
len,
|
||||
j,
|
||||
optLen,
|
||||
option,
|
||||
optValue;
|
||||
|
||||
for (i = 0, len = form.elements.length; i < len; i += 1) {
|
||||
field = form.elements[i];
|
||||
|
||||
switch (field.type) {
|
||||
case 'select-one':
|
||||
case 'select-multiple':
|
||||
if (field.name.length) {
|
||||
for (j = 0, optLen = field.options.length; j < optLen; j += 1) {
|
||||
option = field.options[j];
|
||||
|
||||
if (option.selected) {
|
||||
optValue = '';
|
||||
|
||||
if (option.hasAttribute) {
|
||||
optValue = (option.hasAttribute('value') ? option.value : option.text);
|
||||
} else {
|
||||
optValue = (option.attributes.value.specified ? option.value : option.text);
|
||||
}
|
||||
|
||||
parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(optValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case undefined: // fieldset
|
||||
case 'file': // file input
|
||||
case 'submit': // submit button
|
||||
case 'reset': // reset button
|
||||
case 'button': // custom button
|
||||
break;
|
||||
case 'radio': // radio button
|
||||
case 'checkbox': // checkbox
|
||||
if (!field.checked) {
|
||||
break;
|
||||
}
|
||||
/* falls through */
|
||||
default:
|
||||
// Don't include form fields without names.
|
||||
if (field.name.length) {
|
||||
parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts.join('&');
|
||||
}
|
||||
};
|
||||
|
||||
var XHR = function (options) {
|
||||
var that = this,
|
||||
defaults,
|
||||
xhr,
|
||||
i,
|
||||
customHeadersLen,
|
||||
customHeadersItem;
|
||||
|
||||
// Define default options.
|
||||
defaults = {
|
||||
method: 'get', // Type of request.
|
||||
url: '', // Request url (relative path).
|
||||
async: true, // Defines if request is asynchronous or not.
|
||||
serialize: false, // Defines if forms data sent in a POST request should be serialized.
|
||||
data: null, // Data to be sent as the body of the request. Default is "null" for browser compatibility issues.
|
||||
contentType: 'application/x-www-form-urlencoded', // Sets the Content Type of the request.
|
||||
responseType: 'xml',
|
||||
customHeaders: [], // Set custom request headers. Default value is empty array.
|
||||
success: function () {}, // Callback function to handle success.
|
||||
error: function () {} // Callback function to handle errors.
|
||||
};
|
||||
|
||||
// Extend the default options with user's specified ones.
|
||||
options = helpers.extend({}, defaults, options);
|
||||
|
||||
that.method = options.method;
|
||||
that.url = options.url;
|
||||
that.async = options.async;
|
||||
that.serialize = options.serialize;
|
||||
that.data = options.data;
|
||||
that.contentType = options.contentType;
|
||||
that.responseType = options.responseType;
|
||||
that.customHeaders = options.customHeaders;
|
||||
that.success = options.success;
|
||||
that.error = options.error;
|
||||
that.progressEvent = {};
|
||||
|
||||
customHeadersLen = that.customHeaders.length;
|
||||
|
||||
// Create a new XMLHttpRequest.
|
||||
xhr = new XMLHttpRequest();
|
||||
|
||||
// onreadystatechange event
|
||||
xhr.onreadystatechange = function () {
|
||||
// if request is completed, handle success or error states.
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
|
||||
that.success(xhr.status, xhr.responseText, xhr.responseXML, xhr.statusText);
|
||||
} else {
|
||||
that.error(xhr.status, xhr.responseText, xhr.responseXML, xhr.statusText);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// onprogress event
|
||||
xhr.onprogress = function (event) {
|
||||
if (event.lengthComputable){
|
||||
that.progressEvent = {
|
||||
bubbles: event.bubbles,
|
||||
cancelable: event.cancelable,
|
||||
currentTarget: event.currentTarget,
|
||||
defaultPrevented: event.defaultPrevented,
|
||||
eventPhase: event.eventPhase,
|
||||
explicitOriginalTarget: event.explicitOriginalTarget,
|
||||
isTrusted: event.isTrusted,
|
||||
lengthComputable: event.lengthComputable,
|
||||
loaded: event.loaded,
|
||||
originalTarget: event.originalTarget,
|
||||
target: event.target,
|
||||
timeStamp: event.timeStamp,
|
||||
total: event.total,
|
||||
type: event.type
|
||||
};
|
||||
|
||||
return that.progressEvent;
|
||||
}
|
||||
};
|
||||
|
||||
// Encode URL in case of a "GET" request.
|
||||
if (that.method === 'get') {
|
||||
that.url = helpers.encodeUrl(that.url);
|
||||
}
|
||||
|
||||
// Prepare the request to be sent.
|
||||
xhr.open(that.method, that.url, that.async);
|
||||
|
||||
// Set "Content-Type".
|
||||
if (that.contentType !== false) {
|
||||
xhr.setRequestHeader('Content-Type', that.contentType);
|
||||
}
|
||||
|
||||
// Set custom headers.
|
||||
if (customHeadersLen > 0) {
|
||||
for (i = 0; i < customHeadersLen; i += 1) {
|
||||
customHeadersItem = that.customHeaders[i];
|
||||
|
||||
if (typeof customHeadersItem === 'object') {
|
||||
for (var prop in customHeadersItem) {
|
||||
if (customHeadersItem.hasOwnProperty(prop)) {
|
||||
xhr.setRequestHeader(prop, customHeadersItem[prop]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Property "customHeader" expects an array of objects for value.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize form if option set to "true".
|
||||
if (that.serialize === true) {
|
||||
that.data = helpers.serialize(that.data);
|
||||
}
|
||||
|
||||
// Send data.
|
||||
xhr.send(that.data);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
return XHR;
|
||||
}));
|
121
render/js/metro.js
Normal file
121
render/js/metro.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
function slugify(name) {
|
||||
name = name.toLowerCase();
|
||||
name = name.replace(/ /g, '_');
|
||||
name = name.replace(/[^a-z0-9_-]+/g, '');
|
||||
return name;
|
||||
}
|
||||
|
||||
// Inspired by http://ahalota.github.io/Leaflet.CountrySelect
|
||||
L.CitySelect = L.Control.extend({
|
||||
options: {
|
||||
position: 'topright',
|
||||
title: 'City'
|
||||
},
|
||||
onAdd: function(map) {
|
||||
this.div = L.DomUtil.create('div');
|
||||
this.select = L.DomUtil.create('select', 'leaflet-countryselect', this.div);
|
||||
var that = this;
|
||||
|
||||
var xhr = new XHR({
|
||||
method: 'get',
|
||||
url: 'cities.txt?',
|
||||
async: true,
|
||||
data: {},
|
||||
serialize: false,
|
||||
success: function (status, responseText, responseXML, statusText) {
|
||||
if (status == 200 && responseText) {
|
||||
var cities = responseText.split("\n");
|
||||
cities = cities.filter(city => city.length).sort();
|
||||
|
||||
var content = '';
|
||||
|
||||
if (that.options.title.length > 0) {
|
||||
content += '<option>' + that.options.title + '</option>';
|
||||
}
|
||||
|
||||
for (var i = 0; i < cities.length; ++i){
|
||||
city_name = cities[i].split(',')[0];
|
||||
content += '<option value="' + city_name+ '">' + cities[i] + '</option>';
|
||||
}
|
||||
|
||||
that.select.innerHTML = content;
|
||||
}
|
||||
},
|
||||
error: function (status, responseText, responseXML, statusText) {
|
||||
console.log('Request was unsuccessful: ' + status + ', ' + statusText);
|
||||
}
|
||||
});
|
||||
|
||||
this.select.onmousedown = L.DomEvent.stopPropagation;
|
||||
return this.div;
|
||||
},
|
||||
on: function(type, handler) {
|
||||
if (type == 'change') {
|
||||
this.onChange = handler;
|
||||
L.DomEvent.addListener(this.select, 'change', this._onChange, this);
|
||||
} else {
|
||||
console.log('CitySelect - cannot handle ' + type + ' events.')
|
||||
}
|
||||
},
|
||||
_onChange: function(e) {
|
||||
var selectedCity = this.select.options[this.select.selectedIndex].value;
|
||||
e.cityName = selectedCity;
|
||||
this.onChange(e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
L.citySelect = function(id, options) {
|
||||
return new L.CitySelect(id, options);
|
||||
};
|
||||
|
||||
|
||||
var selector = L.citySelect({position: 'topright'}).addTo(map);
|
||||
selector.on('change', function(e) {
|
||||
if (e.cityName === 'City')
|
||||
return;
|
||||
|
||||
var cityName = slugify(e.cityName);
|
||||
|
||||
var xhr = new XHR({
|
||||
method: 'get',
|
||||
url: cityName + '.geojson?',
|
||||
async: true,
|
||||
responseType: 'json',
|
||||
data: {},
|
||||
serialize: false,
|
||||
success: function (status, responseText, responseXML, statusText) {
|
||||
if (status == 200 && responseText) {
|
||||
|
||||
var json = JSON.parse(responseText);
|
||||
var newCity = L.geoJSON(json, {
|
||||
style: function(feature) {
|
||||
if ('stroke' in feature.properties)
|
||||
return {color: feature.properties.stroke};
|
||||
},
|
||||
pointToLayer: function (feature, latlng) {
|
||||
return L.circleMarker(latlng, {
|
||||
color: feature.properties['marker-color'],
|
||||
//line-width: 1,
|
||||
//weight: 1,
|
||||
radius: 4
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (map.previousCity != null) {
|
||||
map.removeLayer(map.previousCity);
|
||||
}
|
||||
map.previousCity = newCity;
|
||||
|
||||
map.addLayer(newCity);
|
||||
map.fitBounds(newCity.getBounds());
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
error: function (status, responseText, responseXML, statusText) {
|
||||
console.log('Request was unsuccessful: ' + status + ', ' + statusText);
|
||||
}
|
||||
});
|
||||
});
|
52
render/render.html
Normal file
52
render/render.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossorigin=""></script>
|
||||
|
||||
<style>
|
||||
html, body
|
||||
{
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#map {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.leaflet-countryselect {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
OSM_URL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
var osmAttrib = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
osm = L.tileLayer(OSM_URL, {
|
||||
maxZoom: 18,
|
||||
attribution: osmAttrib,
|
||||
opacity: 0.5
|
||||
});
|
||||
|
||||
var initialLocation = [55.7510888, 37.7642849];
|
||||
|
||||
var map = L.map('map').setView(initialLocation, 15).addLayer(osm);
|
||||
|
||||
L.marker(initialLocation)
|
||||
.addTo(map)
|
||||
.bindPopup('Choose a city from the list at the top-right corner!')
|
||||
.openPopup();
|
||||
</script>
|
||||
<script src="js/lib/xhr.js"></script>
|
||||
<script src="js/metro.js"></script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue