forked from MapComplete/MapComplete
		
	Add loading of live data
This commit is contained in:
		
							parent
							
								
									feab5a19b3
								
							
						
					
					
						commit
						0d6412f824
					
				
					 14 changed files with 291 additions and 35 deletions
				
			
		| 
						 | 
					@ -17,6 +17,7 @@ import * as bike_repair_station from "../../assets/layers/bike_repair_station/bi
 | 
				
			||||||
import * as birdhides from "../../assets/layers/bird_hide/birdhides.json"
 | 
					import * as birdhides from "../../assets/layers/bird_hide/birdhides.json"
 | 
				
			||||||
import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json"
 | 
					import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json"
 | 
				
			||||||
import * as bike_cafes from "../../assets/layers/bike_cafe/bike_cafes.json"
 | 
					import * as bike_cafes from "../../assets/layers/bike_cafe/bike_cafes.json"
 | 
				
			||||||
 | 
					import * as bike_monitoring_station from "../../assets/layers/bike_monitoring_station/bike_monitoring_station.json"
 | 
				
			||||||
import * as cycling_themed_objects from "../../assets/layers/cycling_themed_object/cycling_themed_objects.json"
 | 
					import * as cycling_themed_objects from "../../assets/layers/cycling_themed_object/cycling_themed_objects.json"
 | 
				
			||||||
import * as bike_shops from "../../assets/layers/bike_shop/bike_shop.json"
 | 
					import * as bike_shops from "../../assets/layers/bike_shop/bike_shop.json"
 | 
				
			||||||
import * as maps from "../../assets/layers/maps/maps.json"
 | 
					import * as maps from "../../assets/layers/maps/maps.json"
 | 
				
			||||||
| 
						 | 
					@ -43,6 +44,7 @@ export class FromJSON {
 | 
				
			||||||
            FromJSON.Layer(viewpoint),
 | 
					            FromJSON.Layer(viewpoint),
 | 
				
			||||||
            FromJSON.Layer(bike_parking),
 | 
					            FromJSON.Layer(bike_parking),
 | 
				
			||||||
            FromJSON.Layer(bike_repair_station),
 | 
					            FromJSON.Layer(bike_repair_station),
 | 
				
			||||||
 | 
					            FromJSON.Layer(bike_monitoring_station),
 | 
				
			||||||
            FromJSON.Layer(birdhides),
 | 
					            FromJSON.Layer(birdhides),
 | 
				
			||||||
            FromJSON.Layer(nature_reserve),
 | 
					            FromJSON.Layer(nature_reserve),
 | 
				
			||||||
            FromJSON.Layer(bike_cafes),
 | 
					            FromJSON.Layer(bike_cafes),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,7 +117,7 @@ export class FilteredLayer {
 | 
				
			||||||
                feature.properties["_lon"] = "" + lat; // We expect a string here for lat/lon
 | 
					                feature.properties["_lon"] = "" + lat; // We expect a string here for lat/lon
 | 
				
			||||||
                feature.properties["_lat"] = "" + lon;
 | 
					                feature.properties["_lat"] = "" + lon;
 | 
				
			||||||
                // But the codegrid SHOULD be a number!
 | 
					                // But the codegrid SHOULD be a number!
 | 
				
			||||||
                CodeGrid.grid.getCode(lat, lon, (error, code) => {
 | 
					                CodeGrid.getCode(lat, lon, (error, code) => {
 | 
				
			||||||
                    if (error === null) {
 | 
					                    if (error === null) {
 | 
				
			||||||
                        feature.properties["_country"] = code;
 | 
					                        feature.properties["_country"] = code;
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,14 @@ export class LayerUpdater {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.retries.setData(0);
 | 
					        self.retries.setData(0);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        let newIds = 1;
 | 
				
			||||||
 | 
					        for (const feature of geojson.features) {
 | 
				
			||||||
 | 
					            if(feature.properties.id === undefined){
 | 
				
			||||||
 | 
					                feature.properties.id = "ext/"+newIds;
 | 
				
			||||||
 | 
					                newIds++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        function renderLayers(layers: FilteredLayer[]) {
 | 
					        function renderLayers(layers: FilteredLayer[]) {
 | 
				
			||||||
            if (layers.length === 0) {
 | 
					            if (layers.length === 0) {
 | 
				
			||||||
                self.runningQuery.setData(false);
 | 
					                self.runningQuery.setData(false);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import {Bounds} from "../Bounds";
 | 
					import {Bounds} from "../Bounds";
 | 
				
			||||||
import {TagsFilter} from "../Tags";
 | 
					import {TagsFilter} from "../Tags";
 | 
				
			||||||
import $ from "jquery"
 | 
					import * as $ from "jquery"
 | 
				
			||||||
import * as OsmToGeoJson from "osmtogeojson";
 | 
					import * as OsmToGeoJson from "osmtogeojson";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,6 @@ export class Overpass {
 | 
				
			||||||
            console.log("Using testing URL")
 | 
					            console.log("Using testing URL")
 | 
				
			||||||
            query = Overpass.testUrl;
 | 
					            query = Overpass.testUrl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        $.getJSON(query,
 | 
					        $.getJSON(query,
 | 
				
			||||||
            function (json, status) {
 | 
					            function (json, status) {
 | 
				
			||||||
                if (status !== "success") {
 | 
					                if (status !== "success") {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,12 @@
 | 
				
			||||||
import codegrid from "codegrid-js";
 | 
					import codegrid from "codegrid-js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class CodeGrid {
 | 
					export default class CodeGrid {
 | 
				
			||||||
    public static readonly grid = CodeGrid.InitGrid();
 | 
					    private static readonly grid = CodeGrid.InitGrid();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static getCode(lat: any, lon: any, handle: (error, code) => void) {
 | 
				
			||||||
 | 
					        CodeGrid.grid.getCode(lat, lon, handle);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static InitGrid(): any {
 | 
					    private static InitGrid(): any {
 | 
				
			||||||
        const grid = codegrid.CodeGrid("./tiles/");
 | 
					        const grid = codegrid.CodeGrid("./tiles/");
 | 
				
			||||||
| 
						 | 
					@ -15,4 +20,6 @@ export default class CodeGrid {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        return grid;
 | 
					        return grid;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								Logic/Web/LiveQueryHandler.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								Logic/Web/LiveQueryHandler.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Fetches data from random data sources
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
 | 
					import * as $ from "jquery"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class LiveQueryHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static cache = {} // url --> UIEventSource<actual data>
 | 
				
			||||||
 | 
					    private static neededShorthands = {} // url -> (shorthand:paths)[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static FetchLiveData(url: string, shorthands: string[]): UIEventSource<any /* string -> string */> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const shorthandsSet: string[] = LiveQueryHandler.neededShorthands[url] ?? []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const shorthand of shorthands) {
 | 
				
			||||||
 | 
					            if (shorthandsSet.indexOf(shorthand) < 0) {
 | 
				
			||||||
 | 
					                shorthandsSet.push(shorthand);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LiveQueryHandler.neededShorthands[url] = shorthandsSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (LiveQueryHandler[url] === undefined) {
 | 
				
			||||||
 | 
					            const source = new UIEventSource({});
 | 
				
			||||||
 | 
					            LiveQueryHandler[url] = source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                console.log("Fetching live data from a third-party (unknown) API:",url)
 | 
				
			||||||
 | 
					            $.getJSON(url, function (data) {
 | 
				
			||||||
 | 
					                for (const shorthandDescription of shorthandsSet) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const descr = shorthandDescription.trim().split(":");
 | 
				
			||||||
 | 
					                    const shorthand = descr[0];
 | 
				
			||||||
 | 
					                    const path = descr[1];
 | 
				
			||||||
 | 
					                    const parts = path.split(".");
 | 
				
			||||||
 | 
					                    let trail = data;
 | 
				
			||||||
 | 
					                    for (const part of parts) {
 | 
				
			||||||
 | 
					                        if (trail !== undefined) {
 | 
				
			||||||
 | 
					                            trail = trail[part];
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    source.data[shorthand] = trail;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                source.ping();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return LiveQueryHandler[url];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -184,3 +184,6 @@ Park icon via http://www.onlinewebfonts.com/icon/425974, CC BY 3.0 (@sterankofra
 | 
				
			||||||
Forest icon via https://www.onlinewebfonts.com/icon/498112, CC BY
 | 
					Forest icon via https://www.onlinewebfonts.com/icon/498112, CC BY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Statistics icon via https://www.onlinewebfonts.com/icon/197818
 | 
					Statistics icon via https://www.onlinewebfonts.com/icon/197818
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Chronometer (on monitoring_station.svg): ANTU chronometer
 | 
				
			||||||
 | 
					https://commons.wikimedia.org/w/index.php?title=Antu_chronometer&action=edit&redlink=1
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
import {UIElement} from "./UIElement";
 | 
					import {UIElement} from "./UIElement";
 | 
				
			||||||
import OpeningHoursVisualization from "./OhVisualization";
 | 
					import OpeningHoursVisualization from "./OhVisualization";
 | 
				
			||||||
import {UIEventSource} from "../Logic/UIEventSource";
 | 
					import {UIEventSource} from "../Logic/UIEventSource";
 | 
				
			||||||
 | 
					import {VariableUiElement} from "./Base/VariableUIElement";
 | 
				
			||||||
 | 
					import LiveQueryHandler from "../Logic/Web/LiveQueryHandler";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SpecialVisualizations {
 | 
					export default class SpecialVisualizations {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +10,7 @@ export default class SpecialVisualizations {
 | 
				
			||||||
        funcName: string,
 | 
					        funcName: string,
 | 
				
			||||||
        constr: ((tagSource: UIEventSource<any>, argument: string[]) => UIElement),
 | 
					        constr: ((tagSource: UIEventSource<any>, argument: string[]) => UIElement),
 | 
				
			||||||
        docs: string,
 | 
					        docs: string,
 | 
				
			||||||
        args: {name: string, defaultValue: string, doc: string}[]
 | 
					        args: { name: string, defaultValue?: string, doc: string }[]
 | 
				
			||||||
    }[] =
 | 
					    }[] =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [{
 | 
					        [{
 | 
				
			||||||
| 
						 | 
					@ -24,6 +26,26 @@ export default class SpecialVisualizations {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                funcName: "live",
 | 
				
			||||||
 | 
					                docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}",
 | 
				
			||||||
 | 
					                args: [{
 | 
				
			||||||
 | 
					                    name: "Url", doc: "The URL to load"
 | 
				
			||||||
 | 
					                }, {
 | 
				
			||||||
 | 
					                    name: "Shorthands",
 | 
				
			||||||
 | 
					                    doc: "A list of shorthands, of the format 'shorthandname:path.path.path'. Seperated by ;"
 | 
				
			||||||
 | 
					                }, {
 | 
				
			||||||
 | 
					                    name: "path", doc: "The path (or shorthand) that should be returned"
 | 
				
			||||||
 | 
					                }],
 | 
				
			||||||
 | 
					                constr: (tagSource: UIEventSource<any>, args) => {
 | 
				
			||||||
 | 
					                    const url = args[0];
 | 
				
			||||||
 | 
					                    const shorthands = args[1];
 | 
				
			||||||
 | 
					                    const neededValue = args[2];
 | 
				
			||||||
 | 
					                    const source = LiveQueryHandler.FetchLiveData(url, shorthands.split(";"));
 | 
				
			||||||
 | 
					                    return new VariableUiElement(source.map(data => data[neededValue] ?? "Loading..."));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -47,10 +47,10 @@ export default class Translation extends UIElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const knownSpecial of knownSpecials) {
 | 
					            for (const knownSpecial of knownSpecials) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                do {
 | 
				
			||||||
                    const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*)\\)}(.*)`);
 | 
					                    const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*)\\)}(.*)`);
 | 
				
			||||||
                    if (matched === null) {
 | 
					                    if (matched === null) {
 | 
				
			||||||
                    continue;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    const partBefore = matched[1];
 | 
					                    const partBefore = matched[1];
 | 
				
			||||||
                    const argument = matched[2];
 | 
					                    const argument = matched[2];
 | 
				
			||||||
| 
						 | 
					@ -64,6 +64,10 @@ export default class Translation extends UIElement {
 | 
				
			||||||
                        console.error(e);
 | 
					                        console.error(e);
 | 
				
			||||||
                        template = partBefore + partAfter;
 | 
					                        template = partBefore + partAfter;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } while (true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            newTranslations[lang] = template;
 | 
					            newTranslations[lang] = template;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,10 +103,10 @@ export default class Translations {
 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            respectPrivacy: new T({
 | 
					            respectPrivacy: new T({
 | 
				
			||||||
                en: "Please respect privacy. Do not photograph people nor license plates.<br/>Respect copyright. Only upload images you made yourself. Do not upload Google Streetview Images - these will be removed.",
 | 
					                en: "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
 | 
				
			||||||
                ca: "Respecta la privacitat. No fotografiïs gent o matrícules",
 | 
					                ca: "Respecta la privacitat. No fotografiïs gent o matrícules",
 | 
				
			||||||
                es: "Respeta la privacidad. No fotografíes gente o matrículas",
 | 
					                es: "Respeta la privacidad. No fotografíes gente o matrículas",
 | 
				
			||||||
                nl: "Respecteer privacy. Fotografeer geen mensen of nummerplaten.<br/>Repecteer auteursrechten. Voeg enkel foto's toe die je zelf maakte. Screenshots van andere services (zoals Google Streetview) worden verwijderd",
 | 
					                nl: "Fotografeer geen mensen of nummerplaten. Voeg geen Google Maps, Google Streetview of foto's met auteursrechten toe.",
 | 
				
			||||||
                fr: "Merci de respecter la vie privée. Ne publiez pas les plaques d\'immatriculation",
 | 
					                fr: "Merci de respecter la vie privée. Ne publiez pas les plaques d\'immatriculation",
 | 
				
			||||||
                gl: "Respecta a privacidade. Non fotografes xente ou matrículas",
 | 
					                gl: "Respecta a privacidade. Non fotografes xente ou matrículas",
 | 
				
			||||||
                de: "Bitte respektieren Sie die Privatsphäre. Fotografieren Sie weder Personen noch Nummernschilder"
 | 
					                de: "Bitte respektieren Sie die Privatsphäre. Fotografieren Sie weder Personen noch Nummernschilder"
 | 
				
			||||||
| 
						 | 
					@ -969,6 +969,9 @@ export default class Translations {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static WT(s: string | Translation): Translation {
 | 
					    public static WT(s: string | Translation): Translation {
 | 
				
			||||||
 | 
					        if(s === undefined){
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (typeof (s) === "string") {
 | 
					        if (typeof (s) === "string") {
 | 
				
			||||||
            return new Translation({en: s});
 | 
					            return new Translation({en: s});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,58 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "id": "bike_monitoring_station",
 | 
				
			||||||
 | 
					  "name": {
 | 
				
			||||||
 | 
					    "en": "Monitoring stations"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "minzoom": 12,
 | 
				
			||||||
 | 
					  "overpassTags": {
 | 
				
			||||||
 | 
					    "and": [
 | 
				
			||||||
 | 
					      "man_made=monitoring_station",
 | 
				
			||||||
 | 
					      "monitoring:bicycle=yes"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "title": {
 | 
				
			||||||
 | 
					    "render": {
 | 
				
			||||||
 | 
					      "nl": "Fietstelstation",
 | 
				
			||||||
 | 
					      "en": "Bicycle counting station"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "mappings": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "if": "name~*",
 | 
				
			||||||
 | 
					        "then": {
 | 
				
			||||||
 | 
					          "en": "Bicycle counting station {name}",
 | 
				
			||||||
 | 
					          "nl": "Fietstelstation {name}"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "if": "ref~*",
 | 
				
			||||||
 | 
					        "then": {
 | 
				
			||||||
 | 
					          "en": "Bicycle counting station {ref}",
 | 
				
			||||||
 | 
					          "nl": "Fietstelstation {ref}"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "description": {},
 | 
				
			||||||
 | 
					  "tagRenderings": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "render": "<b>{live({url},{url:format},hour)}</b> cyclists last hour<br/><b>{live({url},{url:format},day)}</b> cyclists today<br/><b>{live({url},{url:format},year)}</b> cyclists this year<br/>",
 | 
				
			||||||
 | 
					      "condition": {
 | 
				
			||||||
 | 
					        "and": ["url~*","url:format~*"]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "hideUnderlayingFeaturesMinPercentage": 0,
 | 
				
			||||||
 | 
					  "icon": {
 | 
				
			||||||
 | 
					    "render": "./assets/layers/bike_monitoring_station/monitoring_station.svg"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "width": {
 | 
				
			||||||
 | 
					    "render": "8"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "iconSize": {
 | 
				
			||||||
 | 
					    "render": "40,40,center"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "color": {
 | 
				
			||||||
 | 
					    "render": "#00f"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "presets": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										96
									
								
								assets/layers/bike_monitoring_station/monitoring_station.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								assets/layers/bike_monitoring_station/monitoring_station.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,96 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
				
			||||||
 | 
					<svg
 | 
				
			||||||
 | 
					   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
				
			||||||
 | 
					   xmlns:cc="http://creativecommons.org/ns#"
 | 
				
			||||||
 | 
					   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
				
			||||||
 | 
					   xmlns:svg="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					   xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					   id="svg27"
 | 
				
			||||||
 | 
					   version="1.1"
 | 
				
			||||||
 | 
					   fill="none"
 | 
				
			||||||
 | 
					   viewBox="0 0 97 123"
 | 
				
			||||||
 | 
					   height="123"
 | 
				
			||||||
 | 
					   width="97">
 | 
				
			||||||
 | 
					  <metadata
 | 
				
			||||||
 | 
					     id="metadata31">
 | 
				
			||||||
 | 
					    <rdf:RDF>
 | 
				
			||||||
 | 
					      <cc:Work
 | 
				
			||||||
 | 
					         rdf:about="">
 | 
				
			||||||
 | 
					        <dc:format>image/svg+xml</dc:format>
 | 
				
			||||||
 | 
					        <dc:type
 | 
				
			||||||
 | 
					           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
				
			||||||
 | 
					        <dc:title></dc:title>
 | 
				
			||||||
 | 
					      </cc:Work>
 | 
				
			||||||
 | 
					    </rdf:RDF>
 | 
				
			||||||
 | 
					  </metadata>
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     style="fill:#ffd42a"
 | 
				
			||||||
 | 
					     id="path2"
 | 
				
			||||||
 | 
					     fill="#5675DF"
 | 
				
			||||||
 | 
					     d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" />
 | 
				
			||||||
 | 
					  <ellipse
 | 
				
			||||||
 | 
					     style="fill:#ffd42a"
 | 
				
			||||||
 | 
					     id="ellipse4"
 | 
				
			||||||
 | 
					     fill="#5675DF"
 | 
				
			||||||
 | 
					     ry="47.5"
 | 
				
			||||||
 | 
					     rx="48.5"
 | 
				
			||||||
 | 
					     cy="47.5"
 | 
				
			||||||
 | 
					     cx="48.5" />
 | 
				
			||||||
 | 
					  <defs
 | 
				
			||||||
 | 
					     id="defs25">
 | 
				
			||||||
 | 
					    <filter
 | 
				
			||||||
 | 
					       color-interpolation-filters="sRGB"
 | 
				
			||||||
 | 
					       filterUnits="userSpaceOnUse"
 | 
				
			||||||
 | 
					       height="53.5"
 | 
				
			||||||
 | 
					       width="40.7188"
 | 
				
			||||||
 | 
					       y="25.5"
 | 
				
			||||||
 | 
					       x="32.2812"
 | 
				
			||||||
 | 
					       id="filter0_d">
 | 
				
			||||||
 | 
					      <feFlood
 | 
				
			||||||
 | 
					         id="feFlood10"
 | 
				
			||||||
 | 
					         result="BackgroundImageFix"
 | 
				
			||||||
 | 
					         flood-opacity="0" />
 | 
				
			||||||
 | 
					      <feColorMatrix
 | 
				
			||||||
 | 
					         id="feColorMatrix12"
 | 
				
			||||||
 | 
					         values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
 | 
				
			||||||
 | 
					         type="matrix"
 | 
				
			||||||
 | 
					         in="SourceAlpha" />
 | 
				
			||||||
 | 
					      <feOffset
 | 
				
			||||||
 | 
					         id="feOffset14"
 | 
				
			||||||
 | 
					         dy="4" />
 | 
				
			||||||
 | 
					      <feGaussianBlur
 | 
				
			||||||
 | 
					         id="feGaussianBlur16"
 | 
				
			||||||
 | 
					         stdDeviation="2" />
 | 
				
			||||||
 | 
					      <feColorMatrix
 | 
				
			||||||
 | 
					         id="feColorMatrix18"
 | 
				
			||||||
 | 
					         values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
 | 
				
			||||||
 | 
					         type="matrix" />
 | 
				
			||||||
 | 
					      <feBlend
 | 
				
			||||||
 | 
					         id="feBlend20"
 | 
				
			||||||
 | 
					         result="effect1_dropShadow"
 | 
				
			||||||
 | 
					         in2="BackgroundImageFix"
 | 
				
			||||||
 | 
					         mode="normal" />
 | 
				
			||||||
 | 
					      <feBlend
 | 
				
			||||||
 | 
					         id="feBlend22"
 | 
				
			||||||
 | 
					         result="shape"
 | 
				
			||||||
 | 
					         in2="effect1_dropShadow"
 | 
				
			||||||
 | 
					         in="SourceGraphic"
 | 
				
			||||||
 | 
					         mode="normal" />
 | 
				
			||||||
 | 
					    </filter>
 | 
				
			||||||
 | 
					  </defs>
 | 
				
			||||||
 | 
					  <g
 | 
				
			||||||
 | 
					     style="fill:#ffffff"
 | 
				
			||||||
 | 
					     transform="matrix(0.1210062,0,0,0.1210062,13.583344,11.282657)"
 | 
				
			||||||
 | 
					     id="g844">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					       d="m 464.99,141.27 c 0,0 8.996,9.05 15.346,0.017 l 12.1,-17.349 c 4.225,-6.103 -1.231,-10.327 -1.796,-10.744 l -73.41,-51.29 h -0.017 c -4.52,-3.145 -7.521,-1.236 -9.1,0.533 l -1.311,1.891 -11.181,16.01 c -0.053,0.061 -6.315,9.937 3.695,15.16 v 0.013 c 14.295,7.03 40.526,21.868 65.66,45.814 l 0.018,-0.051"
 | 
				
			||||||
 | 
					       id="path838" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					       d="M 341.45,0 H 225.76 c -8.978,0 -16.334,7.213 -16.334,16.338 v 13.484 c 0,9.112 7.356,16.468 16.334,16.468 h 5.725 v 9.521 c 0.009,13.688 8.648,14.825 12.985,14.534 0.737,-0.109 1.501,-0.226 2.308,-0.33 0.009,0 0.122,-0.026 0.122,-0.026 h -0.009 c 15.397,-2.108 40.9,-4.125 70.15,-0.403 l 0.017,-0.026 c 0,0 18.737,3.474 18.686,-13.758 v -9.52 h 5.725 c 8.987,0 16.325,-7.348 16.325,-16.473 V 16.338 C 357.7939,7.221 350.437,0 341.45,0"
 | 
				
			||||||
 | 
					       id="path840" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					       d="m 11,5.563 c -3.71,0 -6.719,3.01 -6.719,6.719 0,3.71 3.01,6.719 6.719,6.719 3.71,0 6.719,-3.01 6.719,-6.719 0,-3.711 -3.01,-6.719 -6.719,-6.719 m 0.1,2.938 c 0.542,0 1.06,0.107 1.553,0.318 0.493,0.212 0.918,0.496 1.275,0.854 0.358,0.358 0.644,0.782 0.855,1.275 0.212,0.493 0.316,1.011 0.316,1.553 0,0.542 -0.105,1.06 -0.316,1.553 -0.212,0.493 -0.498,0.918 -0.855,1.275 -0.358,0.358 -0.782,0.642 -1.275,0.854 -0.493,0.212 -1.011,0.318 -1.553,0.318 -0.597,0 -1.165,-0.125 -1.703,-0.377 C 8.859,15.872 8.401,15.516 8.022,15.058 7.998,15.023 7.987,14.984 7.989,14.941 7.991,14.898 8.006,14.863 8.034,14.836 l 0.713,-0.719 c 0.035,-0.031 0.079,-0.047 0.131,-0.047 0.056,0.007 0.095,0.028 0.119,0.063 0.253,0.33 0.564,0.585 0.932,0.766 0.368,0.181 0.759,0.27 1.172,0.27 0.361,0 0.707,-0.07 1.035,-0.211 0.328,-0.141 0.612,-0.331 0.852,-0.57 0.24,-0.24 0.43,-0.523 0.57,-0.852 0.141,-0.328 0.211,-0.672 0.211,-1.033 0,-0.361 -0.07,-0.705 -0.211,-1.033 -0.141,-0.328 -0.331,-0.612 -0.57,-0.852 -0.24,-0.24 -0.523,-0.43 -0.852,-0.57 -0.328,-0.141 -0.674,-0.211 -1.035,-0.211 -0.34,0 -0.666,0.06 -0.979,0.184 -0.312,0.123 -0.591,0.3 -0.834,0.529 l 0.715,0.719 c 0.108,0.104 0.131,0.224 0.072,0.359 -0.059,0.139 -0.161,0.209 -0.307,0.209 H 7.434 c -0.09,0 -0.168,-0.034 -0.234,-0.1 C 7.134,11.671 7.1,11.593 7.1,11.503 V 9.169 C 7.1,9.023 7.17,8.921 7.309,8.862 7.444,8.803 7.564,8.827 7.668,8.934 L 8.346,9.606 C 8.718,9.255 9.142,8.984 9.619,8.792 c 0.477,-0.193 0.97,-0.289 1.48,-0.289"
 | 
				
			||||||
 | 
					       transform="matrix(35.45144,0,0,35.45144,-106.35,-106.35)"
 | 
				
			||||||
 | 
					       id="path842" />
 | 
				
			||||||
 | 
					  </g>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 4.6 KiB  | 
| 
						 | 
					@ -20,11 +20,11 @@
 | 
				
			||||||
  "icon": "./assets/themes/cyclofix/logo.svg",
 | 
					  "icon": "./assets/themes/cyclofix/logo.svg",
 | 
				
			||||||
  "version": "0",
 | 
					  "version": "0",
 | 
				
			||||||
  "startLat": 50.8465573,
 | 
					  "startLat": 50.8465573,
 | 
				
			||||||
 
 | 
					  "defaultBackgroundId": "CartoDB.Voyager",
 | 
				
			||||||
  "startLon":  4.3516970,
 | 
					  "startLon":  4.3516970,
 | 
				
			||||||
  "startZoom": 16,
 | 
					  "startZoom": 16,
 | 
				
			||||||
  "widenFactor": 0.05,
 | 
					  "widenFactor": 0.05,
 | 
				
			||||||
  "socialImage": "./assets/themes/cyclofix/logo.svg",
 | 
					  "socialImage": "./assets/themes/cyclofix/logo.svg",
 | 
				
			||||||
  "layers": ["bike_repair_station", "bike_cafes", "bike_shops", "drinking_water", "bike_parking","bike_themed_object"],
 | 
					  "layers": ["bike_repair_station", "bike_cafes", "bike_shops", "drinking_water", "bike_parking","bike_themed_object","bike_monitoring_station"],
 | 
				
			||||||
  "roamingRenderings": []
 | 
					  "roamingRenderings": []
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								test.ts
									
										
									
									
									
								
							| 
						 | 
					@ -1,15 +1,16 @@
 | 
				
			||||||
//*
 | 
					//*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {UIEventSource} from "./Logic/UIEventSource";
 | 
					import LiveQueryHandler from "./Logic/Web/LiveQueryHandler";
 | 
				
			||||||
import OpeningHoursVisualization from "./UI/OhVisualization";
 | 
					import {VariableUiElement} from "./UI/Base/VariableUIElement";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const oh = "Tu-Fr 09:00-17:00 'as usual'; mo off 'yyy'; su off 'xxx'"
 | 
					const source = LiveQueryHandler.FetchLiveData("https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CJM90",
 | 
				
			||||||
const tags = new UIEventSource<any>({opening_hours:oh});
 | 
					    "hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt".split(";"))
 | 
				
			||||||
new OpeningHoursVisualization(tags, 'opening_hours').AttachTo('maindiv')
 | 
					source.addCallback((data) => {console.log(data)})
 | 
				
			||||||
 | 
					new VariableUiElement(source.map(data => {
 | 
				
			||||||
 | 
					    return ["Data is:", data.hour, "last hour;", data.day, "last day; ", data.year, "last year;"].join(" ")
 | 
				
			||||||
 | 
					})).AttachTo('maindiv')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
window.setTimeout(() => {tags.data._country = "be"; }, 5000)
 | 
					 | 
				
			||||||
/*/
 | 
					/*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue