LazMapViewer ergänzt um lokale Dateien zu verwenden

Rund um die LCL und andere Komponenten
Antworten
Ekkehard
Beiträge: 12
Registriert: So 12. Feb 2023, 12:42

LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von Ekkehard »

Hallo,
ich bin nach etwas Sucherei auf die schöne Komponente LazMapViewer gestoßen, die fast alles kann was ich benötige.
Es hat eine Weile gedauert, bis ich alle ergänzend benötigten Komponenten zusammen hatte. Es wäre schön eine Liste davon zu haben, was, von wo und in welcher Reihenfolge zu Laden und zu Installieren ist :).
Das "fast alles kann" bezieht sich auf die Option, lokale Dateien (also auf der eigenen Festplatte, Netzlaufwerk etc) verwenden zu können.
Es gibt das Projekt Maperitive (http://maperitive.net/), welches es erlaubt eigene OSM-Tiles im eigenen Layout zu rendern und so für höhere Zugriffszahlen auf eigene Ressourcen zu setzen. Diese dort generierten Tiles wollte ich gerne einlesen.
Ich war in der Lage, das zu realisieren.
Dazu war es nötig eine abgeleitete Klasse zur vorhandenen Download-Engine zu erstellen, die erkennt, ob eine lokale Datei anstelle einer Datei aus dem Internet zu laden ist.
Diese Klasse heißt jetzt der Nomenklatur des Projektes folgend TMVDLEFFPCTFR, die komplette Datei ist angehängt. Die einzige Methode die überschrieben wurde ist die Methode "DownloadFile":

Code: Alles auswählen

procedure TMVDLEFFPCTFR.DownloadFile(const Url: string; AStream: TStream);
const
  FILE_SCHEME : String = 'FILE:///';
var
  fs : TFileStream;
  fn : String;
  s : String;
begin
  s := UpperCase(Copy(Url,1,Length(FILE_SCHEME)));
  if s <> FILE_SCHEME then
  begin // The URL-Scheme is not file, try the default loading stuff
    inherited;
    Exit;
  end;
  {$IFDEF LOG_URL}
  DebugLn(Url);
  {$ENDIF}
  fn := Copy(Url,Length(FILE_SCHEME)+1,MaxInt);   // Extract the file-path
  if not FileExists(fn) then Exit; // If the file does not exists, exit
  fs := Nil;
  try
    try
      fs := TFileStream.Create(fn,fmOpenRead); //Open the Tile-File
      AStream.CopyFrom(fs,fs.Size); //Copy the contentn into the strem
      AStream.Position := 0;
    except
    end;	
  finally
    if Assigned(fs) then
      fs.Free;
  end;
end;            
Im Hauptprogramm muss jetzt eine Instanz dieser Klasse, die voreingestellte DownloadEngine ersetzen.
Zusätzlich muss noch ein weiterer "MapProvider" erzeugt werden, der den Zugriff auf die Dateien enthält.
Im Wesentlichen beschränken sich die Änderungen auf das "FormCreate"-Event. Die Datei ist ebenfalls angefügt.

Code: Alles auswählen

  FMvDlEFileReader := TMVDLEFFPCTFR.Create(Nil);
  MapView.DownloadEngine := FMvDlEFileReader;
  // Read from Files, generated by Maperitive (http://maperitive.net/)
  MapView.Engine.AddMapProvider('OpenStreetMap Maperitive-Files', ptEPSG3857, 'file:///C:/Program_Files/Maperitive/Tiles/%z%/%x%/%y%.png', 0, 19, 3,Nil);
Im Ergebnis kann man jetzt aus der lokalen Quelle die Tile einlesen.
Schönen Sonntag.
Dateianhänge
main.pas
(25.06 KiB) 42-mal heruntergeladen
mvDLEFpcTFR.pas
(2.22 KiB) 39-mal heruntergeladen

wp_xyz
Beiträge: 4885
Registriert: Fr 8. Apr 2011, 09:01

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von wp_xyz »

Ich meine, das ist eine nützliche Erweiterung, zumindest der "Download" von lokaler Datei. Da es verschiedene Download-Engines im LazMapViewer gibt, habe ich mir gedacht, es wäre am besten, wenn das gleich im "Urahnen" aller Download-Engines eingebaut wäre. Das ist im neuesten Commit auf ccr (svn) der Fall.

Den neuen Map-Provider kannst du dir übrigens auch in der MapProvider xml-Datei eintragen, in der die verfügbaren MapProvider verwaltet werden. Siehe dazu im FullDemo, BtnSaveMapProvidersClick und BtnLoadMapProvidersClick.

Ekkehard
Beiträge: 12
Registriert: So 12. Feb 2023, 12:42

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von Ekkehard »

Besten Dank für die schnelle Übernahme.
Leider hat sich ein kleiner Fehler eingeschlichen.
Die Dateiindikation hat drei "/", nicht zwei und so bleibt der dritte "/" vor der Laufwerksbezeichnung "C:" stehen und "/C:/..." führt zu einem Ladefehler der Datei.

In mvDownloadEngine.pas Zeile 41 sollte es deshalb heißen.

Code: Alles auswählen

  FILE_SCHEME = 'file:///';       
Um unnötige Exceptions zu vermeiden ist es mMn sinnvoll in Zeile 60 der gleichen Datei
vor dem TFileStream.Create ein "if FileExists ..." einzufügen.

Code: Alles auswählen

  
  if not FileExists(AFileName) then Exit;
  fs := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
Danke für den Tipp mit dem XML-File, klappt perfekt!

wp_xyz
Beiträge: 4885
Registriert: Fr 8. Apr 2011, 09:01

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von wp_xyz »

Richtig. Jetzt habe ich gleich die URIParser-Unit von FPC aufgerufen, dort wird's schon richtig sein. Könntest du nochmals testen? (Ich habe gerade keine vernünftige Testumgebung für das File-Protocol).

Das FileExists() habe ich nicht aufgenommen, denn die Vollständigkeit der lokalen Dateien ist eine Sache der Anwendung, und fehlende Dateien müssen gemeldet werden. Sonst bleibt eine Kachel in der Karte leer, und der Kunde wundert sich, und der Programmierer sucht und sucht...

Ekkehard
Beiträge: 12
Registriert: So 12. Feb 2023, 12:42

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von Ekkehard »

Funktioniert! Und diese URIParser-Funktion zu verwenden, ist eine sehr gute Idee (ich scheine mich wirklich noch nicht gut genug mit Lazarus auszukennen :oops: ).
Das FileExists() habe ich nicht aufgenommen, denn die Vollständigkeit der lokalen Dateien ist eine Sache der Anwendung, und fehlende Dateien müssen gemeldet werden.
Per se können die lokalen Daten kaum vollständig sein, weil der Aufwand diese zu rechnen nicht unerheblich ist. Ich will aber nicht diskutieren, es ist ein bisschen eine Geschmackssache :)

Eine Option könnte sein, dass man einen weiteren Map-Provider in der "Hinterhand" hat, der genau dann verwendet wird, wenn der erste Map-Provider eine Kachel nicht laden kann.
Das führt dann weiter zu der Idee, eine Liste von solchen Map-Providern zu führen, die der Priorität nach aufgerufen werden, bis eine gültige Kachel geliefert wird.

wp_xyz
Beiträge: 4885
Registriert: Fr 8. Apr 2011, 09:01

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von wp_xyz »

Ich verstehe jetzt nicht im Detail, was du machen willst. Vielleicht kannst du ein kleines Projekt posten, in dem man das sieht.

Könnte man evtl einen spezielle "Download-Engine" bauen, die Maperitive als Tile-Generator anwirft und die erzeugten Tiles in den Cache schreiben lässt? Ab da könnte man wieder normal weitermachen.

Eben habe ich eine neue Version hochgeladen, in der ich eine Download-Engine basierend auf WinInet implementiert habe. ist zwar nur für Windows, aber damit spart man sich das Gefrickel mit den OpenSSL-DLLs. Die neue Download-Engine wird auf Windows als neue Default-Engine verwendet (bei den anderen Betriebssystemen ist das weiterhin die FPC-Download-Engine basierend auf FpHTTPClient). Mein Eindruck ist, dass der Download damit flotter vonstatten geht als mit FpHttpClient.

Leider habe ich entdeckt, dass es beim Beenden des fulldemo-Projekts häufig einen Crash beim Programmende gibt, wenn man (egal mit welcher Download-Engine) gerade Tiles heruntergeladen hat (und nicht aus dem Cache geladen hat - darum habe ich das bisher übersehen). Falls du zufällig auf die Ursache stößt: jede Hilfe ist bei der Fehlersuche in Threads willkommen.
Zuletzt geändert von wp_xyz am Mo 13. Feb 2023, 11:16, insgesamt 1-mal geändert.

Ekkehard
Beiträge: 12
Registriert: So 12. Feb 2023, 12:42

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von Ekkehard »

Ich verstehe jetzt nicht im Detail, was du machen willst. Vielleicht kannst du ein kleines Projekt posten, in dem man das sieht.
Die Anwendung ist die Folgende.
Ein Kunde von mir braucht/hat ein Map-Anwendung, die ganz normal über einen (lokalen) Web-Server läuft, der aber auch von außen (zB für Kunden) zugänglich ist. Da die Zugriffe dort höher sind, wollten wir eine Überlastung der OSM-Server vermeiden und haben das Angenehme mit dem Nützlichen verbunden und mit Materitive schöne eigene Kacheln gerendert. Letzteres ist mit überschaubarem Zeitaufwand möglich, da das abgebildete Gebiet nicht sehr groß ist (größere Kleinstadt).
Leider gibt es ein Problem. Die auf der Karte abgebildeten Inhalte werden an einem PC erstellt, der technisch bedingt und nicht verhinder- oder behebbar, über eine schlechte Netzwerkanbindung verfügt, die mit häufigen sporadischen Verbindungsabbrüchen einhergeht.
Der PC auf dem die Daten erfasst werden muss aber zwingend die Karte darstellen und zwar sicher, jede einzelne Kachel. Die erfassten Daten selbst sind in der Menge sehr klein (Poisition, einzelne Textzeilen) und können problemlos, ggf mit Wiederholung und ggf ein paar Minuten Verzögerung auf den Web-Server übertragen werden.
Da die vollständigen Karten-Kacheln auf dem Webserver vorliegen, lag es nahe, den Inhalt des Tile-Verzeichnisses manuell per USB-Stick auf den PC zu übertragen. Die Option, ein Hilfsprogramm zu schreiben, welches die Tiles erst ausliest und dann in den Cache des MapViewers schreibt, erschien mir wenig effektiv, deshalb dachte ich die Option des dierekten Ladens von der Platte wäre eleganter.
Die Ergänzung ("Was tun wenn eine Kachel nicht da ist") war eher eine Spielerei, die mir in den Kopf kam, als ich hier lokal mit der Entwicklung beschäftigt war.
Könnte man evtl einen spezielle "Download-Engine" bauen, die MapIterative als Tile-Generator anwirft und die erzeugten Tiles in den Cache schreiben lässt?
Diese Idee ist natürlich wesentlich eleganter und das werde ich einmal ausprobieren, zumal Maperitive so etwas per Kommandozeile unterstützt.
...Beenden des fulldemo-Projekts häufig einen Crash beim Programmende...
Das hatte ich auch bemerkt, allerdings wegignoriert. Mir hat bei der Suche nach solchen Fehlern die Verwendung von FastMM5 (https://github.com/pleriche/FastMM5) geholfen, weil solche Fehler sehr häufig durch überschriebene Zeiger (alternativ Stack) auftreten, die erst bei der finalen Freigabe von Objekten bei der Beendung des Programms zu fatalen Fehlern führen. Fairerweise muss ich aber hinzufügen, dass ich FastMM5 bisher nur in Delphi verwendet habe und nicht genau weiß wie und ob er auch unter Lazarus/FPC gut funktioniert.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6208
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: LazMapViewer ergänzt um lokale Dateien zu verwenden

Beitrag von af0815 »

Ekkehard hat geschrieben:
Mo 13. Feb 2023, 08:51
Das hatte ich auch bemerkt, allerdings wegignoriert. Mir hat bei der Suche nach solchen Fehlern die Verwendung von FastMM5 (https://github.com/pleriche/FastMM5) geholfen, weil solche Fehler sehr häufig durch überschriebene Zeiger (alternativ Stack) auftreten, die erst bei der finalen Freigabe von Objekten bei der Beendung des Programms zu fatalen Fehlern führen. Fairerweise muss ich aber hinzufügen, dass ich FastMM5 bisher nur in Delphi verwendet habe und nicht genau weiß wie und ob er auch unter Lazarus/FPC gut funktioniert.
Delphi only und außerhalb von GPL Anwendungen kostenpflichtig (Je Developer), zusätzlich nur Windows (es gibt nur eine dll). Es gab mal eine alte Version die über den Umweg von Kylix auch im FPC (FastMM4) funktioniert haben soll, das ist aber lange her und zu vergessen.
Und wird auch niemals kommen.
Pierre le Riche hat geschrieben:I've spent a lot of time looking at the compiled code disassembly, reorganising the Pascal code, and making use of inlining to nudge the compiler towards producing better output. There's no built-in assembler support for some of the platforms, so with the goal of eventually supporting more platforms than Windows it is worth the time spent.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten