diff --git a/GTFSManager.py b/GTFSManager.py
index 0758fc3..39bea10 100644
--- a/GTFSManager.py
+++ b/GTFSManager.py
@@ -27,6 +27,7 @@
import numpy as np
import io # used in hyd csv import
import requests, platform # used to log user stats
+requests.packages.urllib3.disable_warnings() # suppress warning messages like "InsecureRequestWarning: Unverified HTTPS request is being made." from https://stackoverflow.com/a/44850849/4355695
# setting constants
root = os.path.dirname(__file__) # needed for tornado
@@ -43,6 +44,8 @@
configFile = 'config.json'
thisURL = ''
+# paths were you must not tread.. see "MyStaticFileHandler" class in apy.py
+forbiddenPaths = ['/pw/']
debugMode = False # using this flag at various places to do or not do things based on whether we're in development or production
requiredFeeds = ['agency.txt','calendar.txt','stops.txt','routes.txt','trips.txt','stop_times.txt']
@@ -69,7 +72,23 @@
logmessage('Loaded dependencies, starting static GTFS Manager program.')
-'''
+#################################
+# Tornado classes : One class = one API call / request
+
+# 22.4.19 Making a custom class/function to control how the user's browser loads normal URLs
+# from https://stackoverflow.com/a/55762431/4355695 : restrict direct browser access to .py files and stuff
+class MyStaticFileHandler(tornado.web.StaticFileHandler):
+ def validate_absolute_path(self, root, absolute_path):
+ if absolute_path.endswith('.py') or any([ (x in absolute_path) for x in forbiddenPaths]):
+ # raise tornado.web.HTTPError(403) # this is the usual thing: raise 403 Forbidden error. But instead..
+ return os.path.join(root,'lib','errorpage.txt')
+
+ if absolute_path.endswith('favicon.ico'):
+ return os.path.join(root,'lib','favicon.ico')
+
+ return super().validate_absolute_path(root, absolute_path) # you may pass
+
+comment= '''
# Tornado API functions template:
class APIHandler(tornado.web.RequestHandler):
def get(self):
@@ -1398,7 +1417,9 @@ def make_app():
(r"/API/tableReadSave", tableReadSave),
(r"/API/tableColumn", tableColumn),
#(r"/API/idList", idList),
- (r"/(.*)", tornado.web.StaticFileHandler, {"path": root, "default_filename": "index.html"})
+ # (r"/(.*)", tornado.web.StaticFileHandler, {"path": root, "default_filename": "index.html"})
+ (r"/(.*)", MyStaticFileHandler, {"path": root, "default_filename": "index.html"})
+
])
# for catching Ctrl+C and exiting gracefully. From https://nattster.wordpress.com/2013/06/05/catch-kill-signal-in-python/
diff --git a/GTFSserverfunctions.py b/GTFSserverfunctions.py
index ae012c9..48a0217 100644
--- a/GTFSserverfunctions.py
+++ b/GTFSserverfunctions.py
@@ -1667,7 +1667,7 @@ def logUse(action='launch'):
cvar['3'] = ['version', platform.release() ]
payload['_cvar'] = json.dumps(cvar)
try:
- r = requests.get('http://nikhilvj.co.in/tracking/piwik.php', params=payload, verify=False, timeout=1)
+ r = requests.get('https://nikhilvj.co.in/tracking/piwik.php', params=payload, verify=False, timeout=1)
except requests.exceptions.RequestException as e:
# print('exception',e)
pass
\ No newline at end of file
diff --git a/README.md b/README.md
index e6061c9..601ddec 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
A browser-based user interface for creating, editing, exporting of static GTFS (General Transit Feed Specification Reference) feeds for a public transit authority.
-**Release Status** : V 3.4.2 (live branch may be some commits ahead)
+**Release Status** : V 3.4.3 (live branch may be some commits ahead)
Download from [Releases page](https://github.com/WRI-Cities/static-GTFS-manager/releases/).
- Windows binary is available in the repo itself, just double-click GTFSManager.exe to start the program.
- Mac, Ubuntu users can run by first installing docker, then double-clicking two scripts. See [Running with Docker on any OS](https://github.com/WRI-Cities/static-GTFS-manager/wiki/Running-with-Docker-on-any-OS)
@@ -20,10 +20,12 @@ The GTFS data pre-loaded in the program is of Kochi Metro, Kerala, India which o
See the [KMRL open data portal](https://kochimetro.org/open-data/) and some news articles: [1](http://www.newindianexpress.com/cities/kochi/2018/mar/17/kochi-metro-adopts-open-data-system-to-improve-access-to-its-services-1788342.html), [2](http://indianexpress.com/article/india/kochi-metro-throws-open-transit-data-to-public-on-the-lines-of-london-new-york-5100381/), [3](http://www.thehindu.com/news/cities/Kochi/open-data-to-improve-commuter-experience/article23275844.ece), [4](http://www.thehindu.com/news/cities/Kochi/kmrl-moves-a-step-ahead-to-open-up-transit-data/article23247617.ece).
-This program adheres to the static GTFS (General Transit Feed Specification Reference) open transit data specs as published by Google Transit here:
-It also implements a [GTFS extension for translations](https://developers.google.com/transit/gtfs/reference/gtfs-extensions#translations) of stops and routes names to facilitate multilingual use of the data.
+This program adheres strictly to the static GTFS (General Transit Feed Specification Reference) open transit data specs as published by Google Transit here:
-Lead programmer up till November 2018: [Nikhil VJ](https://answerquest.github.io) from Pune, India.
+Note: We have created a second complimentary application **[Payanam](https://github.com/WRI-Cities/payanam)** to address needs where a) the system is simple and routes don't have variations; b) properly mapped stops data is not available to begin with.
+**[Payanam](https://github.com/WRI-Cities/payanam)** can take you from nothing up to a basic GTFS, and after that you can use this application to edit it. So check it out!
+
+Lead programmer as of July 2019: [Nikhil VJ](https://nikhilvj.co.in) from Pune, India.
See this and many more GTFS related resources listed on **[Awesome Transit](https://github.com/CUTR-at-USF/awesome-transit#gtfs-tools)**, a one-stop community listing for all things GTFS.
diff --git a/js/deleteID.js b/js/deleteID.js
index 23bea06..2beeecd 100644
--- a/js/deleteID.js
+++ b/js/deleteID.js
@@ -417,6 +417,7 @@ function populateLists() {
$('#stop2Delete').html(content);
$('#stop2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Stop'});
$('#stop2Delete').trigger('chosen:updated'); // if the function is called again, then update it
+ $("#stop2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#stop2Delete').on('change', function(evt,params) {
if(!params) return;
let stop_id = params.selected;
@@ -436,6 +437,7 @@ function populateLists() {
$('#route2Delete').html(content);
$('#route2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Route' });
$('#route2Delete').trigger('chosen:updated');
+ $("#route2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#route2Delete').on('change', function(evt,params) {
if(!params) return;
let route = params.selected;
@@ -455,6 +457,7 @@ function populateLists() {
$('#trip2Delete').html(content);
$('#trip2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Trip'});
$('#trip2Delete').trigger('chosen:updated');
+ $("#trip2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#trip2Delete').on('change', function(evt,params) {
if(!params) return;
let trip = params.selected;
@@ -473,6 +476,7 @@ function populateLists() {
$('#shape2Delete').html(content);
$('#shape2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Shape'});
$('#shape2Delete').trigger('chosen:updated');
+ $("#shape2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#shape2Delete').on('change', function(evt,params) {
if(!params) return;
let shape = params.selected;
@@ -491,6 +495,7 @@ function populateLists() {
$('#service2Delete').html(content);
$('#service2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Calendar Service'});
$('#service2Delete').trigger('chosen:updated');
+ $("#service2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#service2Delete').on('change', function(evt,params) {
if(!params) return;
let service = params.selected;
@@ -510,6 +515,7 @@ function populateLists() {
$('#zone2Delete').html(content);
$('#zone2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Fare Zone'});
$('#zone2Delete').trigger('chosen:updated');
+ $("#zone2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#zone2Delete').on('change', function(evt,params) {
if(!params) return;
let zone = params.selected;
@@ -529,6 +535,7 @@ function populateLists() {
$('#fareID2Delete').html(content);
$('#fareID2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick a Fare ID'});
$('#fareID2Delete').trigger('chosen:updated');
+ $("#fareID2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#fareID2Delete').on('change', function(evt,params) {
if(!params) return;
let fare = params.selected;
@@ -548,6 +555,7 @@ function populateLists() {
$('#agency2Delete').html(content);
$('#agency2Delete').chosen({search_contains:true, allow_single_deselect:true, width:300, placeholder_text_single:'Pick an Agency ID'});
$('#agency2Delete').trigger('chosen:updated');
+ $("#agency2Delete").off("change"); // 10.5.19 fix for multiple triggers
$('#agency2Delete').on('change', function(evt,params) {
if(!params) return;
let agency = params.selected;
@@ -846,4 +854,4 @@ function getPythonTranslations() {
};
xhr.send();
}
-*/
\ No newline at end of file
+*/
diff --git a/js/homepage.js b/js/homepage.js
index 3119e4f..16ad879 100644
--- a/js/homepage.js
+++ b/js/homepage.js
@@ -61,7 +61,7 @@ function getPythonPastCommits() {
var data = JSON.parse(xhr.responseText);
var content = '
';
for (i in data.commits) {
- content += '- ' + data.commits[i] + ' : Download gtfs.zip
';
+ content += '- ' + data.commits[i] + ' : Download gtfs.zip
';
}
content += '
';
@@ -77,7 +77,7 @@ function getPythonPastCommits() {
function exportGTFS() {
// lowercase and zap everything that is not a-z, 0-9, - or _ from https://stackoverflow.com/a/4460306/4355695
- var commit = $("#commitName").val().toLowerCase().replace(/[^a-z0-9-_]/g, "");
+ var commit = $("#commitName").val().toLowerCase().replace(/[^a-z0-9-_.]/g, "");
$("#commitName").val(commit); // showing the corrected name to user.
@@ -181,4 +181,4 @@ function gtfsBlankSlate() {
$("#gtfsBlankSlateStatus").html('' + jqXHR.responseText + '');
}
});
-}
\ No newline at end of file
+}
diff --git a/js/sequence.js b/js/sequence.js
index bb4df01..d649098 100644
--- a/js/sequence.js
+++ b/js/sequence.js
@@ -76,17 +76,11 @@ $("#sequence-1-table").tabulator({
// #################################
/* Initiate map */
-var osmLink = 'OpenStreetMap';
-var MBAttrib = '© ' + osmLink + ' Contributors & Mapbox';
-var mapboxUrl = 'https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png';
-var scenicUrl = 'https://api.mapbox.com/styles/v1/nikhilsheth/cj8rdd7wu45nl2sps9teusbbr/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoibmlraGlsc2hldGgiLCJhIjoiQTREVlJuOCJ9.YpMpVVbkxOFZW-bEq1_LIw' ;
+var carto1 = L.tileLayer.provider('CartoDB.Positron');
+var carto2 = L.tileLayer.provider('CartoDB.Positron');
-var MBdark = L.tileLayer(mapboxUrl, {id: 'nikhilsheth.jme9hi44', attribution: MBAttrib });
-var scenic0 = L.tileLayer(scenicUrl, { attribution: MBAttrib });
-var scenic1 = L.tileLayer(scenicUrl, { attribution: MBAttrib });
-
-var mapOptions0 = { 'center': [0,0], 'zoom': 2, 'layers': scenic0, scrollWheelZoom: false };
-var mapOptions1 = { 'center': [0,0], 'zoom': 2, 'layers': scenic1, scrollWheelZoom: false };
+var mapOptions0 = { 'center': [0,0], 'zoom': 2, 'layers': carto1, scrollWheelZoom: true };
+var mapOptions1 = { 'center': [0,0], 'zoom': 2, 'layers': carto2, scrollWheelZoom: true };
//var mapOptionsClone = jQuery.extend(true, {}, mapOptions);
var map = [];
@@ -937,4 +931,4 @@ function saveRoutes() {
xhr.send(JSON.stringify(data)); // this is where POST differs from GET : we can send a payload instead of just url arguments.
}
-*/
\ No newline at end of file
+*/
diff --git a/js/stops.js b/js/stops.js
index d88df4b..19e5ddd 100644
--- a/js/stops.js
+++ b/js/stops.js
@@ -114,19 +114,11 @@ $("#stops-table").tabulator({
// #################################
/* 3. Initiate map */
-var osmLink = 'OpenStreetMap';
-var MBAttrib = '© ' + osmLink + ' Contributors & Mapbox';
-var mapboxUrl = 'https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png';
-var scenicUrl = 'https://api.mapbox.com/styles/v1/nikhilsheth/cj8rdd7wu45nl2sps9teusbbr/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoibmlraGlsc2hldGgiLCJhIjoiQTREVlJuOCJ9.YpMpVVbkxOFZW-bEq1_LIw' ;
-
-var MBstreets = L.tileLayer(mapboxUrl, {id: 'nikhilsheth.m0mlpl2d', attribution: MBAttrib, maxZoom: 20}),
- MBsatlabel = L.tileLayer(mapboxUrl, {id: 'nikhilsheth.m0mmaa87', attribution: MBAttrib, maxZoom: 20}),
- MBsat = L.tileLayer(mapboxUrl, {id: 'nikhilsheth.m0mni8e7', attribution: MBAttrib, maxZoom: 20}),
- MBlight = L.tileLayer(mapboxUrl, {id: 'nikhilsheth.m0mmobne', attribution: MBAttrib, maxZoom: 20}),
- MBdark = L.tileLayer(mapboxUrl, {id: 'nikhilsheth.jme9hi44', attribution: MBAttrib, maxZoom: 20}),
- OsmIndia = L.tileLayer(mapboxUrl, {id: 'openstreetmap.1b68f018', attribution: MBAttrib, maxZoom: 20}),
- GithubLight = L.tileLayer('https://{s}.tiles.mapbox.com/v3/github.map-xgq2svrz/{z}/{x}/{y}.png', {attribution: MBAttrib, maxZoom: 20}),
- scenic = L.tileLayer(scenicUrl, {attribution: MBAttrib, maxZoom: 20}) ;
+var cartoLight = L.tileLayer.provider('CartoDB.Positron');
+var cartoDark = L.tileLayer.provider('CartoDB.DarkMatter');
+var esriSat = L.tileLayer.provider('Esri.WorldImagery');
+var OSM = L.tileLayer.provider('OpenStreetMap.Mapnik');
+
var gStreets = L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
maxZoom: 20,
subdomains:['mt0','mt1','mt2','mt3']
@@ -141,12 +133,19 @@ var gSat = L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',{
subdomains:['mt0','mt1','mt2','mt3']
});
-const startLocation = [10.030259357021862, 76.31446838378908];
+var baseLayers = {
+ "CartoDB Positron" : cartoLight,
+ "CartoDB DarkMatter" : cartoDark,
+ "ESRI Sat" : esriSat,
+ "OpenStreetMap" : OSM,
+ "gStreets": gStreets,
+ "gHybrid": gHybrid
+};
var map = new L.Map('map', {
center: [0,0],
zoom: 2,
- layers: [MBlight],
+ layers: [cartoLight],
scrollWheelZoom: true
});
@@ -163,17 +162,7 @@ var stopsLayer = new L.geoJson(null)
})
.on('click',markerOnClick);
-var baseLayers = {
- "Scenic" : scenic,
- "OpenStreetMap.IN": OsmIndia,
- "Streets": MBstreets,
- "Satellite": MBsatlabel ,
- "Light": MBlight,
- "Dark" : MBdark,
- "gStreets": gStreets,
- "gHybrid": gHybrid,
- "gSat": gSat
-};
+
var overlays = {
'stops': stopsLayer,
@@ -205,7 +194,7 @@ var dragmarkerOptions = {
};
var clickedflag = 0;
-var dragmarker = L.circleMarker(startLocation, dragmarkerOptions);
+var dragmarker = L.circleMarker(null, dragmarkerOptions);
/* we're not dragging anymore!
dragmarker.on('dragend', function(e) {
updateLatLng();
@@ -474,9 +463,9 @@ function updateLatLng(latlong, revflag) {
dragmarker.setLatLng([lat, lng]);
map.panTo([lat, lng]);
} else {
- lat = Math.round(( dragmarker.getLatLng().lat + 0.0000001) * 10000) / 10000;
+ lat = Math.round(( dragmarker.getLatLng().lat + 0.0000001) * 100000) / 100000;
// Rounding, from https://stackoverflow.com/questions/11832914/round-to-at-most-2-decimal-places-only-if-necessary. The +0.000.. is to trip forward a number hinging on x.9999999...
- lng = Math.round(( dragmarker.getLatLng().lng + 0.0000001) * 10000) / 10000;
+ lng = Math.round(( dragmarker.getLatLng().lng + 0.0000001) * 100000) / 100000;
document.getElementById('newlatlng').value = lat + ',' + lng;
//document.getElementById('longitude').value = marker.getLatLng().lng;
//map.panTo(dragmarker.getLatLng());
@@ -618,11 +607,12 @@ function databank() {
console.log(databankCounter,'locations found in databank.');
// removing all and adding consecutively so stops are always on top
- map.removeLayer(stopsLayer);
+ // map.removeLayer(stopsLayer);
if(! map.hasLayer(databankLayer)) databankLayer.addTo(map);
- map.addLayer(stopsLayer);
+ // map.addLayer(stopsLayer);
+ stopsLayer.bringToFront();
map.flyToBounds(databankLayer.getBounds(), {padding:[10,10], maxZoom:14});
}
});
-}
\ No newline at end of file
+}
diff --git a/lib/errorpage.txt b/lib/errorpage.txt
new file mode 100644
index 0000000..da48ee7
--- /dev/null
+++ b/lib/errorpage.txt
@@ -0,0 +1 @@
+YOU SHALL NOT PASS !!
\ No newline at end of file
diff --git a/lib/favicon.ico b/lib/favicon.ico
new file mode 100644
index 0000000..d58a8bb
Binary files /dev/null and b/lib/favicon.ico differ
diff --git a/lib/leaflet-providers.js b/lib/leaflet-providers.js
new file mode 100644
index 0000000..ab55e61
--- /dev/null
+++ b/lib/leaflet-providers.js
@@ -0,0 +1,781 @@
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['leaflet'], factory);
+ } else if (typeof modules === 'object' && module.exports) {
+ // define a Common JS module that relies on 'leaflet'
+ module.exports = factory(require('leaflet'));
+ } else {
+ // Assume Leaflet is loaded into global object L already
+ factory(L);
+ }
+}(this, function (L) {
+ 'use strict';
+
+ L.TileLayer.Provider = L.TileLayer.extend({
+ initialize: function (arg, options) {
+ var providers = L.TileLayer.Provider.providers;
+
+ var parts = arg.split('.');
+
+ var providerName = parts[0];
+ var variantName = parts[1];
+
+ if (!providers[providerName]) {
+ throw 'No such provider (' + providerName + ')';
+ }
+
+ var provider = {
+ url: providers[providerName].url,
+ options: providers[providerName].options
+ };
+
+ // overwrite values in provider from variant.
+ if (variantName && 'variants' in providers[providerName]) {
+ if (!(variantName in providers[providerName].variants)) {
+ throw 'No such variant of ' + providerName + ' (' + variantName + ')';
+ }
+ var variant = providers[providerName].variants[variantName];
+ var variantOptions;
+ if (typeof variant === 'string') {
+ variantOptions = {
+ variant: variant
+ };
+ } else {
+ variantOptions = variant.options;
+ }
+ provider = {
+ url: variant.url || provider.url,
+ options: L.Util.extend({}, provider.options, variantOptions)
+ };
+ }
+
+ // replace attribution placeholders with their values from toplevel provider attribution,
+ // recursively
+ var attributionReplacer = function (attr) {
+ if (attr.indexOf('{attribution.') === -1) {
+ return attr;
+ }
+ return attr.replace(/\{attribution.(\w*)\}/,
+ function (match, attributionName) {
+ return attributionReplacer(providers[attributionName].options.attribution);
+ }
+ );
+ };
+ provider.options.attribution = attributionReplacer(provider.options.attribution);
+
+ // Compute final options combining provider options with any user overrides
+ var layerOpts = L.Util.extend({}, provider.options, options);
+ L.TileLayer.prototype.initialize.call(this, provider.url, layerOpts);
+ }
+ });
+
+ /**
+ * Definition of providers.
+ * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
+ */
+
+ L.TileLayer.Provider.providers = {
+ OpenStreetMap: {
+ url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution:
+ '© OpenStreetMap contributors'
+ },
+ variants: {
+ Mapnik: {},
+ DE: {
+ url: 'https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18
+ }
+ },
+ CH: {
+ url: 'https://tile.osm.ch/switzerland/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18,
+ bounds: [[45, 5], [48, 11]]
+ }
+ },
+ France: {
+ url: 'https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 20,
+ attribution: '© Openstreetmap France | {attribution.OpenStreetMap}'
+ }
+ },
+ HOT: {
+ url: 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
+ options: {
+ attribution:
+ '{attribution.OpenStreetMap}, ' +
+ 'Tiles style by Humanitarian OpenStreetMap Team ' +
+ 'hosted by OpenStreetMap France'
+ }
+ },
+ BZH: {
+ url: 'https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png',
+ options: {
+ attribution: '{attribution.OpenStreetMap}, Tiles courtesy of Breton OpenStreetMap Team',
+ bounds: [[46.2, -5.5], [50, 0.7]]
+ }
+ }
+ }
+ },
+ OpenSeaMap: {
+ url: 'https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png',
+ options: {
+ attribution: 'Map data: © OpenSeaMap contributors'
+ }
+ },
+ OpenPtMap: {
+ url: 'http://openptmap.org/tiles/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 17,
+ attribution: 'Map data: © OpenPtMap contributors'
+ }
+ },
+ OpenTopoMap: {
+ url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 17,
+ attribution: 'Map data: {attribution.OpenStreetMap}, SRTM | Map style: © OpenTopoMap (CC-BY-SA)'
+ }
+ },
+ OpenRailwayMap: {
+ url: 'https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution: 'Map data: {attribution.OpenStreetMap} | Map style: © OpenRailwayMap (CC-BY-SA)'
+ }
+ },
+ OpenFireMap: {
+ url: 'http://openfiremap.org/hytiles/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution: 'Map data: {attribution.OpenStreetMap} | Map style: © OpenFireMap (CC-BY-SA)'
+ }
+ },
+ SafeCast: {
+ url: 'https://s3.amazonaws.com/te512.safecast.org/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 16,
+ attribution: 'Map data: {attribution.OpenStreetMap} | Map style: © SafeCast (CC-BY-SA)'
+ }
+ },
+ Thunderforest: {
+ url: 'https://{s}.tile.thunderforest.com/{variant}/{z}/{x}/{y}.png?apikey={apikey}',
+ options: {
+ attribution:
+ '© Thunderforest, {attribution.OpenStreetMap}',
+ variant: 'cycle',
+ apikey: '',
+ maxZoom: 22
+ },
+ variants: {
+ OpenCycleMap: 'cycle',
+ Transport: {
+ options: {
+ variant: 'transport'
+ }
+ },
+ TransportDark: {
+ options: {
+ variant: 'transport-dark'
+ }
+ },
+ SpinalMap: {
+ options: {
+ variant: 'spinal-map'
+ }
+ },
+ Landscape: 'landscape',
+ Outdoors: 'outdoors',
+ Pioneer: 'pioneer'
+ }
+ },
+ OpenMapSurfer: {
+ url: 'https://maps.heigit.org/openmapsurfer/tiles/{variant}/webmercator/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ variant: 'roads',
+ attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data {attribution.OpenStreetMap}'
+ },
+ variants: {
+ Roads: 'roads',
+ AdminBounds: {
+ options: {
+ variant: 'adminb',
+ maxZoom: 18
+ }
+ }
+ }
+ },
+ Hydda: {
+ url: 'https://{s}.tile.openstreetmap.se/hydda/{variant}/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18,
+ variant: 'full',
+ attribution: 'Tiles courtesy of OpenStreetMap Sweden — Map data {attribution.OpenStreetMap}'
+ },
+ variants: {
+ Full: 'full',
+ Base: 'base',
+ RoadsAndLabels: 'roads_and_labels'
+ }
+ },
+ MapBox: {
+ url: 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}{r}.png?access_token={accessToken}',
+ options: {
+ attribution:
+ '© Mapbox ' +
+ '{attribution.OpenStreetMap} ' +
+ 'Improve this map',
+ subdomains: 'abcd',
+ id: 'mapbox.streets',
+ accessToken: '',
+ }
+ },
+ Stamen: {
+ url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}{r}.{ext}',
+ options: {
+ attribution:
+ 'Map tiles by Stamen Design, ' +
+ 'CC BY 3.0 — ' +
+ 'Map data {attribution.OpenStreetMap}',
+ subdomains: 'abcd',
+ minZoom: 0,
+ maxZoom: 20,
+ variant: 'toner',
+ ext: 'png'
+ },
+ variants: {
+ Toner: 'toner',
+ TonerBackground: 'toner-background',
+ TonerHybrid: 'toner-hybrid',
+ TonerLines: 'toner-lines',
+ TonerLabels: 'toner-labels',
+ TonerLite: 'toner-lite',
+ Watercolor: {
+ url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}.{ext}',
+ options: {
+ variant: 'watercolor',
+ ext: 'jpg',
+ minZoom: 1,
+ maxZoom: 16
+ }
+ },
+ Terrain: {
+ options: {
+ variant: 'terrain',
+ minZoom: 0,
+ maxZoom: 18
+ }
+ },
+ TerrainBackground: {
+ options: {
+ variant: 'terrain-background',
+ minZoom: 0,
+ maxZoom: 18
+ }
+ },
+ TopOSMRelief: {
+ url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}.{ext}',
+ options: {
+ variant: 'toposm-color-relief',
+ ext: 'jpg',
+ bounds: [[22, -132], [51, -56]]
+ }
+ },
+ TopOSMFeatures: {
+ options: {
+ variant: 'toposm-features',
+ bounds: [[22, -132], [51, -56]],
+ opacity: 0.9
+ }
+ }
+ }
+ },
+ Esri: {
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}',
+ options: {
+ variant: 'World_Street_Map',
+ attribution: 'Tiles © Esri'
+ },
+ variants: {
+ WorldStreetMap: {
+ options: {
+ attribution:
+ '{attribution.Esri} — ' +
+ 'Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
+ }
+ },
+ DeLorme: {
+ options: {
+ variant: 'Specialty/DeLorme_World_Base_Map',
+ minZoom: 1,
+ maxZoom: 11,
+ attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme'
+ }
+ },
+ WorldTopoMap: {
+ options: {
+ variant: 'World_Topo_Map',
+ attribution:
+ '{attribution.Esri} — ' +
+ 'Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
+ }
+ },
+ WorldImagery: {
+ options: {
+ variant: 'World_Imagery',
+ attribution:
+ '{attribution.Esri} — ' +
+ 'Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
+ }
+ },
+ WorldTerrain: {
+ options: {
+ variant: 'World_Terrain_Base',
+ maxZoom: 13,
+ attribution:
+ '{attribution.Esri} — ' +
+ 'Source: USGS, Esri, TANA, DeLorme, and NPS'
+ }
+ },
+ WorldShadedRelief: {
+ options: {
+ variant: 'World_Shaded_Relief',
+ maxZoom: 13,
+ attribution: '{attribution.Esri} — Source: Esri'
+ }
+ },
+ WorldPhysical: {
+ options: {
+ variant: 'World_Physical_Map',
+ maxZoom: 8,
+ attribution: '{attribution.Esri} — Source: US National Park Service'
+ }
+ },
+ OceanBasemap: {
+ options: {
+ variant: 'Ocean_Basemap',
+ maxZoom: 13,
+ attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
+ }
+ },
+ NatGeoWorldMap: {
+ options: {
+ variant: 'NatGeo_World_Map',
+ maxZoom: 16,
+ attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
+ }
+ },
+ WorldGrayCanvas: {
+ options: {
+ variant: 'Canvas/World_Light_Gray_Base',
+ maxZoom: 16,
+ attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ'
+ }
+ }
+ }
+ },
+ OpenWeatherMap: {
+ url: 'http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png?appid={apiKey}',
+ options: {
+ maxZoom: 19,
+ attribution: 'Map data © OpenWeatherMap',
+ apiKey:'',
+ opacity: 0.5
+ },
+ variants: {
+ Clouds: 'clouds',
+ CloudsClassic: 'clouds_cls',
+ Precipitation: 'precipitation',
+ PrecipitationClassic: 'precipitation_cls',
+ Rain: 'rain',
+ RainClassic: 'rain_cls',
+ Pressure: 'pressure',
+ PressureContour: 'pressure_cntr',
+ Wind: 'wind',
+ Temperature: 'temp',
+ Snow: 'snow'
+ }
+ },
+ HERE: {
+ /*
+ * HERE maps, formerly Nokia maps.
+ * These basemaps are free, but you need an API key. Please sign up at
+ * https://developer.here.com/plans
+ */
+ url:
+ 'https://{s}.{base}.maps.api.here.com/maptile/2.1/' +
+ '{type}/{mapID}/{variant}/{z}/{x}/{y}/{size}/{format}?' +
+ 'app_id={app_id}&app_code={app_code}&lg={language}',
+ options: {
+ attribution:
+ 'Map © 1987-' + new Date().getFullYear() + ' HERE',
+ subdomains: '1234',
+ mapID: 'newest',
+ 'app_id': '',
+ 'app_code': '',
+ base: 'base',
+ variant: 'normal.day',
+ maxZoom: 20,
+ type: 'maptile',
+ language: 'eng',
+ format: 'png8',
+ size: '256'
+ },
+ variants: {
+ normalDay: 'normal.day',
+ normalDayCustom: 'normal.day.custom',
+ normalDayGrey: 'normal.day.grey',
+ normalDayMobile: 'normal.day.mobile',
+ normalDayGreyMobile: 'normal.day.grey.mobile',
+ normalDayTransit: 'normal.day.transit',
+ normalDayTransitMobile: 'normal.day.transit.mobile',
+ normalNight: 'normal.night',
+ normalNightMobile: 'normal.night.mobile',
+ normalNightGrey: 'normal.night.grey',
+ normalNightGreyMobile: 'normal.night.grey.mobile',
+ normalNightTransit: 'normal.night.transit',
+ normalNightTransitMobile: 'normal.night.transit.mobile',
+ reducedDay: 'reduced.day',
+ reducedNight: 'reduced.night',
+ basicMap: {
+ options: {
+ type: 'basetile'
+ }
+ },
+ mapLabels: {
+ options: {
+ type: 'labeltile',
+ format: 'png'
+ }
+ },
+ trafficFlow: {
+ options: {
+ base: 'traffic',
+ type: 'flowtile'
+ }
+ },
+ carnavDayGrey: 'carnav.day.grey',
+ hybridDay: {
+ options: {
+ base: 'aerial',
+ variant: 'hybrid.day'
+ }
+ },
+ hybridDayMobile: {
+ options: {
+ base: 'aerial',
+ variant: 'hybrid.day.mobile'
+ }
+ },
+ hybridDayTransit: {
+ options: {
+ base: 'aerial',
+ variant: 'hybrid.day.transit'
+ }
+ },
+ hybridDayGrey: {
+ options: {
+ base: 'aerial',
+ variant: 'hybrid.grey.day'
+ }
+ },
+ pedestrianDay: 'pedestrian.day',
+ pedestrianNight: 'pedestrian.night',
+ satelliteDay: {
+ options: {
+ base: 'aerial',
+ variant: 'satellite.day'
+ }
+ },
+ terrainDay: {
+ options: {
+ base: 'aerial',
+ variant: 'terrain.day'
+ }
+ },
+ terrainDayMobile: {
+ options: {
+ base: 'aerial',
+ variant: 'terrain.day.mobile'
+ }
+ }
+ }
+ },
+ FreeMapSK: {
+ url: 'http://t{s}.freemap.sk/T/{z}/{x}/{y}.jpeg',
+ options: {
+ minZoom: 8,
+ maxZoom: 16,
+ subdomains: '1234',
+ bounds: [[47.204642, 15.996093], [49.830896, 22.576904]],
+ attribution:
+ '{attribution.OpenStreetMap}, vizualization CC-By-SA 2.0 Freemap.sk'
+ }
+ },
+ MtbMap: {
+ url: 'http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png',
+ options: {
+ attribution:
+ '{attribution.OpenStreetMap} & USGS'
+ }
+ },
+ CartoDB: {
+ url: 'https://{s}.basemaps.cartocdn.com/{variant}/{z}/{x}/{y}{r}.png',
+ options: {
+ attribution: '{attribution.OpenStreetMap} © CARTO',
+ subdomains: 'abcd',
+ maxZoom: 19,
+ variant: 'light_all'
+ },
+ variants: {
+ Positron: 'light_all',
+ PositronNoLabels: 'light_nolabels',
+ PositronOnlyLabels: 'light_only_labels',
+ DarkMatter: 'dark_all',
+ DarkMatterNoLabels: 'dark_nolabels',
+ DarkMatterOnlyLabels: 'dark_only_labels',
+ Voyager: 'rastertiles/voyager',
+ VoyagerNoLabels: 'rastertiles/voyager_nolabels',
+ VoyagerOnlyLabels: 'rastertiles/voyager_only_labels',
+ VoyagerLabelsUnder: 'rastertiles/voyager_labels_under'
+ }
+ },
+ HikeBike: {
+ url: 'https://tiles.wmflabs.org/{variant}/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution: '{attribution.OpenStreetMap}',
+ variant: 'hikebike'
+ },
+ variants: {
+ HikeBike: {},
+ HillShading: {
+ options: {
+ maxZoom: 15,
+ variant: 'hillshading'
+ }
+ }
+ }
+ },
+ BasemapAT: {
+ url: 'https://maps{s}.wien.gv.at/basemap/{variant}/normal/google3857/{z}/{y}/{x}.{format}',
+ options: {
+ maxZoom: 19,
+ attribution: 'Datenquelle: basemap.at',
+ subdomains: ['', '1', '2', '3', '4'],
+ format: 'png',
+ bounds: [[46.358770, 8.782379], [49.037872, 17.189532]],
+ variant: 'geolandbasemap'
+ },
+ variants: {
+ basemap: {
+ options: {
+ maxZoom: 20, // currently only in Vienna
+ variant: 'geolandbasemap'
+ }
+ },
+ grau: 'bmapgrau',
+ overlay: 'bmapoverlay',
+ highdpi: {
+ options: {
+ variant: 'bmaphidpi',
+ format: 'jpeg'
+ }
+ },
+ orthofoto: {
+ options: {
+ maxZoom: 20, // currently only in Vienna
+ variant: 'bmaporthofoto30cm',
+ format: 'jpeg'
+ }
+ }
+ }
+ },
+ nlmaps: {
+ url: 'https://geodata.nationaalgeoregister.nl/tiles/service/wmts/{variant}/EPSG:3857/{z}/{x}/{y}.png',
+ options: {
+ minZoom: 6,
+ maxZoom: 19,
+ bounds: [[50.5, 3.25], [54, 7.6]],
+ attribution: 'Kaartgegevens © Kadaster'
+ },
+ variants: {
+ 'standaard': 'brtachtergrondkaart',
+ 'pastel': 'brtachtergrondkaartpastel',
+ 'grijs': 'brtachtergrondkaartgrijs',
+ 'luchtfoto': {
+ 'url': 'https://geodata.nationaalgeoregister.nl/luchtfoto/rgb/wmts/1.0.0/2016_ortho25/EPSG:3857/{z}/{x}/{y}.png',
+ }
+ }
+ },
+ NASAGIBS: {
+ url: 'https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{maxZoom}/{z}/{y}/{x}.{format}',
+ options: {
+ attribution:
+ 'Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System ' +
+ '(ESDIS) with funding provided by NASA/HQ.',
+ bounds: [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]],
+ minZoom: 1,
+ maxZoom: 9,
+ format: 'jpg',
+ time: '',
+ tilematrixset: 'GoogleMapsCompatible_Level'
+ },
+ variants: {
+ ModisTerraTrueColorCR: 'MODIS_Terra_CorrectedReflectance_TrueColor',
+ ModisTerraBands367CR: 'MODIS_Terra_CorrectedReflectance_Bands367',
+ ViirsEarthAtNight2012: {
+ options: {
+ variant: 'VIIRS_CityLights_2012',
+ maxZoom: 8
+ }
+ },
+ ModisTerraLSTDay: {
+ options: {
+ variant: 'MODIS_Terra_Land_Surface_Temp_Day',
+ format: 'png',
+ maxZoom: 7,
+ opacity: 0.75
+ }
+ },
+ ModisTerraSnowCover: {
+ options: {
+ variant: 'MODIS_Terra_Snow_Cover',
+ format: 'png',
+ maxZoom: 8,
+ opacity: 0.75
+ }
+ },
+ ModisTerraAOD: {
+ options: {
+ variant: 'MODIS_Terra_Aerosol',
+ format: 'png',
+ maxZoom: 6,
+ opacity: 0.75
+ }
+ },
+ ModisTerraChlorophyll: {
+ options: {
+ variant: 'MODIS_Terra_Chlorophyll_A',
+ format: 'png',
+ maxZoom: 7,
+ opacity: 0.75
+ }
+ }
+ }
+ },
+ NLS: {
+ // NLS maps are copyright National library of Scotland.
+ // http://maps.nls.uk/projects/api/index.html
+ // Please contact NLS for anything other than non-commercial low volume usage
+ //
+ // Map sources: Ordnance Survey 1:1m to 1:63K, 1920s-1940s
+ // z0-9 - 1:1m
+ // z10-11 - quarter inch (1:253440)
+ // z12-18 - one inch (1:63360)
+ url: 'https://nls-{s}.tileserver.com/nls/{z}/{x}/{y}.jpg',
+ options: {
+ attribution: 'National Library of Scotland Historic Maps',
+ bounds: [[49.6, -12], [61.7, 3]],
+ minZoom: 1,
+ maxZoom: 18,
+ subdomains: '0123',
+ }
+ },
+ JusticeMap: {
+ // Justice Map (http://www.justicemap.org/)
+ // Visualize race and income data for your community, county and country.
+ // Includes tools for data journalists, bloggers and community activists.
+ url: 'http://www.justicemap.org/tile/{size}/{variant}/{z}/{x}/{y}.png',
+ options: {
+ attribution: 'Justice Map',
+ // one of 'county', 'tract', 'block'
+ size: 'county',
+ // Bounds for USA, including Alaska and Hawaii
+ bounds: [[14, -180], [72, -56]]
+ },
+ variants: {
+ income: 'income',
+ americanIndian: 'indian',
+ asian: 'asian',
+ black: 'black',
+ hispanic: 'hispanic',
+ multi: 'multi',
+ nonWhite: 'nonwhite',
+ white: 'white',
+ plurality: 'plural'
+ }
+ },
+ Wikimedia: {
+ url: 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}{r}.png',
+ options: {
+ attribution: 'Wikimedia',
+ minZoom: 1,
+ maxZoom: 19
+ }
+ },
+ GeoportailFrance: {
+ url: 'https://wxs.ign.fr/{apikey}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER={variant}&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}',
+ options: {
+ attribution: 'Geoportail France',
+ bounds: [[-75, -180], [81, 180]],
+ minZoom: 2,
+ maxZoom: 18,
+ // Get your own geoportail apikey here : http://professionnels.ign.fr/ign/contrats/
+ // NB : 'choisirgeoportail' is a demonstration key that comes with no guarantee
+ apikey: 'choisirgeoportail',
+ format: 'image/jpeg',
+ style : 'normal',
+ variant: 'GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-EXPRESS.STANDARD'
+ },
+ variants: {
+ parcels: {
+ options : {
+ variant: 'CADASTRALPARCELS.PARCELS',
+ maxZoom: 20,
+ style : 'bdparcellaire',
+ format: 'image/png'
+ }
+ },
+ ignMaps: 'GEOGRAPHICALGRIDSYSTEMS.MAPS',
+ maps: 'GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-EXPRESS.STANDARD',
+ orthos: {
+ options: {
+ maxZoom: 19,
+ variant: 'ORTHOIMAGERY.ORTHOPHOTOS'
+ }
+ }
+ }
+ },
+ OneMapSG: {
+ url: 'https://maps-{s}.onemap.sg/v3/{variant}/{z}/{x}/{y}.png',
+ options: {
+ variant: 'Default',
+ minZoom: 11,
+ maxZoom: 18,
+ bounds: [[1.56073, 104.11475], [1.16, 103.502]],
+ attribution: ' New OneMap | Map data © contributors, Singapore Land Authority'
+ },
+ variants: {
+ Default: 'Default',
+ Night: 'Night',
+ Original: 'Original',
+ Grey: 'Grey',
+ LandLot: 'LandLot'
+ }
+ }
+ };
+
+ L.tileLayer.provider = function (provider, options) {
+ return new L.TileLayer.Provider(provider, options);
+ };
+
+ return L;
+}));
diff --git a/linux_GTFSManager_executable b/linux_GTFSManager_executable
new file mode 100644
index 0000000..f739a79
Binary files /dev/null and b/linux_GTFSManager_executable differ
diff --git a/sequence.html b/sequence.html
index 6c50389..3954ede 100644
--- a/sequence.html
+++ b/sequence.html
@@ -25,6 +25,7 @@
+