Leaflet: Ändern Sie die crs-Eigenschaft von map dynamisch

Erstellt am 19. März 2014  ·  34Kommentare  ·  Quelle: Leaflet/Leaflet

Kürzlich musste eines unserer Projekte die crs zur Laufzeit ändern (während wir die Basisebene der Karte ändern), und ich habe festgestellt, dass jemand die gleiche Anforderung durch # 1818 erfüllt hat, ich habe die erwähnte Weise versucht (unter Verwendung der baselayerchange Ereignis, aber der Autor verwendet Merkblatt 0.5, während ich 0.7.2 verwende, was bedeutet, dass es nicht funktioniert.

Also habe ich versucht, es selbst zu machen, das ist, was ich versucht habe:

layer.on('add',function(){
    var crs=getCrsByLayer(this);
    map.options.crs=crs; //reset the crs of the map

    //we have to clear this two properties, or the coord of the tile would be re-wrapped wrongly by the former crs of the map
    layer._wrapLng=null;
    layer._wrapLat=null;

       //it seems that the tiles calculation procedure have been done before the add event fired, so hack it to redraw the layer.
    layer.redraw();
});

Es funktioniert jedoch nicht, also habe ich versucht, mich mit den Codes zu befassen, und schließlich habe ich festgestellt, dass sich die Eigenschaft _initialTopLeftPoint der Karte nicht ändert, wenn ich das crs über map.options.crs zurücksetze, was bedeutet, dass die Fliesengrenzen werden nicht korrekt berechnet. Da dies ein Privatgrundstück ist, halte ich es nicht für eine gute Idee, es draußen aufzustellen.

Also denke ich, ob Sie diese Funktion noch einmal überdenken können? Es verursacht keine weiteren Codes, Sie machen einfach, dass die crs-bezogene Eigenschaft neu initialisiert werden kann.

Danke.

feature later projections

Hilfreichster Kommentar

Es ist das gleiche Problem bei /issues/2553. Und ich verwende die baselayerchange Layer-Ereignisse, um mehrere Karten zu ändern, Baidu verwendet das CRS EPSG:900913 , andere Karten verwenden EPSG3857. Dies ist meine Demo: https ://lhywell.github.io/ map/example/example2/leaf2.html

map.on('baselayerchange', function(layer) {
        let center = map.getCenter();
        let zoom = map.getZoom();
        console.log(map.options.crs)
        if (layer.name.indexOf('百度') > -1) {
            map.options.crs = L.CRS.Baidu;
            map.options.tms = true;
            map._resetView(center, zoom, true);
        } else {
            map.options.crs = L.CRS.EPSG3857;
            map.options.tms = false;
            map._resetView(center, zoom, true);
        }
})

Alle 34 Kommentare

Du hast recht, ich sollte mich darum kümmern.

@perliedman Gedanken?

Mein Bauchgefühl ist, dass die Zulassung einer Änderung des CRS zu einer Komplexität führen würde, die wir nicht unbedingt wollen. Dies ist ein ungewöhnlicher Fall und zwingt gleichzeitig jeden Code, der sich mit Pixel- und / oder projizierten Koordinaten befasst, sich bewusst zu sein, dass sich das CRS jederzeit ändern kann. Dies könnte für Plugin-Autoren besonders schwierig sein, die dieses Szenario wahrscheinlich nicht erwägen oder testen werden.

Ich würde eher empfehlen, für diesen Fall mehrere Karteninstanzen zu verwenden.

Sie haben die Verwendung mehrerer Karten erwähnt, aber wie sieht es mit den Overlays aus? wie die Markierungen, Polylinien, Polygone usw. in der vorherigen Karte? Sie einzeln zur neuen Karte hinzufügen?

Und ich denke, das crs-Feature kann nicht so viele komplexe Situationen verursachen, wie Sie dachten, Leute, die sich nicht darum kümmern, können es ignorieren, als ob es nicht existiert. Menschen, die es brauchen, können davon profitieren.

Für die Plugin-Autoren: Wenn die vom Plugin bereitgestellte Funktion mit dem Crs zusammenhängt, dann denke ich, dass der Autor es wissen und das entsprechende Wissen über das Crs haben muss. Andernfalls hat es für den Schreiber keine Auswirkung.

Ich bin mir ziemlich sicher, dass ich Plugins geschrieben habe, die CRS-Änderungen nicht ordnungsgemäß handhaben würden, also ist es durchaus möglich, während ich immer noch das Wissen über CRS habe.

Natürlich könnte man sagen, dass dies ein Fehler meinerseits ist, und darüber diskutieren wir, aber es macht die Dinge sicherlich etwas komplizierter, da es als Plugin-Autor keine Möglichkeit gibt, zu wissen, wann sich das CRS ändert .

Ist die Verwendung mehrerer Karten also die einzige Wahl? Dann wird das Synchronisieren der Steuerelemente, Markierungen, Polylinien usw. zwischen verschiedenen Karten ein Albtraum. :(

Bei meinem ersten Gedanken ziehe ich es vor, zwei Methoden zum Zuordnen hinzuzufügen:

setCRS() und getCRS()

Und stellen Sie den Komponenten, die für Crs sensibilisiert sind, ein Crs-Änderungsereignis zur Verfügung.

Siehe http://jsfiddle.net/Hb5Hx/1/. Stellen Sie die Basisebene mit verschiedenen crs ein, ich ändere keine anderen Codes wie den Hersteller usw., es funktioniert einfach. :)

Ich denke, eine API in dieser Richtung wäre vorzuziehen, wenn wir entscheiden, dass es sich lohnt, CRS-Änderungen zu unterstützen, nachdem die Karte erstellt wurde.

Es scheint, dass das Merkblatt das Ändern von crs zur Laufzeit aufgrund von zwei Hauptaspekten nicht unterstützt:
1 Dadurch werden die Codes komplizierter als zuvor.
Ich gebe zu, dass es evtl.

2 Es gibt nicht so viele Anforderungen.
Ich denke jedoch, dass dies möglicherweise nicht die Wahrheit ist. Die kommerziellen APIs wie Google Map unterstützen dies (durch unterschiedliche Kartentypbreite und unterschiedliche Projektion).

Schauen Sie sich das an : https://google-developers.appspot.com/maps/documentation/javascript/examples/full/aerial-simple

Das 45° imagery ist eine andere Art von Projektion (wenn nicht, repariere mich).
Und wir haben die gleichen Bilder wie diese, die wir mit den allgemeinen Kacheln kombinieren wollen, deshalb bevorzuge ich diese Funktion persönlich so sehr :).

Und schließlich, wenn diese Funktion nicht zur Kern-API hinzugefügt werden kann, können Sie mir einige Vorschläge machen, um sie zu hacken? Ich habe zwar dafür gesorgt, dass es zu funktionieren scheint, aber ich verwende so viele private interne Methoden, die ich für unzuverlässig halte, da sich die Methoden irgendwann ändern können.

Und tatsächlich plane ich, ein Plugin wie das Google Map Type Control zu erstellen, jeder Maptype kann sein eigenes Crs haben. Ich hoffe, dass dies auf zuverlässige Weise geschehen kann. :)

Vielen Dank für die Bereitstellung eines Anwendungsfalls dafür - das scheint nützlicher zu sein, als ich zuerst dachte.

Gibt es jedoch 45-Grad-Kachelsätze (AFAIK, Sie dürfen Google Maps-Kacheln nicht mit einem anderen Code als Google Maps anzeigen)? Wie funktioniert das CRS? Irgendwelche Unterlagen vorhanden?

Haben Sie versucht, was passiert, wenn Sie _initialTopLeftPoint manuell zurücksetzen, das Sie in Ihrem ersten Kommentar erwähnt haben? Gibt es andere Probleme in Leaflet, die verhindern, dass CRS geändert wird, nachdem die Karte erstellt wurde?

1 Für die Fliesensets.
Ich gebe zu, dass ich keine 45-Grad-Kachel wie Google Map oder andere 2,5-D-Kacheln zur freien Verwendung gefunden habe. In unserer Anwendung erstellen wir unsere eigenen 45-Grad-Kacheln mit Gebäudemodellen darüber.

Für das crs denke ich, dass es nicht das beliebte CRS wie 3857 4326 oder etwas anderes verwendet. In unserem Beispiel verwenden wir das benutzerdefinierte crs mit einer benutzerdefinierten Transformation.

2 Ich habe es noch versucht, und das ist das Ergebnis (ich habe es in meinem letzten Kommentar gepostet)
http://jsfiddle.net/Hb5Hx/1/

Das ist, was ich getan habe, als ich versuchte, das crs zu ändern, nachdem die Karte erstellt wurde:

               //remove the last layer
        map.removeLayer(wms);
               //change the crs
        map.options.crs = crs;
                //re-calculate the leftpoint
        map._initialTopLeftPoint = map._getNewTopLeftPoint(map.getCenter());
                //add the other layer
        map.addLayer(osm);
        //update the overlays(only mk and mk2 in example)
        mk.update();
        mk2.update();

Und ich habe festgestellt, dass es ein explizites Problem gibt: Die Kartenmitte wird geändert, wenn zu den anderen Crs gewechselt wird.
Ich habe nicht überprüft, was das Problem ist.

@perliedman , wie wär's damit?

Dies ist meine bescheidene Meinung zu diesem Feature:

Es ist eine ziemlich ungewöhnliche Funktion, die angefordert werden muss (ich kann kein anderes Problem finden, das dies anfordert, und nichts auf uservoice). Angesichts dessen und der Unsicherheiten darüber, welche Auswirkungen diese Funktion sowohl auf die Kerncodebasis als auch auf die Plugins haben würde, glaube ich nicht, dass Sie erwarten können, dass diese Funktion von jemand anderem hinzugefügt wird, zumindest nicht kurzfristig.

Wenn Sie diese Funktion wirklich benötigen, sollten Sie sich meiner Meinung nach dafür entscheiden und diese Funktion implementieren und eine Pull-Anfrage stellen. Ohne weitere technische Details ist es wahrscheinlich schwer zu bestimmen, ob dies eine Funktion ist, die in das Merkblatt aufgenommen werden sollte; vielleicht könnte es sogar in ein Plugin umgewandelt werden (auch wenn es mir ein bisschen schwer fällt zu sehen, wie das funktionieren würde). Ich würde gerne versuchen, Ihre Fragen zu verstehen, aber es ist viel einfacher, wenn Sie tatsächlichen Code zum Überprüfen und Testen haben.

Das Schlimmste, was dabei herauskommen könnte, ist, dass Sie einen Leaflet-Fork mit funktionierendem CRS-Switching haben, was meiner Meinung nach immer noch fast das wäre, was Sie wollen.

@mourner , hast du eine Meinung zur Priorität dieser Funktion und/oder der Schwierigkeit, sie zu unterstützen?

Mir gefällt die Idee, dass jede Kartenebene ihre eigene Projektion haben könnte. Ich habe die Kacheln eines lokalen Kartenanbieters (swisstopo) hinzugefügt, die eine andere Projektion verwenden, zusammen mit den Openstreetmap-Kacheln, und es wäre großartig, wenn ich spontan zu dieser Kartenebene wechseln könnte.
Nur um Sie wissen zu lassen, dass es andere Leute gibt, denen diese Funktion gefallen würde.

Ich stimme @AndyBlah zu. Ich habe zwei Kartenebenen mit unterschiedlichen KBS. Im Moment weiß ich nicht, wie ich mit dieser Situation umgehen soll

@Hashedhash , ich habe eine Lösung gefunden, es funktioniert aber nicht so gut, wie ich es gerne hätte. Etwas wie das:

map.removeLayer(map.curLayer);
var center = map.getCenter();
map.options.crs = map.newLayer.crs;
map.setView(center); //we need this, because after changing crs the center is shifted (as mentioned above probably it's an issue to)
map._resetView(map.getCenter(), map.getZoom(), true); //we need this to redraw all layers (polygons, markers...) in the new projection.
map.addLayer(map.newLayer);

@guliash , danke für die Lösung, ich denke, es löst dieses Problem. Aber in meinem Fall habe ich der Karte 2 Ebenen mit unterschiedlichen CRS (EPSG3395 und EPSG3857) hinzugefügt und ich habe bereits herausgefunden, dass es derzeit nicht möglich ist, es mit Leaflet zum Laufen zu bringen

Es ist ein harter Job, die OP zu überzeugen. :)

Ich habe diese Anforderung auch aus wissenschaftlichen Daten auf Karten, wo Sie nahtlos zwischen zB verschiedenen arktischen Projektionen wechseln möchten (was manchmal nur Rotation ist): http://webmap.arcticconnect.org/examples/Leaflet.PolarMap/polar -projections/index.html (Ich bin nicht mit dieser Website verbunden) Ich denke, es wäre gut zu überprüfen, was diese Jungs getan haben, damit es funktioniert https://github.com/GeoSensorWebLab/polarmap.js und ob sie mussten Verwenden Sie Workarounds usw.

@neothemachine :+1: Gute Arbeit.

unterschiedliche Crs für Base Layer und Overlay https://github.com/Leaflet/Leaflet/pull/4150

js-Fiddle-Beispiel: http://jsfiddle.net/alekzonder/qxdxqsm3/

Ich habe auch eine Verwendung dafür, um Nicht-Kartendaten anzuzeigen, die zusätzlich zum üblichen Zoomen horizontal gestreckt oder komprimiert werden können (in meinem Fall repräsentiert die horizontale Koordinate die Zeit).

Der Status dieses Problems ist noch mehr oder weniger in meinem Kommentar https://github.com/Leaflet/Leaflet/issues/2553#issuecomment -39296082 beschrieben.

Um dies voranzutreiben, müssen wir einen konkreten Vorschlag erörtern. Es zu ermöglichen, CRS zu wechseln, nachdem die Karte erstellt wurde, _könnte_ eine gute Idee sein (aber das Zulassen von Layern mit unterschiedlichem CRS ist es nicht).

Jemand, der diese Funktion benötigt, muss höchstwahrscheinlich nachsehen, was erforderlich ist und ob es machbar ist. Halten Sie nicht den Atem an und hoffen Sie, dass die Leaflet-Entwickler dies tun werden.

Außerdem bin ich, wie oben erwähnt, offen für Diskussionen und versuche, alle Fragen zur Entwicklung zu beantworten.

(nur eine Anmerkung): Es könnte interessant sein zu sehen, wie https://www.windyty.com den Wechsel zwischen Ebene 4 und 5 zu/von 3D-Globus durchführt. Sie verwenden Leaflet 0.7.5 und schaffen diesen Wechsel irgendwie ohne Neuladen.

@hyperknot sie verwenden zwei völlig unterschiedliche Bibliotheken. Der Globus ist auf Leinwand gezeichnet, ohne Leaflet. Überprüfen Sie den DOM-Inspektor, und Sie können sehen, dass es eine Leaflet-Karte und ein weiteres Div für den Globus gibt. Sie zeigen / verbergen den Globus, wenn Sie hinein- oder herauszoomen.

@perliedman danke, ich verstehe. Es ist also im Grunde ein Trick, zwei Viewer oben mit einer Deckkraft von 0/1 zu synchronisieren.

Ich brauche diese Erweiterung auch. Mein Anwendungsfall:
Ich verwende Basisschichten aus verschiedenen Quellen mit unterschiedlichen CRS, zum Beispiel 'OSM' und 'Dutch Aerial'.
Die niederländischen Daten befinden sich im niederländischen CRS.
Ich lade auch Vektordaten von einem WebAPI-Server. Wenn ich diesen Server anrufe, kann ich das CRS hinzufügen, damit ich die Daten im richtigen CRS zurückbekomme. Aber ich muss wissen, dass der Benutzer die Basisebene gewechselt und die Karten-CRS geändert hat. Irgendwann werde ich ngx-Leaflet von Asymmetrik verwenden.
Das ist für uns ein echter Hingucker. Wenn Leaflet damit nicht umgehen kann, muss ich eine andere Kartenkomponente verwenden.

@pmeems Hallo, nach dem, was ich Ihrer Beschreibung entnehmen kann, sollten die Vektordaten kein Problem sein: Leaflet verwendet _immer_ WGS84 für seine API, daher würde sich dies nicht ändern, selbst wenn Sie das CRS der Karte ändern. Sie benötigen jedoch eine Möglichkeit, das KBS zu ändern, wenn Ihre Tilesets unterschiedliche Projektionen verwenden.

Aus meiner Sicht haben Sie zwei Möglichkeiten, dies zu tun:

  • Verwenden Sie eine der oben geposteten Problemumgehungen (ich denke, es könnten auch andere Problemumgehungen in verwandten Problemen gepostet werden)
  • Sorgen Sie dafür, dass diese Funktion in Leaflet implementiert ist - mein früherer Kommentar gilt immer noch, verlassen Sie sich nicht darauf, dass irgendjemand einfach vortritt und dies tut - erwägen Sie entweder, diese Funktionalität beizusteuern (wir wären 🎉 🔥, wenn Sie dies tun würden), oder ziehen Sie vielleicht in Betracht, die Entwicklung zu sponsern dieser Funktion, wenn es Ihnen wichtig ist

Übrigens bin ich mir nicht sicher, wie ich das "Wenn Leaflet damit nicht umgehen kann, muss ich eine andere Kartenkomponente verwenden" interpretieren, aber es ist leicht als eine Art Bedrohung zu lesen. Bitte beachten Sie, dass Sie kein Leaflet-Kunde sind: Leaflet ist Open Source, und wir verdienen mit Leaflet kein Geld an Ihnen. Wir freuen uns, wenn Sie Leaflet verwenden, und wir versuchen auch gerne, Ihnen dabei zu helfen, dies in einem angemessenen Rahmen zu tun, aber wenn Sie lieber eine andere Bibliothek verwenden, ist das auch völlig in Ordnung für uns. Auch hier gilt: Wenn Ihnen ein bestimmtes Feature wichtig ist, denken Sie darüber nach, es hinzuzufügen (wir helfen gerne bei der Beantwortung von Fragen und geben Feedback), oder sponsern Sie vielleicht jemanden, der es hinzufügt.

Danke Per für deine schnelle Antwort.
Ich wollte dich nicht bedrohen. Ich weiß, dass Sie alle großartige Arbeit leisten, und zwar hauptsächlich als Freiwillige.
Ich kann nicht selbst beitragen, dies aus meiner Schuld, aber ich werde den Sponsorenvorschlag intern besprechen.

Zu Ihrer Bemerkung zu den Vektordaten. Meinen Sie damit, dass die Vektordaten clientseitig immer wieder auf WGS84 projiziert werden? Wenn ja, würde es die Leistung verbessern, wenn ich die Daten auch in WGS84 anfordere, oder?
Die Daten befinden sich in WG84 in unserer PostGIS-Datenbank und wir reprojizieren auf der Serverseite, bevor wir die Daten zurücksenden.

@pmeems in Ordnung, schön zu hören.

Betreff: Projektionen, ja, Leaflet-Vektoren werden immer aus WGS84 erstellt (d. h. Sie geben WGS84-Daten ein) und werden auf dem Client in das aktuelle CRS neu projiziert. Wenn Ihre Daten also WGS84 in Ihrer Datenbank sind, müssen Sie sie nicht auf etwas anderes umprojektieren. Wenn Ihre Daten in einem anderen Format vorliegen, können Sie beispielsweise Proj4Leaflet verwenden oder neu projizieren , damit sie mit der API von Leaflet funktionieren.

Es ist das gleiche Problem bei /issues/2553. Und ich verwende die baselayerchange Layer-Ereignisse, um mehrere Karten zu ändern, Baidu verwendet das CRS EPSG:900913 , andere Karten verwenden EPSG3857. Dies ist meine Demo: https ://lhywell.github.io/ map/example/example2/leaf2.html

map.on('baselayerchange', function(layer) {
        let center = map.getCenter();
        let zoom = map.getZoom();
        console.log(map.options.crs)
        if (layer.name.indexOf('百度') > -1) {
            map.options.crs = L.CRS.Baidu;
            map.options.tms = true;
            map._resetView(center, zoom, true);
        } else {
            map.options.crs = L.CRS.EPSG3857;
            map.options.tms = false;
            map._resetView(center, zoom, true);
        }
})

@lhywell Irgendeine Idee zum Aktualisieren der Standorte von Markern, Polygonen, Polylinien usw. nach dem Ändern des CRS? Latlngs mit neuem CRS konvertieren und einzeln aktualisieren? Wenn ja, ist das zu schrecklich.
Übrigens, nachdem sich das CRS geändert hat, sollte center in einen neuen Punkt konvertiert werden, der das neue CRS verwendet, aber nicht dasselbe wie zuvor, richtig?

Ich wollte die Ebenensteuerung verwenden, um zwischen Ebenen mit verschiedenen CRS's mit unterschiedlichen Zoomstufen (WGS84 / British National Grid) zu wechseln.

Ich stellte fest, dass ich Control.Layers._checkDisabledLayers überschreiben musste, damit die Ebenen in der Ebenensteuerung nicht deaktiviert wurden. setze map.options.crs auf ein baselayerchange Ereignis; und rufen Sie map.fitBounds() sowohl in den baselayerchange - als auch in den zoomend -Event-Handlern auf.

L.Control.Layers.prototype._checkDisabledLayers = function() {};

map.on('baselayerchange', function(layer) {
    map.options.crs = layer.name.split(':')[0] == 'OS' ? crs27700() : L.CRS.EPSG3857;
    map.fitBounds(bounds);
    map.baselayerchange = true;
});

map.on('zoomend', function() {
    if (map.baselayerchange) map.fitBounds(bounds);
    delete map.baselayerchange;
});

Ein voll funktionsfähiges Beispiel mit weiteren Hinweisen finden Sie unter moving-type.co.uk/dev/track.trail/leaflet-layer-control-multi-crs.html ; Code kann über View Source angezeigt werden.

So habe ich es am Ende gemacht:

function getMaxBounds(crs) {
  const { bounds } = crs.projection;
  return new L.LatLngBounds(
    crs.unproject(bounds.min),
    crs.unproject(bounds.max),
  );
}

function changeCRS(map, crs) {
  const bounds = map.getBounds();
  map.options.crs = crs;
  // Ensure zoom is not affected by differing CRS scales
  map.options.zoomSnap = 0;
  map.fitBounds(bounds);
  map.setMaxBounds(crs instanceof L.Proj.CRS ? getMaxBounds(crs) : null);
  map.options.zoomSnap = 1;
}

Wenn Sie ein L.markerClusterGroup verwenden, müssen Sie dies auch nach changeCRS tun:

const layers = markerClusterGroup.getLayers();
markerClusterGroup.clearLayers();
markerClusterGroup.addLayers(layers);
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

edmsgists picture edmsgists  ·  3Kommentare

gdbd picture gdbd  ·  3Kommentare

brambow picture brambow  ·  3Kommentare

timwis picture timwis  ·  3Kommentare

ssured picture ssured  ·  3Kommentare