Lot's of small improvements

This commit is contained in:
Pieter Vander Vennet 2020-06-29 03:12:44 +02:00
parent 9bd37d9cde
commit 8bca006787
29 changed files with 375 additions and 173 deletions

View file

@ -73,5 +73,5 @@ export class Helpers {
});
}
}

View file

@ -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,

View file

@ -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,25 +58,24 @@ 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',
hideSingleBase: true
})
layerControl.addTo(this.map);
this.map.zoomControl.setPosition("bottomright");
const self = this;
@ -82,11 +83,13 @@ export class Basemap {
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})
});
}

View file

@ -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
});
}
});

View file

@ -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 {

View file

@ -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))

View file

@ -10,8 +10,9 @@ export class UserDetails {
public img: string;
public unreadMessages = 0;
public totalMessages = 0;
public osmConnection : OsmConnection;
public dryRun : boolean;
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 = {};

View 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;
}
})
}
}

View file

@ -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

View file

@ -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
View 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();
}
}
}

View file

@ -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 {

View file

@ -1,4 +1,4 @@
import {UIElement} from "./UIElement";
import {UIElement} from "../UIElement";
export class FixedUiElement extends UIElement {
private _html: string;

View file

@ -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"

View file

@ -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>;

View file

@ -1,4 +1,4 @@
import {UIElement} from "./UIElement";
import {UIElement} from "../UIElement";
export class VerticalCombine extends UIElement {
private _elements: UIElement[];

View file

@ -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 {

View file

@ -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 {

View file

@ -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
View 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();
}
}
}

View file

@ -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;
}

View file

@ -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
View 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
View 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
View 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
View 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

View file

@ -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;

View file

@ -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;
@ -27,7 +28,7 @@ if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
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";
Overpass.testUrl = "http://127.0.0.1:8080/test.json";
}
@ -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,42 +131,53 @@ 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
*/
selectedElement.addCallback((data) => {
// Which is the applicable set?
for (const layer of questSetToRender.layers) {
// Which is the applicable set?
for (const layer of questSetToRender.layers) {
const applicable = layer.overpassFilter.matches(TagUtils.proprtiesToKV(data));
if (applicable) {
// This layer is the layer that gives the questions
leftMessage.setData(() =>
new FeatureInfoBox(
allElements.getElement(data.id),
layer.elementsToShow,
layer.questions,
changes,
osmConnection.userDetails
));
break;
}
const applicable = layer.overpassFilter.matches(TagUtils.proprtiesToKV(data));
if (applicable) {
// This layer is the layer that gives the questions
leftMessage.setData(() =>
new FeatureInfoBox(
allElements.getElement(data.id),
layer.elementsToShow,
layer.questions,
changes,
osmConnection.userDetails
));
break;
}
}
}
);
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
View file

@ -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();
//*/