forked from MapComplete/MapComplete
Further work on infobox, styling everything, removing clutter
This commit is contained in:
parent
2acd53d150
commit
0b4016b65d
48 changed files with 1283 additions and 454 deletions
|
@ -58,21 +58,36 @@ export class Basemap {
|
|||
|
||||
|
||||
constructor(leafletElementId: string, location: UIEventSource<{ zoom: number, lat: number, lon: number }>) {
|
||||
this. map = L.map(leafletElementId, {
|
||||
this.map = L.map(leafletElementId, {
|
||||
center: [location.data.lat, location.data.lon],
|
||||
zoom: location.data.zoom,
|
||||
layers: [this.osmLayer]
|
||||
layers: [this.osmLayer],
|
||||
attributionControl: false
|
||||
});
|
||||
|
||||
L.control.attribution({
|
||||
position: 'bottomleft'
|
||||
}).addTo(this.map);
|
||||
|
||||
this.Location = location;
|
||||
L.control.layers(this.baseLayers).addTo(this.map);
|
||||
const layerControl = L.control.layers(this.baseLayers, null,
|
||||
{
|
||||
position: 'bottomleft',
|
||||
hideSingleBase: true
|
||||
})
|
||||
layerControl.addTo(this.map);
|
||||
|
||||
this.map.zoomControl.setPosition("bottomleft");
|
||||
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.ping();
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,16 +16,23 @@ export class Changes {
|
|||
private readonly login: OsmConnection;
|
||||
public readonly _allElements: ElementStorage;
|
||||
|
||||
public _pendingChanges: { elementId: string, key: string, value: string }[] = []; // Gets reset on uploadAll
|
||||
private _pendingChanges: { elementId: string, key: string, value: string }[] = []; // Gets reset on uploadAll
|
||||
private newElements: OsmObject[] = []; // Gets reset on uploadAll
|
||||
|
||||
public readonly pendingChangesES = new UIEventSource(this._pendingChanges);
|
||||
private readonly centerMessage: UIEventSource<string>;
|
||||
public readonly pendingChangesES = new UIEventSource<number>(this._pendingChanges.length);
|
||||
public readonly isSaving = new UIEventSource(false);
|
||||
private readonly _changesetComment: string;
|
||||
private readonly _centerMessage: UIEventSource<string>;
|
||||
|
||||
constructor(login: OsmConnection, allElements: ElementStorage, centerMessage: UIEventSource<string>) {
|
||||
constructor(
|
||||
changesetComment: string,
|
||||
login: OsmConnection,
|
||||
allElements: ElementStorage,
|
||||
centerMessage: UIEventSource<string>) {
|
||||
this._changesetComment = changesetComment;
|
||||
this.login = login;
|
||||
this._allElements = allElements;
|
||||
this.centerMessage = centerMessage;
|
||||
this._centerMessage = centerMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,15 +44,15 @@ export class Changes {
|
|||
addChange(elementId: string, key: string, value: string) {
|
||||
|
||||
if (!this.login.userDetails.data.loggedIn) {
|
||||
this.centerMessage.setData(
|
||||
this._centerMessage.setData(
|
||||
"<p>Bedankt voor je antwoord!</p>" +
|
||||
"<p>Gelieve <span class='activate-osm-authentication'>in te loggen op OpenStreetMap</span> om dit op te slaan.</p>"+
|
||||
"<p>Gelieve <span class='activate-osm-authentication'>in te loggen op OpenStreetMap</span> om dit op te slaan.</p>" +
|
||||
"<p>Nog geen account? <a href=\'https://www.openstreetmap.org/user/new\' target=\'_blank\'>Registreer hier</a></p>"
|
||||
);
|
||||
const self = this;
|
||||
this.login.userDetails.addCallback(() => {
|
||||
if (self.login.userDetails.data.loggedIn) {
|
||||
self.centerMessage.setData("");
|
||||
self._centerMessage.setData("");
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
@ -67,7 +74,7 @@ export class Changes {
|
|||
eventSource.ping();
|
||||
// We get the id from the event source, as that ID might be rewritten
|
||||
this._pendingChanges.push({elementId: eventSource.data.id, key: key, value: value});
|
||||
this.pendingChangesES.ping();
|
||||
this.pendingChangesES.setData(this._pendingChanges.length);
|
||||
|
||||
|
||||
}
|
||||
|
@ -114,9 +121,18 @@ export class Changes {
|
|||
public uploadAll(optionalContinuation: (() => void)) {
|
||||
const self = this;
|
||||
|
||||
this.isSaving.setData(true);
|
||||
const optionalContinuationWrapped = function () {
|
||||
self.isSaving.setData(false);
|
||||
if (optionalContinuation) {
|
||||
optionalContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const pending: { elementId: string; key: string; value: string }[] = this._pendingChanges;
|
||||
this._pendingChanges = [];
|
||||
this.pendingChangesES.setData(this._pendingChanges);
|
||||
this.pendingChangesES.setData(this._pendingChanges.length);
|
||||
|
||||
const newElements = this.newElements;
|
||||
this.newElements = [];
|
||||
|
@ -203,7 +219,7 @@ export class Changes {
|
|||
|
||||
console.log("Beginning upload...");
|
||||
// At last, we build the changeset and upload
|
||||
self.login.UploadChangeset("Updaten van metadata met Mapcomplete",
|
||||
self.login.UploadChangeset(self._changesetComment,
|
||||
function (csId) {
|
||||
|
||||
let modifications = "";
|
||||
|
@ -243,7 +259,7 @@ export class Changes {
|
|||
return changes;
|
||||
},
|
||||
handleMapping,
|
||||
optionalContinuation);
|
||||
optionalContinuationWrapped);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,6 @@ export class FilteredLayer {
|
|||
private readonly _removeContainedElements;
|
||||
private readonly _removeTouchingElements;
|
||||
|
||||
private readonly _popupContent: ((source: UIEventSource<any>) => UIElement);
|
||||
|
||||
private readonly _style: (properties) => any;
|
||||
|
||||
private readonly _storage: ElementStorage;
|
||||
|
@ -41,6 +39,7 @@ export class FilteredLayer {
|
|||
* The leaflet layer object which should be removed on rerendering
|
||||
*/
|
||||
private _geolayer;
|
||||
private _selectedElement: UIEventSource<any>;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
|
@ -49,8 +48,9 @@ export class FilteredLayer {
|
|||
filters: TagsFilter,
|
||||
removeContainedElements: boolean,
|
||||
removeTouchingElements: boolean,
|
||||
popupContent: ((source: UIEventSource<any>) => UIElement),
|
||||
style: ((properties) => any)) {
|
||||
style: ((properties) => any),
|
||||
selectedElement: UIEventSource<any>) {
|
||||
this._selectedElement = selectedElement;
|
||||
|
||||
if (style === undefined) {
|
||||
style = function () {
|
||||
|
@ -60,7 +60,6 @@ export class FilteredLayer {
|
|||
this.name = name;
|
||||
this._map = map;
|
||||
this.filters = filters;
|
||||
this._popupContent = popupContent;
|
||||
this._style = style;
|
||||
this._storage = storage;
|
||||
this._removeContainedElements = removeContainedElements;
|
||||
|
@ -167,8 +166,6 @@ export class FilteredLayer {
|
|||
},
|
||||
|
||||
pointToLayer: function (feature, latLng) {
|
||||
|
||||
const eventSource = self._storage.addOrGetElement(feature);
|
||||
const style = self._style(feature.properties);
|
||||
let marker;
|
||||
if (style.icon === undefined) {
|
||||
|
@ -180,19 +177,6 @@ export class FilteredLayer {
|
|||
});
|
||||
}
|
||||
|
||||
eventSource.addCallback(function () {
|
||||
self.updateStyle();
|
||||
});
|
||||
const content = self._popupContent(eventSource)
|
||||
marker.bindPopup(
|
||||
"<div class='popupcontent'>" +
|
||||
content.Render() +
|
||||
"</div>"
|
||||
).on("popupopen", function () {
|
||||
content.Activate();
|
||||
content.Update();
|
||||
});
|
||||
|
||||
return marker;
|
||||
},
|
||||
|
||||
|
@ -203,14 +187,9 @@ export class FilteredLayer {
|
|||
eventSource.addCallback(function () {
|
||||
self.updateStyle();
|
||||
});
|
||||
const content = self._popupContent(eventSource)
|
||||
layer.bindPopup(
|
||||
"<div class='popupcontent'>" +
|
||||
content.Render() +
|
||||
"</div>"
|
||||
).on("popupopen", function () {
|
||||
content.Activate();
|
||||
content.Update();
|
||||
layer.on("click", function(){
|
||||
console.log("Selected ",feature)
|
||||
self._selectedElement.setData(feature.properties);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -34,7 +34,9 @@ export class ImageSearcher extends UIEventSource<string[]> {
|
|||
self.AddImage(wd.image);
|
||||
Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => {
|
||||
for (const image of images.images) {
|
||||
self.AddImage(image.filename);
|
||||
if (image.startsWith("File:")) {
|
||||
self.AddImage(image);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -48,7 +50,10 @@ export class ImageSearcher extends UIEventSource<string[]> {
|
|||
if (commons.startsWith("Category:")) {
|
||||
Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => {
|
||||
for (const image of images.images) {
|
||||
self.AddImage(image.filename);
|
||||
// @ts-ignore
|
||||
if (image.startsWith("File:")) {
|
||||
self.AddImage(image);
|
||||
}
|
||||
}
|
||||
})
|
||||
} else { // @ts-ignore
|
||||
|
@ -125,7 +130,7 @@ export class ImageSearcher extends UIEventSource<string[]> {
|
|||
const urlSource = new UIEventSource<string>(url);
|
||||
// @ts-ignore
|
||||
if (url.startsWith("File:")) {
|
||||
return new WikimediaImage(urlSource);
|
||||
return new WikimediaImage(urlSource.data);
|
||||
} else {
|
||||
return new SimpleImageElement(urlSource);
|
||||
}
|
||||
|
|
|
@ -57,9 +57,13 @@ export class LayerUpdater {
|
|||
}
|
||||
|
||||
private handleFail(reason: any) {
|
||||
this.runningQuery.setData(false);
|
||||
console.log("QUERY FAILED", reason);
|
||||
// TODO
|
||||
console.log("Retrying in 1s")
|
||||
this.previousBounds = undefined;
|
||||
const self = this;
|
||||
window.setTimeout(
|
||||
function(){self.update()}, 1000
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,7 +93,7 @@ export class LayerUpdater {
|
|||
|
||||
}
|
||||
|
||||
buildBboxFor(): string {
|
||||
private buildBboxFor(): string {
|
||||
const b = this._map.map.getBounds();
|
||||
const latDiff = Math.abs(b.getNorth() - b.getSouth());
|
||||
const lonDiff = Math.abs(b.getEast() - b.getWest());
|
||||
|
@ -101,8 +105,7 @@ export class LayerUpdater {
|
|||
|
||||
this.previousBounds = {north: n, east: e, south: s, west: w};
|
||||
|
||||
const bbox = "[bbox:" + s + "," + w + "," + n + "," + e + "]";
|
||||
return bbox;
|
||||
return "[bbox:" + s + "," + w + "," + n + "," + e + "]";
|
||||
}
|
||||
|
||||
private IsInBounds(): boolean {
|
||||
|
|
|
@ -9,6 +9,9 @@ export class UserDetails {
|
|||
public csCount = 0;
|
||||
public img: string;
|
||||
public unreadMessages = 0;
|
||||
public totalMessages = 0;
|
||||
public osmConnection : OsmConnection;
|
||||
public dryRun : boolean;
|
||||
|
||||
}
|
||||
|
||||
|
@ -26,10 +29,9 @@ export class OsmConnection {
|
|||
|
||||
constructor(dryRun: boolean) {
|
||||
this.userDetails = new UIEventSource<UserDetails>(new UserDetails());
|
||||
this.userDetails.data.osmConnection = this;
|
||||
this.userDetails.data.dryRun = dryRun;
|
||||
this._dryRun = dryRun;
|
||||
if(dryRun){
|
||||
alert("Opgelet: testmode actief. Wijzigingen worden NIET opgeslaan")
|
||||
}
|
||||
|
||||
if (this.auth.authenticated()) {
|
||||
this.AttemptLogin(); // Also updates the user badge
|
||||
|
@ -61,23 +63,43 @@ export class OsmConnection {
|
|||
self.userDetails.ping();
|
||||
}
|
||||
|
||||
if(details == null){
|
||||
if (details == null) {
|
||||
return;
|
||||
}
|
||||
// details is an XML DOM of user details
|
||||
let userInfo = details.getElementsByTagName("user")[0];
|
||||
|
||||
// let moreDetails = new DOMParser().parseFromString(userInfo.innerHTML, "text/xml");
|
||||
|
||||
let data = self.userDetails.data;
|
||||
data.loggedIn = true;
|
||||
console.log(userInfo);
|
||||
data.name = userInfo.getAttribute('display_name');
|
||||
data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count");
|
||||
data.img = userInfo.getElementsByTagName("img")[0].getAttribute("href");
|
||||
data.unreadMessages = userInfo.getElementsByTagName("received")[0].getAttribute("unread");
|
||||
const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0];
|
||||
data.unreadMessages = parseInt(messages.getAttribute("unread"));
|
||||
data.totalMessages = parseInt(messages.getAttribute("count"));
|
||||
self.userDetails.ping();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* All elements with class 'activate-osm-authentication' are loaded and get an 'onclick' to authenticate
|
||||
* @param osmConnection
|
||||
*/
|
||||
registerActivateOsmAUthenticationClass() {
|
||||
|
||||
const authElements = document.getElementsByClassName("activate-osm-authentication");
|
||||
for (let i = 0; i < authElements.length; i++) {
|
||||
let element = authElements.item(i);
|
||||
// @ts-ignore
|
||||
element.onclick = function () {
|
||||
this.AttemptLogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static parseUploadChangesetResponse(response: XMLDocument) {
|
||||
const nodes = response.getElementsByTagName("node");
|
||||
const mapping = {};
|
||||
|
@ -102,6 +124,7 @@ export class OsmConnection {
|
|||
console.log("NOT UPLOADING as dryrun is true");
|
||||
var changesetXML = generateChangeXML("123456");
|
||||
console.log(changesetXML);
|
||||
continuation();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,29 +9,26 @@ import {UserDetails} from "./OsmConnection";
|
|||
export class OsmImageUploadHandler {
|
||||
private _tags: UIEventSource<any>;
|
||||
private _changeHandler: Changes;
|
||||
private _userdetails: UserDetails;
|
||||
private _userdetails: UIEventSource<UserDetails>;
|
||||
|
||||
constructor(tags: UIEventSource<any>,
|
||||
userdetails: UserDetails,
|
||||
userdetails: UIEventSource<UserDetails>,
|
||||
changeHandler: Changes
|
||||
) {
|
||||
if (tags === undefined || userdetails === undefined || changeHandler === undefined) {
|
||||
throw "Something is undefined"
|
||||
}
|
||||
console.log(tags, changeHandler, userdetails)
|
||||
this._tags = tags;
|
||||
this._changeHandler = changeHandler;
|
||||
this._userdetails = userdetails;
|
||||
}
|
||||
|
||||
private generateOptions(license: string) {
|
||||
console.log(this)
|
||||
console.log(this._tags, this._changeHandler, this._userdetails)
|
||||
const tags = this._tags.data;
|
||||
|
||||
const title = tags.name ?? "Unknown area";
|
||||
const description = [
|
||||
"author:" + this._userdetails.name,
|
||||
"author:" + this._userdetails.data.name,
|
||||
"license:" + license,
|
||||
"wikidata:" + tags.wikidata,
|
||||
"osmid:" + tags.id,
|
||||
|
@ -60,7 +57,9 @@ export class OsmImageUploadHandler {
|
|||
|
||||
getUI(): ImageUploadFlow {
|
||||
const self = this;
|
||||
return new ImageUploadFlow(function (license) {
|
||||
return new ImageUploadFlow(
|
||||
this._userdetails,
|
||||
function (license) {
|
||||
return self.generateOptions(license)
|
||||
}
|
||||
);
|
||||
|
|
|
@ -26,8 +26,7 @@ export class Overpass {
|
|||
const query =
|
||||
'[out:json][timeout:25]' + bbox + ';(' + filter + ');out body;>;out skel qt;';
|
||||
console.log(query);
|
||||
const url = "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query);
|
||||
return url;
|
||||
return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ export class QuestionUI extends UIElement {
|
|||
const embeddedScriptSave = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", false )';
|
||||
const embeddedScriptSkip = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", true )';
|
||||
const saveButton = "<input class='save-button' type='button' onclick='" + embeddedScriptSave + "' value='Opslaan' />";
|
||||
const skip = "<input class='skip-button' type='button' onclick='" + embeddedScriptSkip + "' value='Ik ben het niet zeker (vraag overslaan)' />";
|
||||
const skip = "<input class='skip-button' type='button' onclick='" + embeddedScriptSkip + "' value='Ik ben niet zeker (vraag overslaan)' />";
|
||||
return q.question + "<br/> " + answers + saveButton + skip;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,8 +68,7 @@ export class Wikimedia {
|
|||
|
||||
for (const member of members) {
|
||||
|
||||
imageOverview.images.push(
|
||||
{filename: member.title, fileid: member.pageid});
|
||||
imageOverview.images.push(member.title);
|
||||
}
|
||||
if (response.continue === undefined || alreadyLoaded > 30) {
|
||||
handleCategory(imageOverview);
|
||||
|
@ -96,7 +95,10 @@ export class Wikimedia {
|
|||
wd.commonsWiki = commons?.title;
|
||||
|
||||
// P18 is the claim 'depicted in this image'
|
||||
wd.image = "File:" + entity.claims.P18?.[0]?.mainsnak?.datavalue?.value;
|
||||
const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value;
|
||||
if (image) {
|
||||
wd.image = "File:" + image;
|
||||
}
|
||||
handleWikidata(wd);
|
||||
});
|
||||
}
|
||||
|
@ -114,7 +116,7 @@ export class Wikidata {
|
|||
|
||||
export class ImagesInCategory {
|
||||
// Filenames of relevant images
|
||||
images: { filename: string, fileid: number }[] = [];
|
||||
images: string[] = [];
|
||||
}
|
||||
|
||||
export class LicenseInfo {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue