Lot's of small improvements
This commit is contained in:
parent
9bd37d9cde
commit
8bca006787
29 changed files with 375 additions and 173 deletions
|
@ -1,17 +1,12 @@
|
|||
import {Basemap} from "./Logic/Basemap";
|
||||
import {ElementStorage} from "./Logic/ElementStorage";
|
||||
import {Changes} from "./Logic/Changes";
|
||||
import {Question, QuestionDefinition} from "./Logic/Question";
|
||||
import {TagMapping, TagMappingOptions} from "./UI/TagMapping";
|
||||
import {QuestionDefinition} from "./Logic/Question";
|
||||
import {TagMappingOptions} from "./UI/TagMapping";
|
||||
import {UIEventSource} from "./UI/UIEventSource";
|
||||
import {QuestionPicker} from "./UI/QuestionPicker";
|
||||
import {VerticalCombine} from "./UI/VerticalCombine";
|
||||
import {UIElement} from "./UI/UIElement";
|
||||
import {Tag, TagsFilter} from "./Logic/TagsFilter";
|
||||
import {FilteredLayer} from "./Logic/FilteredLayer";
|
||||
import {ImageCarousel} from "./UI/Image/ImageCarousel";
|
||||
import {FixedUiElement} from "./UI/FixedUiElement";
|
||||
import {OsmImageUploadHandler} from "./Logic/OsmImageUploadHandler";
|
||||
import {UserDetails} from "./Logic/OsmConnection";
|
||||
|
||||
|
||||
|
@ -37,7 +32,6 @@ export class LayerDefinition {
|
|||
|
||||
asLayer(basemap: Basemap, allElements: ElementStorage, changes: Changes, userDetails: UIEventSource<UserDetails>, selectedElement: UIEventSource<any>):
|
||||
FilteredLayer {
|
||||
const self = this;
|
||||
return new FilteredLayer(
|
||||
this.name,
|
||||
basemap, allElements, changes,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import L from "leaflet"
|
||||
import {UIEventSource} from "../UI/UIEventSource";
|
||||
import {UIElement} from "../UI/UIElement";
|
||||
|
||||
// Contains all setup and baselayers for Leaflet stuff
|
||||
export class Basemap {
|
||||
|
@ -8,6 +9,7 @@ export class Basemap {
|
|||
public map: Map;
|
||||
|
||||
public Location: UIEventSource<{ zoom: number, lat: number, lon: number }>;
|
||||
public LastClickLocation: UIEventSource<{ lat: number, lon: number }> = new UIEventSource<{lat: number, lon: number}>(undefined)
|
||||
|
||||
private aivLucht2013Layer = L.tileLayer.wms('https://geoservices.informatievlaanderen.be/raadpleegdiensten/OGW/wms?s',
|
||||
{
|
||||
|
@ -19,7 +21,7 @@ export class Basemap {
|
|||
"LAYER=omwrgbmrvl&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={z}&tileRow={y}&tileCol={x}",
|
||||
{
|
||||
// omwrgbmrvl
|
||||
attribution: 'Map Data <a href="osm.org">OpenStreetMap</a> | Luchtfoto\'s van © AIV Vlaanderen (Laatste) © AGIV',
|
||||
attribution: 'Map Data <a href="https://osm.org">OpenStreetMap</a> | Luchtfoto\'s van © AIV Vlaanderen (Laatste) © AGIV',
|
||||
maxZoom: 20,
|
||||
minZoom: 1,
|
||||
wmts: true
|
||||
|
@ -28,20 +30,20 @@ export class Basemap {
|
|||
|
||||
private osmLayer = L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: 'Map Data and background © <a href="osm.org">OpenStreetMap</a>',
|
||||
attribution: 'Map Data and background © <a href="https://osm.org">OpenStreetMap</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 1
|
||||
});
|
||||
private osmBeLayer = L.tileLayer("https://tile.osm.be/osmbe/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: 'Map Data and background © <a href="osm.org">OpenStreetMap</a> | <a href="https://geo6.be/">Tiles courtesy of Geo6</a>',
|
||||
attribution: 'Map Data and background © <a href="https://osm.org">OpenStreetMap</a> | <a href="https://geo6.be/">Tiles courtesy of Geo6</a>',
|
||||
maxZoom: 18,
|
||||
minZoom: 1
|
||||
});
|
||||
|
||||
private grbLayer = L.tileLayer("https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=grb_bsk&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={z}&tileCol={x}&tileRow={y}",
|
||||
{
|
||||
attribution: 'Map Data <a href="osm.org">OpenStreetMap</a> | Background <i>Grootschalig ReferentieBestand</i>(GRB) © AGIV',
|
||||
attribution: 'Map Data <a href="https://osm.org">OpenStreetMap</a> | Background <i>Grootschalig ReferentieBestand</i>(GRB) © AGIV',
|
||||
maxZoom: 20,
|
||||
minZoom: 1,
|
||||
wmts: true
|
||||
|
@ -56,16 +58,17 @@ export class Basemap {
|
|||
"GRB Vlaanderen": this.grbLayer
|
||||
};
|
||||
|
||||
|
||||
constructor(leafletElementId: string, location: UIEventSource<{ zoom: number, lat: number, lon: number }>) {
|
||||
constructor(leafletElementId: string,
|
||||
location: UIEventSource<{ zoom: number, lat: number, lon: number }>,
|
||||
extraAttribution: UIElement) {
|
||||
this.map = L.map(leafletElementId, {
|
||||
center: [location.data.lat, location.data.lon],
|
||||
zoom: location.data.zoom,
|
||||
layers: [this.osmLayer],
|
||||
});
|
||||
|
||||
|
||||
this.map.attributionControl.setPrefix(extraAttribution.Render());
|
||||
this.Location = location;
|
||||
|
||||
const layerControl = L.control.layers(this.baseLayers, null,
|
||||
{
|
||||
position: 'bottomright',
|
||||
|
@ -74,19 +77,19 @@ export class Basemap {
|
|||
layerControl.addTo(this.map);
|
||||
|
||||
|
||||
|
||||
|
||||
this.map.zoomControl.setPosition("bottomright");
|
||||
const self = this;
|
||||
|
||||
this.map.on("moveend", function () {
|
||||
location.data.zoom = self.map.getZoom();
|
||||
location.data.lat = self.map.getCenter().lat;
|
||||
location.data.lon = self.map.getCenter().lon;
|
||||
location.data.lon = self.map.getCenter().lng;
|
||||
location.ping();
|
||||
});
|
||||
|
||||
|
||||
this.map.on("click", function (e) {
|
||||
self.LastClickLocation.setData({lat: e.latlng.lat, lon: e.latlng.lng})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -183,9 +183,10 @@ export class FilteredLayer {
|
|||
eventSource.addCallback(function () {
|
||||
self.updateStyle();
|
||||
});
|
||||
layer.on("click", function(){
|
||||
layer.on("click", function(e){
|
||||
console.log("Selected ",feature)
|
||||
self._selectedElement.setData(feature.properties);
|
||||
L.DomEvent.stop(e); // Marks the event as consumed
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import {UIEventSource} from "../UI/UIEventSource";
|
|||
import {UIElement} from "../UI/UIElement";
|
||||
import L from "leaflet";
|
||||
import {Helpers} from "../Helpers";
|
||||
import {UserDetails} from "./OsmConnection";
|
||||
|
||||
export class GeoLocationHandler extends UIElement {
|
||||
|
||||
|
@ -78,7 +79,6 @@ export class GeoLocationHandler extends UIElement {
|
|||
});
|
||||
|
||||
this.HideOnEmpty(true);
|
||||
|
||||
}
|
||||
|
||||
protected InnerRender(): string {
|
||||
|
|
|
@ -51,7 +51,6 @@ export class GeoOperations {
|
|||
}
|
||||
const intersectionSize = turf.area(intersection);
|
||||
const ratio = intersectionSize / featureSurface;
|
||||
console.log("Intersection ratio", ratio, "intersection:", intersectionSize, "featuresize:", featureSurface, "targetRatio", maxOverlapPercentage / 100);
|
||||
|
||||
if (ratio * 100 >= maxOverlapPercentage) {
|
||||
console.log("Refused", poly.id, " due to ", shouldNotContainElement.id, "intersection ratio is ", ratio, "which is bigger then the target ratio of ", (maxOverlapPercentage / 100))
|
||||
|
|
|
@ -12,6 +12,7 @@ export class UserDetails {
|
|||
public totalMessages = 0;
|
||||
public osmConnection: OsmConnection;
|
||||
public dryRun: boolean;
|
||||
home: { lon: number; lat: number };
|
||||
|
||||
}
|
||||
|
||||
|
@ -66,6 +67,8 @@ export class OsmConnection {
|
|||
if (details == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.UpdatePreferences();
|
||||
// details is an XML DOM of user details
|
||||
let userInfo = details.getElementsByTagName("user")[0];
|
||||
|
||||
|
@ -84,6 +87,13 @@ export class OsmConnection {
|
|||
}
|
||||
data.img = data.img ?? "./assets/osm-logo.svg";
|
||||
|
||||
const homeEl = userInfo.getElementsByTagName("home");
|
||||
if (homeEl !== undefined && homeEl[0] !== undefined) {
|
||||
const lat = parseFloat(homeEl[0].getAttribute("lat"));
|
||||
const lon = parseFloat(homeEl[0].getAttribute("lon"));
|
||||
data.home = {lat: lat, lon: lon};
|
||||
}
|
||||
|
||||
const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0];
|
||||
data.unreadMessages = parseInt(messages.getAttribute("unread"));
|
||||
data.totalMessages = parseInt(messages.getAttribute("count"));
|
||||
|
@ -108,6 +118,47 @@ export class OsmConnection {
|
|||
}
|
||||
}
|
||||
|
||||
public preferences = new UIEventSource<any>({});
|
||||
private UpdatePreferences() {
|
||||
const self = this;
|
||||
this.auth.xhr({
|
||||
method: 'GET',
|
||||
path: '/api/0.6/user/preferences'
|
||||
}, function (error, value: XMLDocument) {
|
||||
if(error){
|
||||
console.log("Could not load preferences", error);
|
||||
return;
|
||||
}
|
||||
const prefs = value.getElementsByTagName("preference");
|
||||
for (let i = 0; i < prefs.length; i++) {
|
||||
const pref = prefs[i];
|
||||
const k = pref.getAttribute("k");
|
||||
const v = pref.getAttribute("v");
|
||||
self.preferences.data[k] = v;
|
||||
}
|
||||
self.preferences.ping();
|
||||
});
|
||||
}
|
||||
|
||||
public SetPreference(k:string, v:string){
|
||||
this.preferences.data[k] = v;
|
||||
this.preferences.ping();
|
||||
this.auth.xhr({
|
||||
method: 'PUT',
|
||||
path: '/api/0.6/user/preferences/'+k,
|
||||
options: { header: { 'Content-Type': 'text/plain' } },
|
||||
content: v
|
||||
},function(error, result) {
|
||||
if(error){
|
||||
console.log("Could not set preference", error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Preference written!", result);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static parseUploadChangesetResponse(response: XMLDocument) {
|
||||
const nodes = response.getElementsByTagName("node");
|
||||
const mapping = {};
|
||||
|
|
49
Logic/StrayClickHandler.ts
Normal file
49
Logic/StrayClickHandler.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import {Basemap} from "./Basemap";
|
||||
import L from "leaflet";
|
||||
import {UIEventSource} from "../UI/UIEventSource";
|
||||
import {UIElement} from "../UI/UIElement";
|
||||
|
||||
/**
|
||||
* The stray-click-hanlders adds a marker to the map if no feature was clicked.
|
||||
* Shows the given uiToShow-element in the messagebox
|
||||
*/
|
||||
export class StrayClickHandler {
|
||||
private _basemap: Basemap;
|
||||
private _lastMarker;
|
||||
private _leftMessage: UIEventSource<() => UIElement>;
|
||||
private _uiToShow: (() => UIElement);
|
||||
|
||||
constructor(
|
||||
basemap: Basemap,
|
||||
selectElement: UIEventSource<any>,
|
||||
leftMessage: UIEventSource<() => UIElement>,
|
||||
uiToShow: (() => UIElement)) {
|
||||
this._basemap = basemap;
|
||||
this._leftMessage = leftMessage;
|
||||
this._uiToShow = uiToShow;
|
||||
const self = this;
|
||||
const map = basemap.map;
|
||||
basemap.LastClickLocation.addCallback(function (lastClick) {
|
||||
|
||||
if (self._lastMarker !== undefined) {
|
||||
map.removeLayer(self._lastMarker);
|
||||
}
|
||||
|
||||
self._lastMarker = L.marker([lastClick.lat, lastClick.lon]);
|
||||
self._lastMarker.addTo(map);
|
||||
|
||||
leftMessage.setData(self._uiToShow);
|
||||
|
||||
});
|
||||
|
||||
selectElement.addCallback(() => {
|
||||
if (self._lastMarker !== undefined) {
|
||||
map.removeLayer(self._lastMarker);
|
||||
this._lastMarker = undefined;
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -73,3 +73,7 @@ Data from OpenStreetMap
|
|||
Images from Wikipedia/Wikimedia
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg
|
||||
Camera Icon, Dave Gandy, CC-BY-SA 3.0
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:Home-icon.svg
|
||||
Home icon by Timothy Miller, CC-BY-SA 3.0
|
||||
|
|
|
@ -58,7 +58,7 @@ export class AddButton extends UIElement {
|
|||
basemap.map.on("mousemove", function(){
|
||||
if (self.state.data === self.PLACING_POI) {
|
||||
|
||||
var icon = "crosshair";
|
||||
let icon = "crosshair";
|
||||
for (const option of self._options) {
|
||||
if (option.name === self.curentAddSelection.data && option.icon !== undefined) {
|
||||
icon = 'url("' + option.icon + '") 32 32 ,crosshair';
|
||||
|
|
38
UI/Base/Button.ts
Normal file
38
UI/Base/Button.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
|
||||
export class Button extends UIElement {
|
||||
private _text: UIElement;
|
||||
private _onclick: () => void;
|
||||
private _clss: string;
|
||||
|
||||
constructor(text: UIElement, onclick: (() => void), clss: string = "") {
|
||||
super(undefined);
|
||||
this._text = text;
|
||||
this._onclick = onclick;
|
||||
if (clss !== "") {
|
||||
|
||||
this._clss = "class='" + clss + "'";
|
||||
}else{
|
||||
this._clss = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected InnerRender(): string {
|
||||
|
||||
return "<form>" +
|
||||
"<button id='button-"+this.id+"' type='button' "+this._clss+">" + this._text.Render() + "</button>" +
|
||||
"</form>";
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
super.InnerUpdate(htmlElement);
|
||||
const self = this;
|
||||
console.log("Update for ", htmlElement)
|
||||
document.getElementById("button-"+this.id).onclick = function(){
|
||||
console.log("Clicked");
|
||||
self._onclick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {UIEventSource} from "../UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
|
||||
export class DropDownUI extends UIElement {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIElement} from "../UIElement";
|
||||
|
||||
export class FixedUiElement extends UIElement {
|
||||
private _html: string;
|
|
@ -1,5 +1,5 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {UIEventSource} from "../UIEventSource";
|
||||
import {FixedUiElement} from "./FixedUiElement";
|
||||
import $ from "jquery"
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {UIEventSource} from "../UIEventSource";
|
||||
|
||||
export class VariableUiElement extends UIElement {
|
||||
private _html: UIEventSource<string>;
|
|
@ -1,4 +1,4 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIElement} from "../UIElement";
|
||||
|
||||
export class VerticalCombine extends UIElement {
|
||||
private _elements: UIElement[];
|
|
@ -1,6 +1,5 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {Helpers} from "../Helpers";
|
||||
import {OsmConnection} from "../Logic/OsmConnection";
|
||||
|
||||
export class CenterMessageBox extends UIElement {
|
||||
|
|
|
@ -2,7 +2,6 @@ import {UIElement} from "./UIElement";
|
|||
import {TagMapping, TagMappingOptions} from "./TagMapping";
|
||||
import {Question, QuestionDefinition} from "../Logic/Question";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {VerticalCombine} from "./VerticalCombine";
|
||||
import {QuestionPicker} from "./QuestionPicker";
|
||||
import {OsmImageUploadHandler} from "../Logic/OsmImageUploadHandler";
|
||||
import {ImageCarousel} from "./Image/ImageCarousel";
|
||||
|
@ -12,6 +11,7 @@ import {Img} from "./Img";
|
|||
import {CommonTagMappings} from "../Layers/CommonTagMappings";
|
||||
import {Tag} from "../Logic/TagsFilter";
|
||||
import {ImageUploadFlow} from "./ImageUploadFlow";
|
||||
import {VerticalCombine} from "./Base/VerticalCombine";
|
||||
|
||||
export class FeatureInfoBox extends UIElement {
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
*/
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {UIElement} from "./UIElement";
|
||||
import {FixedUiElement} from "./FixedUiElement";
|
||||
import {VariableUiElement} from "./VariableUIElement";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
|
||||
export class MessageBoxHandler {
|
||||
private _uielement: UIEventSource<() => UIElement>;
|
||||
|
@ -15,8 +14,16 @@ export class MessageBoxHandler {
|
|||
this.listenTo(uielement);
|
||||
this.update();
|
||||
|
||||
window.onhashchange = function () {
|
||||
if (location.hash === "") {
|
||||
// No more element: back to the map!
|
||||
uielement.setData(undefined);
|
||||
onClear();
|
||||
}
|
||||
}
|
||||
|
||||
new VariableUiElement(new UIEventSource<string>("<h2>Naar de kaart</h2>"),
|
||||
(htmlElement) => {
|
||||
() => {
|
||||
document.getElementById("to-the-map").onclick = function () {
|
||||
uielement.setData(undefined);
|
||||
onClear();
|
||||
|
@ -24,6 +31,7 @@ export class MessageBoxHandler {
|
|||
}
|
||||
).AttachTo("to-the-map");
|
||||
|
||||
|
||||
}
|
||||
|
||||
listenTo(uiEventSource: UIEventSource<any>) {
|
||||
|
@ -33,14 +41,19 @@ export class MessageBoxHandler {
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
update() {
|
||||
const wrapper = document.getElementById("messagesboxmobilewrapper");
|
||||
const gen = this._uielement.data;
|
||||
console.log("Generator: ", gen);
|
||||
if (gen === undefined) {
|
||||
wrapper.classList.add("hidden");
|
||||
wrapper.classList.add("hidden")
|
||||
if (location.hash !== "") {
|
||||
location.hash = ""
|
||||
}
|
||||
return;
|
||||
}
|
||||
location.hash = "#element"
|
||||
wrapper.classList.remove("hidden");
|
||||
gen()
|
||||
?.HideOnEmpty(true)
|
||||
|
|
77
UI/SimpleAddUI.ts
Normal file
77
UI/SimpleAddUI.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {Tag} from "../Logic/TagsFilter";
|
||||
import {FilteredLayer} from "../Logic/FilteredLayer";
|
||||
import {Changes} from "../Logic/Changes";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import {Button} from "./Base/Button";
|
||||
|
||||
/**
|
||||
* Asks to add a feature at the last clicked location, at least if zoom is sufficient
|
||||
*/
|
||||
export class SimpleAddUI extends UIElement {
|
||||
private _zoomlevel: UIEventSource<{ zoom: number }>;
|
||||
private _addButtons: UIElement[];
|
||||
private _lastClickLocation: UIEventSource<{ lat: number; lon: number }>;
|
||||
private _changes: Changes;
|
||||
private _selectedElement: UIEventSource<any>;
|
||||
|
||||
constructor(zoomlevel: UIEventSource<{ zoom: number }>,
|
||||
lastClickLocation: UIEventSource<{ lat: number, lon: number }>,
|
||||
changes: Changes,
|
||||
selectedElement: UIEventSource<any>,
|
||||
addButtons: { name: string; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[],
|
||||
) {
|
||||
super(zoomlevel);
|
||||
this._zoomlevel = zoomlevel;
|
||||
this._lastClickLocation = lastClickLocation;
|
||||
this._changes = changes;
|
||||
this._selectedElement = selectedElement;
|
||||
this._addButtons = [];
|
||||
|
||||
for (const option of addButtons) {
|
||||
// <button type='button'> looks SO retarded
|
||||
// the default type of button is 'submit', which performs a POST and page reload
|
||||
const button =
|
||||
new Button(new FixedUiElement("Voeg hier een " + option.name + " toe"),
|
||||
this.CreatePoint(option));
|
||||
this._addButtons.push(button);
|
||||
}
|
||||
}
|
||||
|
||||
private CreatePoint(option: { name: string; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }) {
|
||||
const self = this;
|
||||
return () => {
|
||||
|
||||
console.log("Creating a new ", option.name, " at last click location");
|
||||
const loc = self._lastClickLocation.data;
|
||||
let feature = self._changes.createElement(option.tags, loc.lat, loc.lon);
|
||||
option.layerToAddTo.AddNewElement(feature);
|
||||
self._selectedElement.setData(feature.properties);
|
||||
}
|
||||
}
|
||||
|
||||
protected InnerRender(): string {
|
||||
const header = "<h2>Geen selectie</h2>" +
|
||||
"Je klikte ergens waar er nog geen gezochte data is.<br/>"
|
||||
if (this._zoomlevel.data.zoom < 19) {
|
||||
return header + "Zoom verder in om een element toe te voegen."
|
||||
}
|
||||
|
||||
var html = "";
|
||||
for (const button of this._addButtons) {
|
||||
// <button type='button'> looks SO retarded
|
||||
// the default type of button is 'submit', which performs a POST and page reload
|
||||
html += button.Render();
|
||||
}
|
||||
return header + html;
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
super.InnerUpdate(htmlElement);
|
||||
for (const button of this._addButtons) {
|
||||
button.Update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ export class UIEventSource<T>{
|
|||
}
|
||||
|
||||
|
||||
public addCallback(callback: ((latestData) => void)) {
|
||||
public addCallback(callback: ((latestData : T) => void)) {
|
||||
this._callbacks.push(callback);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UserDetails} from "../Logic/OsmConnection";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {Basemap} from "../Logic/Basemap";
|
||||
import L from "leaflet";
|
||||
|
||||
/**
|
||||
* Handles and updates the user badge
|
||||
|
@ -8,13 +10,16 @@ import {UIEventSource} from "./UIEventSource";
|
|||
export class UserBadge extends UIElement {
|
||||
private _userDetails: UIEventSource<UserDetails>;
|
||||
private _pendingChanges: UIElement;
|
||||
private _basemap: Basemap;
|
||||
|
||||
|
||||
constructor(userDetails: UIEventSource<UserDetails>,
|
||||
pendingChanges : UIElement) {
|
||||
pendingChanges: UIElement,
|
||||
basemap: Basemap) {
|
||||
super(userDetails);
|
||||
this._userDetails = userDetails;
|
||||
this._pendingChanges = pendingChanges;
|
||||
this._basemap = basemap;
|
||||
|
||||
userDetails.addCallback(function () {
|
||||
const profilePic = document.getElementById("profile-pic");
|
||||
|
@ -33,13 +38,13 @@ export class UserBadge extends UIElement {
|
|||
|
||||
|
||||
let messageSpan = "<span id='messages'>" +
|
||||
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='envelope' src='./assets/envelope.svg'/>" +
|
||||
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='small-userbadge-icon' src='./assets/envelope.svg' alt='msgs'>" +
|
||||
user.totalMessages +
|
||||
"</a></span>";
|
||||
|
||||
if (user.unreadMessages > 0) {
|
||||
messageSpan = "<span id='messages' class='alert'>" +
|
||||
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='envelope' src='./assets/envelope.svg'/>" +
|
||||
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='small-userbadge-icon' src='./assets/envelope.svg' alt='msgs'/>" +
|
||||
" " +
|
||||
"" +
|
||||
user.unreadMessages.toString() +
|
||||
|
@ -51,16 +56,28 @@ export class UserBadge extends UIElement {
|
|||
dryrun = " <span class='alert'>TESTING</span>";
|
||||
}
|
||||
|
||||
return "<img id='profile-pic' src='" + user.img + "'/> " +
|
||||
let home = "";
|
||||
if (user.home !== undefined) {
|
||||
home = "<img id='home' src='./assets/home.svg' alt='home' class='small-userbadge-icon'>";
|
||||
const icon = L.icon({
|
||||
iconUrl: 'assets/home.svg',
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10]
|
||||
});
|
||||
L.marker([user.home.lat, user.home.lon], {icon: icon}).addTo(this._basemap.map);
|
||||
}
|
||||
|
||||
return "<img id='profile-pic' src='" + user.img + "' alt='profile-pic'/> " +
|
||||
"<div id='usertext'>" +
|
||||
"<p id='username'>" +
|
||||
"<a href='https://www.openstreetmap.org/user/" + user.name + "' target='_blank'>" + user.name + "</a>" +
|
||||
dryrun +
|
||||
"</p> " +
|
||||
"<p id='userstats'>" +
|
||||
home +
|
||||
messageSpan +
|
||||
"<span id='csCount'> " +
|
||||
" <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='star' src='./assets/star.svg'/> " + user.csCount +
|
||||
" <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='small-userbadge-icon' src='./assets/star.svg' alt='star'/> " + user.csCount +
|
||||
"</a></span> " +
|
||||
this._pendingChanges.Render() +
|
||||
"</p>" +
|
||||
|
@ -70,6 +87,18 @@ export class UserBadge extends UIElement {
|
|||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
this._pendingChanges.Update();
|
||||
var btn = document.getElementById("home");
|
||||
if (btn) {
|
||||
const self = this;
|
||||
btn.onclick = function () {
|
||||
const home = self._userDetails?.data?.home;
|
||||
if (home === undefined) {
|
||||
return;
|
||||
}
|
||||
self._basemap.map.flyTo([home.lat, home.lon], 18);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Activate() {
|
||||
|
|
3
assets/bug.svg
Normal file
3
assets/bug.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg height="1024" width="733.886" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M243.621 156.53099999999995C190.747 213.312 205.34 304 205.34 304s53.968 64 160 64c106.031 0 160.031-64 160.031-64s14.375-89.469-37.375-146.312c32.375-18.031 51.438-44.094 43.562-61.812-8.938-19.969-48.375-21.75-88.25-3.969-14.812 6.594-27.438 14.969-37.25 23.875-12.438-2.25-25.625-3.781-40.72-3.781-14.061 0-26.561 1.344-38.344 3.25-9.656-8.75-22.062-16.875-36.531-23.344-39.875-17.719-79.375-15.938-88.25 3.969C194.465 113.21900000000005 212.497 138.562 243.621 156.53099999999995zM644.746 569.75c-8.25-1.75-16.125-2.75-23.75-3.5 0-2.125 0.375-4.125 0.375-6.312 0-33.594-4.75-65.654-12.438-96.125 16.438 1.406 37.375-2.375 58.562-11.779 39.875-17.781 65-48.375 56.125-68.219-8.875-19.969-48.375-21.75-88.25-3.969-18.625 8.312-33.812 19.469-44 30.906-7.75-18.25-16.5-35.781-26.812-51.719-30.188 25.156-87.312 62.719-167.062 71.062v321.781c0 0-0.25 32-32.031 32-31.75 0-32-32-32-32V430.219c-79.811-8.344-136.968-45.969-167.093-71.062-9.875 15.312-18.375 32-25.938 49.344-10.281-10.625-24.625-20.844-41.969-28.594-39.875-17.719-79.375-15.938-88.25 3.969-8.906 19.906 16.25 50.438 56.125 68.219 19.844 8.846 39.531 12.812 55.469 12.096-7.656 30.404-12.469 62.344-12.469 95.812 0 2.188 0.375 4.25 0.438 6.5-6.719 0.75-13.688 1.75-20.781 3.25-51.969 10.75-91.781 37.625-88.844 59.812 2.938 22.312 47.5 31.5 99.594 20.688 6.781-1.375 13.438-3.125 19.781-5.062C128.684 686 143.34 723.875 163.622 756.5c-12.031 6.062-24.531 15-36.031 26.625C95.715 815 82.779 853.75 98.715 869.688c15.938 15.937 54.656 3 86.531-28.812 9.344-9.375 16.844-19.25 22.656-29C251.434 854.5 305.965 880 365.465 880c60.343 0 115.781-26.25 159.531-69.938 5.875 10.312 13.75 20.812 23.625 30.688 31.812 31.875 70.625 44.812 86.562 28.875s3-54.625-28.875-86.5c-12.312-12.375-25.688-21.75-38.438-27.938 20.125-32.5 34.625-70.375 43.688-111.062 7.188 2.25 14.688 4.375 22.562 6.062 52.061 10.812 96.625 1.562 99.625-20.688C736.558 607.375 696.746 580.5 644.746 569.75z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
3
assets/github.svg
Normal file
3
assets/github.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" transform="scale(64)" fill="#1B1F23"/>
|
||||
</svg>
|
After Width: | Height: | Size: 967 B |
3
assets/home.svg
Normal file
3
assets/home.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg height="16px" id="Layer_1" style="enable-background:new 0 0 16 16;" version="1.1" viewBox="0 0 16 16" width="16px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M15.45,7L14,5.551V2c0-0.55-0.45-1-1-1h-1c-0.55,0-1,0.45-1,1v0.553L9,0.555C8.727,0.297,8.477,0,8,0S7.273,0.297,7,0.555 L0.55,7C0.238,7.325,0,7.562,0,8c0,0.563,0.432,1,1,1h1v6c0,0.55,0.45,1,1,1h3v-5c0-0.55,0.45-1,1-1h2c0.55,0,1,0.45,1,1v5h3 c0.55,0,1-0.45,1-1V9h1c0.568,0,1-0.437,1-1C16,7.562,15.762,7.325,15.45,7z"/></svg>
|
After Width: | Height: | Size: 689 B |
3
assets/pencil.svg
Normal file
3
assets/pencil.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg height="1024" width="896" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M704 64L576 192l192 192 128-128L704 64zM0 768l0.688 192.562L192 960l512-512L512 256 0 768zM192 896H64V768h64v64h64V896z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 207 B |
19
index.css
19
index.css
|
@ -12,11 +12,6 @@ body {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
|
||||
#geolocate-button {
|
||||
position: absolute;
|
||||
bottom: 27px;
|
||||
|
@ -67,19 +62,17 @@ img {
|
|||
|
||||
/**************** USER BADGE ****************/
|
||||
|
||||
|
||||
.star {
|
||||
.small-userbadge-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: black;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.envelope {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
#home {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
#profile-pic {
|
||||
float: left;
|
||||
width: 4em;
|
||||
|
|
46
index.ts
46
index.ts
|
@ -8,16 +8,17 @@ import {PendingChanges} from "./UI/PendingChanges";
|
|||
import {CenterMessageBox} from "./UI/CenterMessageBox";
|
||||
import {Helpers} from "./Helpers";
|
||||
import {KnownSet} from "./Layers/KnownSet";
|
||||
import {Tag, TagsFilter, TagUtils} from "./Logic/TagsFilter";
|
||||
import {Tag, TagUtils} from "./Logic/TagsFilter";
|
||||
import {FilteredLayer} from "./Logic/FilteredLayer";
|
||||
import {LayerUpdater} from "./Logic/LayerUpdater";
|
||||
import {VariableUiElement} from "./UI/VariableUIElement";
|
||||
import {UIElement} from "./UI/UIElement";
|
||||
import {MessageBoxHandler} from "./UI/MessageBoxHandler";
|
||||
import {Overpass} from "./Logic/Overpass";
|
||||
import {FixedUiElement} from "./UI/FixedUiElement";
|
||||
import {FeatureInfoBox} from "./UI/FeatureInfoBox";
|
||||
import {GeoLocationHandler} from "./Logic/GeoLocationHandler";
|
||||
import {StrayClickHandler} from "./Logic/StrayClickHandler";
|
||||
import {SimpleAddUI} from "./UI/SimpleAddUI";
|
||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
||||
|
||||
let dryRun = false;
|
||||
|
||||
|
@ -66,7 +67,7 @@ const leftMessage = new UIEventSource<() => UIElement>(undefined);
|
|||
const selectedElement = new UIEventSource<any>(undefined);
|
||||
|
||||
|
||||
const locationControl = new UIEventSource({
|
||||
const locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({
|
||||
zoom: questSetToRender.startzoom,
|
||||
lat: questSetToRender.startLat,
|
||||
lon: questSetToRender.startLon
|
||||
|
@ -81,7 +82,22 @@ const osmConnection = new OsmConnection(dryRun);
|
|||
const changes = new Changes(
|
||||
"Beantwoorden van vragen met MapComplete voor vragenset #" + questSetToRender.name,
|
||||
osmConnection, allElements, centerMessage);
|
||||
const bm = new Basemap("leafletDiv", locationControl);
|
||||
const bm = new Basemap("leafletDiv", locationControl, new VariableUiElement(
|
||||
locationControl.map((location) => {
|
||||
const mapComplete = "<a href='https://github.com/pietervdvn/MapComplete' target='_blank'>Mapcomple</a> " +
|
||||
" " +
|
||||
"<a href='https://github.com/pietervdvn/MapComplete/issues' target='_blank'><img src='./assets/bug.svg' alt='Report bug' class='small-userbadge-icon'></a>";
|
||||
let editHere = "";
|
||||
if (location !== undefined) {
|
||||
editHere = " | " +
|
||||
"<a href='https://www.openstreetmap.org/edit?editor=id#map=" + location.zoom + "/" + location.lat + "/" + location.lon + "' target='_blank'>" +
|
||||
"<img src='./assets/pencil.svg' alt='edit here' class='small-userbadge-icon'>" +
|
||||
"</a>"
|
||||
}
|
||||
return mapComplete + editHere;
|
||||
|
||||
})
|
||||
));
|
||||
|
||||
|
||||
// ------------- Setup the layers -------------------------------
|
||||
|
@ -115,11 +131,21 @@ const layerUpdater = new LayerUpdater(bm, questSetToRender.startzoom, flayers);
|
|||
|
||||
// ------------------ Setup various UI elements ------------
|
||||
|
||||
|
||||
/*
|
||||
const addButton = new AddButton(bm, changes, addButtons);
|
||||
addButton.AttachTo("bottomRight");
|
||||
addButton.Update();
|
||||
*/
|
||||
addButton.Update();*/
|
||||
|
||||
|
||||
new StrayClickHandler(bm, selectedElement, leftMessage, () => {
|
||||
return new SimpleAddUI(bm.Location,
|
||||
bm.LastClickLocation,
|
||||
changes,
|
||||
selectedElement,
|
||||
addButtons);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Show the questions and information for the selected element on the leftMessage
|
||||
|
@ -147,10 +173,11 @@ selectedElement.addCallback((data) => {
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
const pendingChanges = new PendingChanges(
|
||||
changes.pendingChangesES, secondsTillChangesAreSaved, changes.isSaving);
|
||||
|
||||
new UserBadge(osmConnection.userDetails, pendingChanges)
|
||||
new UserBadge(osmConnection.userDetails, pendingChanges, bm)
|
||||
.AttachTo('userbadge');
|
||||
|
||||
var welcomeMessage = () => {
|
||||
|
@ -164,7 +191,7 @@ var welcomeMessage = () => {
|
|||
questSetToRender.welcomeMessage + login +
|
||||
"</div>";
|
||||
}),
|
||||
function (html) {
|
||||
function () {
|
||||
osmConnection.registerActivateOsmAUthenticationClass()
|
||||
});
|
||||
}
|
||||
|
@ -185,7 +212,6 @@ new CenterMessageBox(
|
|||
Helpers.SetupAutoSave(changes, secondsTillChangesAreSaved, saveTimeout);
|
||||
Helpers.LastEffortSave(changes);
|
||||
|
||||
|
||||
osmConnection.registerActivateOsmAUthenticationClass();
|
||||
|
||||
|
||||
|
|
86
test.ts
86
test.ts
|
@ -1,86 +0,0 @@
|
|||
// The message that should be shown at the center of the screen
|
||||
import {UIEventSource} from "./UI/UIEventSource";
|
||||
import {UIElement} from "./UI/UIElement";
|
||||
import {ElementStorage} from "./Logic/ElementStorage";
|
||||
import {OsmConnection} from "./Logic/OsmConnection";
|
||||
import {Changes} from "./Logic/Changes";
|
||||
import {Basemap} from "./Logic/Basemap";
|
||||
import {KnownSet} from "./Layers/KnownSet";
|
||||
import {Overpass} from "./Logic/Overpass";
|
||||
import {FeatureInfoBox} from "./UI/FeatureInfoBox";
|
||||
import {TagMapping, TagMappingOptions} from "./UI/TagMapping";
|
||||
import {CommonTagMappings} from "./Layers/CommonTagMappings";
|
||||
import {ImageCarousel} from "./UI/Image/ImageCarousel";
|
||||
import {WikimediaImage} from "./UI/Image/WikimediaImage";
|
||||
import {OsmImageUploadHandler} from "./Logic/OsmImageUploadHandler";
|
||||
import {DropDownUI} from "./UI/DropDownUI";
|
||||
|
||||
const centerMessage = new UIEventSource<string>("");
|
||||
|
||||
const dryRun = true;
|
||||
// If you have a testfile somewhere, enable this to spoof overpass
|
||||
// This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
|
||||
Overpass.testUrl = "http://127.0.0.1:8080/test.json";
|
||||
// The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource
|
||||
const secondsTillChangesAreSaved = new UIEventSource<number>(0);
|
||||
|
||||
const leftMessage = new UIEventSource<() => UIElement>(undefined);
|
||||
|
||||
const selectedElement = new UIEventSource<any>(undefined);
|
||||
|
||||
const questSetToRender = KnownSet.groen;
|
||||
|
||||
const locationControl = new UIEventSource({
|
||||
zoom: questSetToRender.startzoom,
|
||||
lat: questSetToRender.startLat,
|
||||
lon: questSetToRender.startLon
|
||||
});
|
||||
|
||||
|
||||
// ----------------- Prepare the important objects -----------------
|
||||
|
||||
const saveTimeout = 5000; // After this many milliseconds without changes, saves are sent of to OSM
|
||||
const allElements = new ElementStorage();
|
||||
const osmConnection = new OsmConnection(dryRun);
|
||||
const changes = new Changes(
|
||||
"Beantwoorden van vragen met MapComplete voor vragenset #" + questSetToRender.name,
|
||||
osmConnection, allElements, centerMessage);
|
||||
|
||||
|
||||
const layer = questSetToRender.layers[0];
|
||||
const tags ={
|
||||
id: "way/123",
|
||||
// access: "yes",
|
||||
barrier: "fence",
|
||||
curator: "Arnout Zwaenepoel",
|
||||
description: "Heide en heischraal landschap met landduin en grote soortenverscheidenheid",
|
||||
dog: "no",
|
||||
email: "arnoutenregine@skynet.be",
|
||||
image: "https://natuurpuntbrugge.be/wp-content/uploads/2017/05/Schobbejakshoogte-schapen-PDG-1024x768.jpg",
|
||||
leisure: "nature_reserve",
|
||||
name: "Schobbejakshoogte",
|
||||
operator: "Natuurpunt Brugge",
|
||||
phone: "+32 50 82 26 97",
|
||||
website: "https://natuurpuntbrugge.be/schobbejakshoogte/",
|
||||
// wikidata: "Q4499623",
|
||||
wikipedia: "nl:Schobbejakshoogte",
|
||||
};
|
||||
const tagsES = allElements.addElement({properties: tags});
|
||||
|
||||
|
||||
/*
|
||||
new OsmImageUploadHandler(tagsES, osmConnection.userDetails, changes)
|
||||
.getUI().AttachTo("maindiv");
|
||||
|
||||
/*/
|
||||
|
||||
new FeatureInfoBox(
|
||||
tagsES,
|
||||
layer.elementsToShow,
|
||||
layer.questions,
|
||||
changes,
|
||||
osmConnection.userDetails
|
||||
).AttachTo("maindiv").Activate();
|
||||
//*/
|
||||
|
||||
|
Loading…
Reference in a new issue