Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddOn : Geo #124

Closed
tbaddade opened this issue Jun 8, 2020 · 18 comments
Closed

AddOn : Geo #124

tbaddade opened this issue Jun 8, 2020 · 18 comments

Comments

@tbaddade
Copy link
Member

tbaddade commented Jun 8, 2020

Allgemeines AddOn für Geo-Locations.

  • Openstreetmap (Leaflet) oder Googlemap als Plugin oder wie bei YFeed eine Art Stream registrieren
  • AddOns zusammenlegen
  • Proxy von https://github.com/FriendsOfREDAXO/experimental testen und ggf. Abhängigkeit zum Plugin setzen (wenn das getestet wurde, kann es ggf. in den Core einfließen)
  • Latitude und Longitude über Adresse bei Usereingabe ermitteln (YForm Datensatz, Modul)
  • eigenes YForm-Value registrieren (ein Feld, Daten kommasepariert speichern), YForm-Value google_geocode könnte dann gelöscht werden
  • Modul-Beispiel für die Ausgabe der Karte(n) im Frontend.
  • Umrechnen der Koordinaten
    • UTM
    • Dezimalgrad: dd.ddddd°
    • Grad, Dezimalminuten: dd° mm.mmm'
    • Grad, Minuten, Sekunden: dd° mm' ss.s''
  • ...

Edit: Der AddOn Key geo wurde eben registriert

@alxndr-w
Copy link
Member

alxndr-w commented Jun 8, 2020

  • Modul-Beispiel für die Ausgabe der Karte(n) im Frontend.
  • Reverse-Suche anhand von Geocode die Straße/PLZ/Ort ermitteln

@christophboecker
Copy link
Member

Ich hatte mal mit sowas angefangen, basierend ausschließlich auf Leaflet, bin aber an der JS-Komplexität gescheitert. Ziel war nämlich Flexibilität und Erweiterbarkeit. Hier mal ein paar meiner Ideen:

Einbindung einer Map an so vielen Stellen wie möglich im System

Die Karte soll vom Look&Feel her immer identisch aussehen; die Daten im Datenfeld immer gleich aufgebaut sein. Die Einbindung in eigene Lösungen sollte einfach sein, daher bietet sich an, die Karte über ein rex_fragment zu erzeugen. Darauf aufbauend kann sie in verschiedensten Kontexten eingebaut werden:

  • Als REX_VAR in Modulen und Templates
  • rex_form-Custom-Value
  • yform-Value
  • Metainfo-Value

MForm unterstütze derzeit keine extern bereitgestellten Datentypen, aber das kann ja über das Fragment leicht innerhalb von MForm gelöst werden.

Datenspeicherung

Nur Lat/Lon zu speichern reicht m.E. nicht aus. Da ist so wie mit dem Fokuspunkt. Hat man mehrere Locations und zeigt sie alle initial mit demselben Zoom-Faktor an, ist das fast immer ungünstig. Ein Zoom-Faktor, der die Position einer Location in einer Kleinstadt gut darstellt, ist bei Berlin viel zu detailliert; man sieht dann nicht mehr "wo" in Berlin das ist.

Hier mal als Beispiel die Adresse des ASV Berlin, Veranstalter der Havel-Klassik. Meim ersten Ausschnitt sieht man sehr gut, wo in Berlin die Sause starte, beim zweiten nicht. Der Marker muss auch nicht zwingend in der Kartenmitte sein.
grafik

grafik

Wobei der Zoon-Faktor auch nicht der tolle Wert ist; ein frei wählbares Rechteck wäre besser.

Deshalb war meine Idee, einen Datensatz zu speichern (JSON-String) mit mehr oder weniger Informationen, wobei das JS die Karte dann dem entsprechend aufbaut.

{
    bounds: [ lat_NW,lon_NW,lat_SO,lon_SO],
    position: [lat,lon], 
}

Das wäre flexibel um zusätzliche Features erweiterbar; im Gegensatz zu einem nur kommaseparierten Feld.

Den eigenen Einwand, dass es dann zu komplexe Daten wären, habe ich mir verworfen. Den Zugriff auf PHP-Ebene kann man ja über eine Zugriffsklasse erleichtern inkl. Ausgabe der Koordinaten in verschiedenen Darstellungsweisen und die Umformung. Für JS gilt ähnliches.

Karten-Konfiguration ("Map-Connector" bzw. "Kartensatz")

Dahinter steckt die Idee, dass die meisten Anbieter ja mehrere Kartenvarianten anbieten (klassisch, Satelitenbild, ...). Leaflet kann man sehr einfach mehrere Tile-Ebenen (Layer) beibringen, die dann per Button-Leiste rechts ausgewählt werden.

Von Plugins je Anbieter bin ich schnell abgegangen. Es schien mir flexibler, den Admin der DB die verschiedenen Karten al gusto zusammenzustellen und in einer DB-Tabellen zu speichern.

Im Backend sollte der Admin solche "Kartensätze" zusammenstellen, daraus würde je Kartensatz eine JS-Klasse (Geo.Map.xyz) generiert und dem zu ladenden JS hinzugefügt. In der Feldkonfiguration wäre der "Kartensatz" einer der auszuwählenden Parameter, der an das rex_fragment übergeben wird. Das JS ruft beim Aufbau der Karte die passende Klasse ab und konfiguriert sich darüber die Kartenansicht. (Fallback auf Geo.Map.Default = OSM)

Das wäre so ähnlich wie beim Medien-Manager:

  • Definiere in Tabelle "Karte/Layer" die Kartenansicht
  • Definiere in Tabelle "Kartensatz" die gewünsche Kombination mehrere Karten

grafik

Proxy

Basierend auf dem Obigen würde der jeweilige Map-Connector entweder die Original-URLs enthalten oder eben über den Proxy geleitet. In dem Fall bekommt der MapConnector eine URL, die neben den Variablem {x}, {y}, {z} auch die ID des URL-Datenstzes enthält. Daraus baut der Proxy die Abfrage-URL auf und ruft die Karte ab.

Falls sie schon im Proxy-Cache ist und nicht zu alt, wird sie von dort ausgeliefert.

Der Experimental-Proxy hat wenn ich nicht irre einen etwas anderen Ansatz

Adressauflösung

Soll eine Suche innerhalb der Karte möglich sein, also mit einem karteninternen Eingabefeld? Dann stellt sich noch die Frage, ob nur im Backend oder ggf. auch im Frontend.

Die andere Variante ist die Auflösung von Adressen, deren Bestandteile in anderen Felder des Formulars zu finden sind.

Nicht von der Hand zu weisen ist die 2-Wege Lösung. Suche per Suchfeld in der Karte und Übertragen der Adresse in die Felder des Formulars. Die Rückmeldung des Kartenabieters ist ein JSON-Array, das zumindest rudimentär standardisiert ist. Z.B. als Rechts-Click auf einen Marker und dann im Context-Menü "Adresse übernehmen"

Vieleicht ließe sich eine Art "Form-Connectors" schaffen. In der Feldkonfiguration werden die übrigen Felder ausgewählt, die Adressteile enthalten und denen wird zugeordnet, welche Teile wie einlaufen. Beispiel: Feld "ort" enthält "PLZ ORT", so dass Plz+" "+Ort dorthin übernommen werden oder eben passend ausgelesen. Auf JS-Ebene müssten die Felder IDs haben, die könnten ja beim Feldaufbau ausgelesen werden.

Meine Erfahrungen mit der OSM-Suche waren übrigens ziemlich enttäuschend. Und die Here-Suche machte Zicken wenn sie über den Proxy lief ("kommt nicht vom Browser - abgelehnt")

YForm-Suchfunktion

Das ist ein kniffeliges Thema. Soll nach Koordinaten oder nach Adressen gesucht werden. Wahrscheinlich geht es nur mit Umkreissuche.
Adressen müssen erst einmal aufgelöst werden in Koordinaten. Was wenn sie nicht eindeutig ist?

Hat man sie muss eine DB-Suche erfolgen, bei der die vorhandene Koordinate geprüft wird, ob sie in einer Bestimmten Fläsche liegt. Wie macht man das? Eine Bogensekunde auf dem Längengrad (also Richtung SN) ist immergleich lang (1 sm). Eine Bogensekunde auf dem Breitengrad ist am Äquator ebenfalls 1 sm lang. geht man Richtung S oder N wird es immer weniger bis 0 an den Polen. Gibt es bestimmt Formeln für .... hoffentlich.

Erweiterbarkeit

Das obige Konzept der Speicherung könnte ermöglichen auf JS-Ebene "Tools" bereitzustellen

Jedes Tool baut sich unter seinem Namen in den Datensatz ein. Obiger Datensatz basiert alo auf den Tools Geo.Tool.Pos und Geo.Tools.Bounds. Das ließe sich erweitern durch weitere Tools wie z.B. Geo.Tools.Marker (ermöglicht eine beliebige Zahl Marker einzufügen).

Jedes Tool müsste zwei Fähigkeiten haben: die eigenen Daten aus dem Datensatz anzeigen und Erfassen/Editieren der eigenen Daten und vor dem Speichern in den Datensatz schreiben.

Nun mag es Situationen geben, in denen nicht jedes Tool auch seine Sachen anzeigen oder bearbeiten soll. Das müsste in der Felddefnition eingesteuert werden (Anzeige-Tools: ..., Edit-Tools: ...)

Routenplaner etc. / Karten-App auf dem Mobildevice

Keine Ahnung wie das mit so einem Leaflet-Ansatz funktioniert.

@tbaddade
Copy link
Member Author

Vielen Dank für deinen Input @christophboecker

Kurz zur Datenspeicherung
Wenn ich dich richtig verstehe, willst du in einem Feld verschiedene Werte als Json abspeichern. Ich würde bevorzugen, dass selbst Lat und Long in separate DB-Felder landen. Ein Erklärung dazu hat letztens @akrys hier notiert.

Ich würde da erst einmal anfangen wollen, sodass das Addon mit YForm zusammen passt und die Tiles via Proxy abgeholt werden können.

Mich würde jetzt noch das interessieren:

Dahinter steckt die Idee, dass die meisten Anbieter ja mehrere Kartenvarianten anbieten (klassisch, Satelitenbild, ...). Leaflet kann man sehr einfach mehrere Tile-Ebenen (Layer) beibringen, die dann per Button-Leiste rechts ausgewählt werden.

Betreibst du einen eigenen Tile-Server? Oder wie handhabt ihr das derzeit? Welche Tile-Server nutzt ihr?

@christophboecker
Copy link
Member

Zur Datenspeicherung: es würde bedeuten, dass egal ob als Metafeld, REX_VAR, YForm, rex_form oder MForm die Bilddaten über x Felder gestreut sind und wieder für die Karte zusammengeführt werden müssen. Jedes davon ist anders gestrickt
Das zu bedienen halte ich für um Längen komplizierter als die "Kartenbeschreibung" wo nötig auszulesen, was ja durch Klassen auf JS- und PHP-Seite sehr vereinafcht werden kann.

Außerdem stelle ich mir die Frage, was häufiger vorkommt .... für @akrys ist das wohl wichtig, für jemanden wie mich sind die Einzelwerte in wohl 95% der Fälle egal bzw. wenn dann einfach auslesbar. Und über die gesamte Community betrachtet wird es wohl recht ähnlich sein (klar über 50% brauche es meist nicht). Just my 2 cents.

Was Axels Argumente bzgl. MYSQL angelangt: JSON kann man auslesen; und für komplexe Sachen kann man ja mal über stored procedures nachdenken (beim Install mit installieren). Oder über Methoden zur Lieferung vordefinierter WHERE-Klauseln (ähnlich YForm-Suche).

Außerdem verweist Axel ja selbst darauf dass seine Überlegungen für "ohne Geo-Addon" sind.

@christophboecker
Copy link
Member

Zum Thema Tile-Layer: nein ich betreibe hier nichts, hab nur mal erfolglos experiementiert. ich kann aber meine Erfahrunge teilen.

Die Karte ist in Leaflet ja ersteinmal eine Hülle bzw. eine leere Fläche.

let mapOptions = {
    minZoom:3,
    maxZoom:18,
    scrollWheelZoom:true,
}

// Karte anlegen (ohne Inhalt)
var map = L.map( 'map-1', mapOptions );

und ergibt sowas:
grafik

Auf Basis eines Datensatzes kann die Karte (immer noch ohne Tiles) schon präzieser aufgebaut werden (ausgewählte Region und diverse Pins/Marker):

let datensatz = {
    position: [47.516669,9.433338],
    bounds: [[47.5,9.3],[47.7,9.7]],
    marker: [[47.611593,9.296344],[47.586204,9.560653],[47.54378,9.686559]],
}

let mapOptions = {
    minZoom:3,
    maxZoom:18,
    scrollWheelZoom:true,
}

// Karte anlegen (ohne Inhalt)
var map = L.map( 'map-1', mapOptions );

// Karte auf den Zielbereich focusieren
map.fitBounds( L.latLngBounds(datensatz.bounds) );

// Marker in die Karte packen
for( marker of datensatz.marker ) {
    L.marker( marker ).addTo( map );
}

// Position mit einem anderen Marker markieren
let positionIcon = L.icon( {iconUrl:"assets/addons/geolocation/plugins/leaflet/images/marker-icon-red.png",iconAnchor:[12,41],iconSize:[25,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28]} );
L.marker( datensatz.position, {icon:positionIcon} ).addTo( map );

Nun ist die Karte ausgerichtet und befüllt, aber ohne Tiles wenig aussagefähig:
grafik

Jetzr kommen die Tile-Informationen dazu am Beispiel HERE und drei Kartenvarienten. Ein Datensatz beschribt die Tiles, die dann der Karte hinzugefügt werden:

let datensatz = {
    position: [47.516669,9.433338],
    bounds: [[47.5,9.3],[47.7,9.7]],
    marker: [[47.611593,9.296344],[47.586204,9.560653],[47.54378,9.686559]],
}

let tileConnector = {
    default: {
            url: 'https://{s}.base.maps.ls.hereapi.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?apiKey=.....',
            label: 'Karte',
            options:{
                attribution: 'Map Tiles &copy; 2020 <a href="http://developer.here.com">HERE</a>',
                subdomains:'1234',
            }
        },
    sattelite: {
            url: 'https://{s}.aerial.maps.ls.hereapi.com/maptile/2.1/maptile/newest/satellite.day/{z}/{x}/{y}/256/png8?apiKey=.....',
            label: 'Satelit',
            options:{
                attribution: 'Map Tiles &copy; 2020 <a href="http://developer.here.com">HERE</a>',
                subdomains:'1234',
            }
        },
    hybrid: {
            url: 'https://{s}.aerial.maps.ls.hereapi.com/maptile/2.1/maptile/newest/hybrid.day/{z}/{x}/{y}/256/png8?apiKey=.....',
            label: 'Hybrid',
            options:{
                attribution: 'Map Tiles &copy; 2020 <a href="http://developer.here.com">HERE</a>',
                subdomains:'1234',
            }
        },
}

let mapOptions = {
    minZoom:3,
    maxZoom:18,
    scrollWheelZoom:true,
}

// Karte anlegen (ohne Inhalt)
var map = L.map( 'map-1', mapOptions );

// Karte auf den Zielbereich focusieren
map.fitBounds( L.latLngBounds(datensatz.bounds) );

// Marker in die Karte packen
for( marker of datensatz.marker ) {
    L.marker( marker ).addTo( map );
}

// Position mit einem anderen Marker markieren
let positionIcon = L.icon( {iconUrl:"assets/addons/geolocation/plugins/leaflet/images/marker-icon-red.png",iconAnchor:[12,41],iconSize:[25,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28]} );
L.marker( datensatz.position, {icon:positionIcon} ).addTo( map );

// Tile-Layer hinzufügen
let tileLayer = {};
for( tile in tileConnector ) {
    tileLayer[tileConnector[tile].label] = L.tileLayer( tileConnector[tile].url,tileConnector[tile].options );
    // nur den Default-Layer aktivieren
    if( tile == 'default' ) tileLayer[tileConnector[tile].label].addTo( map );
}
L.control.layers( tileLayer, {} ).addTo(map);

Das Ergenis ist eine Karte zu einem Regatta-Event auf dem Bodensee (Ausgangspunkt Argon und drei weitere Stationen) mit mehreren Tile-Layern zur Auswahl (Element oben rechts, daneben aufgeblendet wenn die Maus darübergeht): Attribution-Texte sieht man unten rechts.
grafik

Den Tile-Connector könnte man für Verwendung mit einem Proxy so aufbauen:

let tileConnector = {
    default: {
            url: 'index.php?rex_api_call=geotiles&z={z}&x={x}&y={y}&l=1',
            label: 'Karte',
            options:{
                attribution: 'Map Tiles &copy; 2020 <a href="http://developer.here.com">HERE</a>',
            }
        },
    sattelite: {
            url: 'index.php?rex_api_call=geotiles&z={z}&x={x}&y={y}&l=2',
            label: 'Satelit',
            options:{
                attribution: 'Map Tiles &copy; 2020 <a href="http://developer.here.com">HERE</a>',
            }
        },
    hybrid: {
            url: 'index.php?rex_api_call=geotiles&z={z}&x={x}&y={y}&l=3',
            label: 'Hybrid',
            options:{
                attribution: 'Map Tiles &copy; 2020 <a href="http://developer.here.com">HERE</a>',
            }
        },
}

Im API würde ausgewertet:

  • x,y,z = Koordinaten des Bildes
  • l = Layer (ID des Layer-Datensatzes in derLayer/Provider-Tabelle)

Gecached werden die Tiles in data/addons/geo/{l}/{x}-{y}-{z}.suffix.

Steuerung der Karten und des Proxy/Cache über zwei Tabellen:

rex_geo_map: Kartendefinition

  • initialer Anzeigebereich wenn nicht anderweitig vorgegeben (Tourismusverband Niederhein wird wohl alle keinen Wert darauf legen, die Katen initial auf New York zu sehen?)
  • Zoom-Begrenzung
  • .... was sonst noch eingeht in die Basis-mapOptions etc.
  • zugeordnete Layer aus rex_geo_layer

Das sind Daten, die in die Karteninitialisierung einlaufen und (Beispiel initialer Anzeigebereich) ggf. durch das Datenfeld überschrieben werden.

rex_geo_layer: Layerbeschreibung / Tile-Provider

  • Die URL zum Tileserver mit allem drum und dran inkl appKeys und so falls erforderlich
  • Label-Texte in verschiedenen Sprachen
  • Attribution-Text (ggf. in mehreren Sprachen)

Wird eine Kachel von Leaflet via API abgerufen sucht sich das API das Bild primär aus dem Cache.

Ansonsten wird via Paramater {l} die URL abgerufen, mit den Parametern gefüllt und das Bild abgeholt. Erhaltenes Bild in den Cache schreiben und an den Client ausliefern.

@christophboecker
Copy link
Member

Sorry, versehentlich geschlossen und wieder geöffnet :-)

@christophboecker
Copy link
Member

Ich schreib mal den Proxy in schön ....

@tbaddade
Copy link
Member Author

Ich schreib mal den Proxy in schön ....

Was meinst du?

@christophboecker
Copy link
Member

Ich baue mal einen Tile-Proxy bzw. Tile-Cache wie oben beschrieben

@tbaddade
Copy link
Member Author

Ich wollte gern den Experimental-Proxy verwenden. Der ist deshalb Experimental da es kaum Test zu gibt. Wenn das gut funktioniert bzw. angepasst wird, sollte das ggf. mit in den Core einfließen.

@schuer
Copy link
Member

schuer commented Jun 20, 2020

@christophboecker
Copy link
Member

Wie angekündigt hab ich mal was gebastelt mit Schwerpunkt Tile-Proxy. Halt als "anfassbaren" Diskussionsbeitrag:

https://github.com/christophboecker/gelocation

Stark am osmproxy orientiert, aber mit mehr Features. Auch ein paar Anwendugsaspekte sind schon drin, damit man die Funktion sehen kann. Insofern halt auch genereller Diskussionsbeitrag für "Geo". Ist auch beim Experimentalproxy verlinkt.

Bitte Erklärungen in der Readme lesen :-)

@skerbis
Copy link
Member

skerbis commented Jul 22, 2020

@christophboecker wow! 👍

@skerbis
Copy link
Member

skerbis commented Sep 1, 2020

yform_geo_osm wurde kürzlich um ein Mass-Geocoding erweitert, @dtpop hat es umgesetzt

@skerbis
Copy link
Member

skerbis commented Mar 23, 2021

@tbaddade hast du dir das AddOn vom @christophboecker angeschaut?

@alxndr-w
Copy link
Member

Kann das hier zu?

@christophboecker
Copy link
Member

Keine Ahnung, war Thomas Blums Thema; ich hab nur einen Diskussionsbeitrag geliefert. Der sich auch schon überholt hat durch das Geolocation-Addon. Aber wenn ich es richtig sehe, war Thomas´ Intention schon anders etwas gelagert als mein Beitrag.

@tbaddade
Copy link
Member Author

Aber wenn ich es richtig sehe, war Thomas´ Intention schon anders etwas gelagert als mein Beitrag.

Das stimmt … aber ich schließe hier dennoch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants