CodeforKarlsruhe/farmshops.eu

Neue Datenstruktur für die Nodes

h8h opened this issue · 11 comments

h8h commented

Hi,

Bei einer fast schon 3MB farmshopGeoJson.js File sollten wir langsam nach einer Möglichkeit suchen, eine Nodes Query API zu bauen. Gerade für mobile Endgeräte die vllt. keinen Breitband Anschluss haben.

Mein Braindump dazu wäre:

  1. Simple API (mit GitHub Pages)
    Wollen wir erstmal die Seite nicht auf einen Server auslagern oder kein Backendserver benutzen, könnte man alles in eine Ordnerstruktur anlegen

Leaflet hat ein Beispiel Cluster, wo 10000 Nodes geclustert werden, die Json Datei dazu ist nur 380KB groß.

Deshalb würde ich für die Simple API, das Desgin genauso wählen, mit dem Zusatz welches property wir haben:

var addressPoints = [
[-37.8839, 175.3745188667, "relation/6800433","farm"],
[geo, geo, "id","property"],

Dann würde ich der id nach Ordner anlegen:
relation/6800433

in diese Ordner würde ich dann json files ablegen:
relation/6800433/detail.json

Wenn nun jmd die Karte lädt, lädt er erstmal die KB große farmshopGeoJson.js.

Wird nun auf ein PopUp geklickt, lädt er dann die Datei aus dem entsprechenden Ordner nach.

  1. Extend API
    Dazu bräuchte man dann einen Backenendserver und eine Datenbank.

Ich würde Elasticsearch favorisieren, da diese Daten im JSON Format annimmt und auch Informationen in Geo Koordinaten speichern und Suchabfragen mittels Boundingboxes möglich sind.

Da Elasticsearch eine REST API direkt anbietet, könnte man dann mit Ajax entsprechende Abfragen bauen.

Weitere Ideen?

Klingt super und lößt ein Problem dass momentan immer ernster wird. Das Importscript funktoniert im Moment nur noch jedes 3. oder 4. mal und ich vermute das liegt an der Masse der Daten und den maximal möglichen Timeout.

Wie würdest Du die Ordnerstruktur anlegen? Mit einem erweiterten Importscript oder komplett wie angerissen mit elastic search? Zu deinem Vorschlag würde ich gerne noch (falls vorhanden) die Öffnungszeiten hinzufügen, weil ich auf lange sicht gerne eine "jetzt gerade geöffnet" Funktion bauen möchte. sieh #24

var addressPoints = [
[geo, geo, "id", "property", "opening_hours"],

Weitere Ideen die ich zu dem Thema bis jetzt hatte:
-Die Daten stärker vorsortieren z.B. eine Query für die Länder Deutschland, Österreich und Schweiz und nicht ein großes Quadrat über Mitteleuropa
-unnötige Werte schon im Importscript löschen oder zusammenfassen (habe ich z.B. durch "out center;" schon ein wenig getan und jetzt keine Polygone mehr in den Daten)
-Variabelnamen nach import minifyen z.B. "a" statt "amenity" , bei 6000 Nodes müsste das die größe stark vermindern
-Die Daten für verschiedene Regionen in verschiedene Dateien packen und nur laden, wenn man sich innerhalb der Region in bestimmten zoomstufen aufhält

Bis jetzt fehlt mir für diese Dinge v.a. das Wissen in node und der overpass query language, sonst hätte ich das schon umgesetzt. Ich will mir das aber noch anlesen.

h8h commented

Das klingt super.

Also ich hab etwas rumgespielt am Import Script.

Es werden jetzt recht viele Ordner erstellt, aber ich denke das ist kein Problem. GitHub kann das bestimmt ab und man sieht nun die Diffs, anhand welche Ordner hinzu kommen oder gelöscht werden.

Mit dem vorgeschlagenen Format komm ich auf 380KB, wenn ich jedoch ein abgespecktes GeoJSON Format nehme, komm ich auf 780KB.

{
   "type" : "FeatureCollection",
   "features" : [
      {
         "geometry" : {
            "type" : "Polygon",
            "coordinates" : [
               [
                  [
                     7.8119761,
                     50.7449095
                  ],
                  [
                     7.8119761,
                     50.7449095
                  ]
               ]
            ]
         },
         "properties" : {
            "property" : "farm",
            "id" : "type/id"
         },
         "type" : "Feature"
      }
   ]
}


Beim GeoJSON-Format könnte man direkt L.geoJson nehmen, bei dem anderen Format müsste man mit einer for-Schleife nach Helfen.

Hier ist mein Beispiel

Todos:

  • Die Marker Icons müssten nun auf properties.proprety matchn, momentan wird nur der schwarze Marker angezeigt
  • Beim Klick auf einen Marker müsste dann per AJAX Request der Inhalt aus data/type/id/details.json nachladen, dazu fehlt in meinem Importskript noch die IDs in der farmshopGeoJson.js
  • ForEach Schleife verbessern, momentan parst er auch "FeatureCollection" nach data/unkown
  • GeoJSON - Format weiter abspecken, statt property -> p

Known-Bugs:

  • Falls es data/type/id/details.json schon gibt, wird der JSON Blob an die Datei angehängt. Kommt in den seltesten Fällen vor Beispiel

Zu deinem Import Problem allgemein:
Soweit ich mich eingelesen habe, könnte man auch einen anderen Weg einschlagen und zwar Planet Extracts herunterladen und dann mit einem Programm, wie bspw. Osmosis Abfragen ähnlich der OverPass API erstellen: osmosis --read-pbf \myregion.pbf --node-key-value keyValueList="highway.speed_camera" --write-xml radar.osm

Vorteil:

  • Regionengenaue Abfrage und Separierung

Großartige Arbeit! Ich schaue mir das später mal genauer an und pulle das hier in einen eigenen branch.

nown-Bugs:
• Falls es data/type/id/details.json schon gibt, wird der JSON Blob an die Datei angehängt. Kommt in den seltesten Fällen vor Beispiel

Könnte man um das zu verhindern nicht jedes Mal vor der query alle Ordner löschen?

Die eigene Abfrage auf Basis der Planet Geojson sieht interessant aus, man muss aber dafür viel downloaden und kann es deswegen nicht so leicht auf einen günstigen Server automatisieren. Anstelle der Planet Geojson möchte ich mir erst einmal die Overpass-api genauer anschauen, da gibt es auch Möglichkeiten nach Verwaltungsebenen zu selektieren.

Liegt jetzt im Branch " new-data-structure" ich habe auch gleich mal die Marker richtig auf die property matchen lassen. Sehr cool. ist wirklich sehr schnell und performant.

Nächster Schritt
popupcontent.js anpassen.

h8h commented

Super danke.

Ich versuch mich dann gleich mal am AJAX.

Ich denk dann bleiben wir erstmal auf dem GeoJSON Format, obwohl das doppelt so groß ist, wie das eingangs vorgeschlagene, aber dann haben wir erstmal weniger Arbeit.

Könnte man um das zu verhindern nicht jedes Mal vor der query alle Ordner löschen?

Nein leider nicht, der Ordner wird bereits jedes Mal gelöscht. Es gibt momentan tatsächlich eine Node, die die gleiche ID hat, vllt. auch ein OSM Bug, den wir reporten sollten.

Der Link dazu steht oben.

Sehr cool danke. Ja ich schaue mal nach wo man so einen bug am besten reportet.

Wenn Du keine Lust hast alle variablen in der großen Datei auszutauschen kannst Du auch gerne nur den Ansatz bauen und ich übernehme die Fleißarbeit. Das Meiste müsste man theoretisch mit suchen und ersetzen austauschen können.

h8h commented

So, war gar nicht so schwer.

Bei mir kommt noch im Browser eine Warnung:
XML Parsing Error: not well-formed Location: file:///home/.../direktvermarkter/data/node/2446898045/details.json Line Number 1, Column 1:

Aber es klappt trotzdem. Ich vermute, das der MIME-Type nicht matchn tut, wenn ich die Seite lokal aufrufe.

Ist jetzt seit heute mittag auf Master und bis jetzt ist mir nichts aufgefallen. Würde das Ticket dann schließen, oder siehst Du noch todos?

h8h commented

Passt, schließen wir erstmal. Dank dir. Den Rest nehmen wir als Bugs auf.

Als Known-Bug kenne ich nur:

  • Import läuft
  • Import löscht alle vorhandenen Ordner und Dateien
  • Import queried die Overpass API
  • Import geht jede ID durch und legt für Nodes, Way, Relation Ordner und details.json an
  • Import geht nächste ID durch und bemerkt das der Ordner bereits existiert und append die Daten zu details.json

Kommt exterm selten vor, ich durchsuch das mit grep -r }{.

Ich hab auch schonmal das Node auf der Karte gesucht, aber nicht gefunden.

In solchen Fällen weiß ich nicht wie der Ajax Load klappt, da es eig. ungültiges JSON ist.

data/way/236696651/details.json:{"type":"Feature","id":"way/236696651","properties":{"addr:city":"Gebhardshain","addr:country":"DE","addr:housenumber":"42","addr:postcode":"57580","addr:street":"Liebergstraße","building":"yes","email":"bauer-hoffmann@t-online.de","fax":"+49 2747 3640","name":"Bauernhof Hoffmann","phone":"+49 2747 3517","shop":"farm","id":"way/236696651"},"geometry":{"type":"Polygon","coordinates":[[[7.8119761,50.7449095],[7.8119761,50.7449095]]]}}{"type":"Feature","id":"way/236696651","properties":{"addr:city":"Gebhardshain","addr:country":"DE","addr:housenumber":"42","addr:postcode":"57580","addr:street":"Liebergstraße","building":"yes","email":"bauer-hoffmann@t-online.de","fax":"+49 2747 3640","name":"Bauernhof Hoffmann","phone":"+49 2747 3517","shop":"farm","id":"way/236696651"},"geometry":{"type":"Point","coordinates":[7.8120688,50.7447609]}}

Hey eine Sache noch, ich bekomme immer wieder diesen Fehler, kann aber nicht genau reproduzieren warum und wann. Ich habe nur mit unterschiedlichen Querys herumgespielt:

fs.js:646
  return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
                 ^

Error: ENOENT: no such file or directory, open 'data/farmshopGeoJson.js'
    at Object.fs.openSync (fs.js:646:18)
    at Object.fs.writeFileSync (fs.js:1299:33)
    at query_overpass (/Users/sgrotz/Downloads/direktvermarkter/update_data.js:127:6)
    at Request.<anonymous> (/Users/sgrotz/Downloads/direktvermarkter/node_modules/query-overpass/index.js:43:28)
    at emitOne (events.js:116:13)
    at Request.emit (events.js:211:7)
    at Request.onRequestResponse (/Users/sgrotz/Downloads/direktvermarkter/node_modules/request/request.js:1062:10)
    at emitOne (events.js:116:13)
    at ClientRequest.emit (events.js:211:7)
    at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:551:21)

Nur kurzere Querys ohne Marktplätze funktionieren zuverlässig.

EDIT:höchstwahrscheinlich ein lokales node Problem bei mir

vorerst erledigt, weitere verbesserungen im eigenen Issues