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 += '
  1. ' + data.commits[i] + ' : Download gtfs.zip
  2. '; + content += '
  3. ' + data.commits[i] + ' : Download gtfs.zip
  4. '; } 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 @@ +