tumic0 / GPXSee

GPS log file viewer and analyzer with support for GPX, TCX, KML, FIT, IGC, NMEA, SLF, SML, LOC, GPI, GeoJSON and OziExplorer files.
https://www.gpxsee.org
GNU General Public License v3.0
950 stars 130 forks source link

[RFC] Overlays. Fixes #178 #491

Closed eddy-geek closed 1 year ago

eddy-geek commented 1 year ago

This is a request for comments, not ready for contribution and maybe never ;-)

still, it works for me with mbtiles. I use it for slope overlays. commit "3." and "4." are the most interesting to look at.

Some things are very brittle, from most important to least:

Still, it's a in a good place to start the conversation :-)

tumic0 commented 1 year ago

Well, you have named the reason why this can generally not be done like you did it for your special use case - it will not work with the majority of the map providers in GPXSee. Every map provider that is not limited to the OSM projection/zoom levels will by definition not work this way. Either you would need to completely rewrite the maps system in GPXSee to allow projection matching and map reprojection (which is a huge task) or you can do such things only "internally" in specific map providers like it is done in the Garmin IMG provider.

For OSM online maps, this could possibly be done in the map definition files like this:

<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.4">
    <name>OSM with overlay</name>
    <url>https://tile.openstreetmap.org/$z/$x/$y.png</url>
        <url>https://tile.overlay.map/$z/$x/$y.png</url>
    <copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © OpenStreetMap (CC-BY-SA)</copyright>
</map>

i.e. by enabling multiple <url> entries, and for MBTiles using the same file suffix method as in IMG maps. But even an attempt to mix this two providers will lead to a big mess - been there done that...

eddy-geek commented 1 year ago

(sorry for the delay, travelling)

For OSM online maps, this could possibly be done in the map definition files like this:

The idea here is to combine arbitrary map definitions from the UI, so it's not really related. Here are screenshots than may make clearer what I've done:

image

Either you would need to completely rewrite the maps system in GPXSee or you can do such things only "internally"

Actually, I think there is a middle ground, as you say the limitation is "OSM projection/zoom level" aka WebMercator (epsg 4326 & 3857), and most of the map content these days is available in a WebMercator version. For example, the french (IGN) and swiss national institutes publish maps in both the local projection and WebMercator.

So, from a user perspective the limitation might be acceptable. For the common use-cases I know about (some combination of satellite pictures + topo maps + track overlays + slope map) they may not even realize it's there.

From a UX perspective, in my (admittedly horrible) sub-menu implementation, I can easily grey-out or remove maps incompatible with the base projection.

Overall I think a same-projection overlay is a useful feature, even a same-format only one to start with. (right-now it works with eg a OpenStreetMap + MBTiles

tumic0 commented 1 year ago

The idea here is to combine arbitrary map definitions from the UI, so it's not really related.

That's of course also possible, but it requires even more work, than the simplest approach I was suggesting and that is already used in IMG files (as they often come split into map + contours). You will have to find some way, how to make the overlays selectable while still fitting into the simple UX of GPXsee. An "Overlays" submenu in "Maps" may work, but I would definitely make a special filesystem folder for the overlays and not duplicate the whole map menu here. And as the "Map" menu is getting crowded, I would probably only display the "Overlays" sub-menu when there is some overlay definition present.

Actually, I think there is a middle ground, as you say the limitation is "OSM projection/zoom level" aka WebMercator (epsg 4326 & 3857), and most of the map content these days is available in a WebMercator version. For example, the french (IGN) and swiss national institutes publish maps in both the local projection and WebMercator.

The problem is, even OSM and WMTS/WMS maps in EPSG:3857 have a different screen coordinate system in GPXsee as the "equations" in WMTS/WMS are designed to work universally with all projections/map bounds. So without some non-trivial changes you can't even mix "the same" OSM and WMTS maps. The core idea when designing the map system was to completely define the map view coordinate system by the current map to allow every possible projection system. This is super flexible for single maps, but as you can see even for the most similar map providers - OSM and MBTiles you had to "hack" the zoom system of MBTiles to get the maps overlayable (and to answer your question, I'm not sure if the way how you did it didn't break some other logic. Check the PDF/PNG exports that fiddle with the zooms and for digital zooming, those are two things that come to my mind).

From a UX perspective, in my (admittedly horrible) sub-menu implementation, I can easily grey-out or remove maps incompatible with the base projection.

The sub-menu is not a that bad idea, but see my notes above. A bigger problem is, that for most map combinations it won't work and people will be confused and report errors like: "the overlays are always grey and can not be selected". I can imagine to limit the overlays to situations, where you don't have to reproject the raster images, but that means it has still to work universally and you have to somehow unify all the map providers.

Would you accept contributions in this direction?

Most probably yes. I'm about to open the contributions to GPXSee anyway and such complex work, when done properly, would definitely be something to start this new era with. But the devil is in the "done properly" detail. Rather than a half-broken implementation that works only on sundays when there is a full-moon, I rather miss this feature...

Do you see other complications?

Oh yes, I see a lot of complications ;-) A few years ago I failed to make it, because with every solved problem, two new have appeared...

eddy-geek commented 1 year ago

Most probably yes

Great, I may come back to this then. Maybe not soon, but I'll keep my branch up-to-date.

I would definitely make a special filesystem folder for the overlays

This works for my use-case but people who want to overlay eg topo map on satellite view will have to duplicate map definitions. Acceptable I guess. From a code perspective I have to admit I was a bit lost with the lifecycle of the QAction objects, but I guess now that I got the overlay menu working, switching it to another subfolder should be a breeze.

... have a different screen coordinate system

This is the part I couldn't grasp yet, how map coordinates interact with the QSceneGraph. Can you explain what this in MapView::setMap does?:

       QRectF vr(mapToScene(viewport()->rect()).boundingRect()
          .intersected(_map->bounds()));
        RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));

What does vr an cr stand for? Is this just about showing the closest part of the map to the previously displayed coordinates?

... OSM and MBTiles you had to "hack" the zoom system of MBTiles to get the maps overlayable

To be pedantic, it's MBTiles maps not even being overlayable on themselves :-)

I can imagine to limit the overlays to situations, where you don't have to reproject the raster images

yes, on that topic I see there seems to be some projection support for vector maps already? with some settings for input/output projection that I don't quite understand, and a setXXXProjection that is only implemented in IMG/Mapsforge/ENC? If so, in theory one could force vector map projection and zoom to the raster map? Probably a nightmare to implement of course :-)

tumic0 commented 1 year ago

What does vr an cr stand for? Is this just about showing the closest part of the map to the previously displayed coordinates?

Exactly. vr is ViewRect and cr CoordinatesRect (WGS84). When the map is changed, you fetch the current (old map's) map view rectangle and convert it to WGS84. Then you convert this WGS84 rectangle to the screen coordinates of the new map and center the new map to show this rectangle. All approximate of course.

yes, on that topic I see there seems to be some projection support for vector maps already? with some settings for input/output projection that I don't quite understand, and a setXXXProjection that is only implemented in IMG/Mapsforge/ENC? If so, in theory one could force vector map projection and zoom to the raster map? Probably a nightmare to implement of course :-)

Every vector map in GPXSee can be shown in one of the ~1700 supported projections (the number of usable projections for one particular map is of course much lower). That's what you set with setOutputProjection(). Some map formats do not contain the map coordinate reference system (CRS) so you have to provide it externally to be able to convert the GPS data to the map coordinates. With setInputProjection() you configure the map provider to use one of the ~4300 supported CRSs.

The CRS is not the only transformation, it only translates from WGS84 to the map coordinates. There is additionally an affine transformation that translates from the map coordinates to the screen coordinates of the map (the transformations are different for different resolutions/zoom levels).

The system in GPXSee is, every map provider provides a ll2xy() and xy2ll() function that translates directly from WGS84 to the screen coordinates of the map (the coordinates of the underlying QGraphicsScene). If you want overlays, you have to somehow unite this ll2xy/xy2ll functions for multiple maps. If there is a different projection or a different map resolution, you can of course not do that (without reprojecting the maps which you can not do lossless for raster maps). But if the map projection and resolution is the same (or the map is a vector map that you can lossless transform to any projection/resolution), then it is theoretically possible to overlay the maps and the user should be able to do that.

But at the moment, you can not even say what the map projection/resolution is - the map providers do not provide such info. There are even some map formats like QCM that do not use geographical projections at all but rather some kind of polynomial approximation. This is not a problem as long as the map is simply a magic black box that "somehow" transforms WGS84 coordinates to screen coordinates, but when you want to mix such magic boxes, you are screwed...

eddy-geek commented 1 year ago

Hello,

Trying to pick this up (already rebased my branch on latest including the recent big refactoring)

Check the PDF/PNG exports that fiddle with the zooms and for digital zooming, those are two things that come to my mind).

checked, it all mostly works except for maybe the transparency setting of overlay not being applied to the pdf

you have to somehow unite this ll2xy/xy2ll functions for multiple maps

Indeed, this is possible with tricky change for a lot of "phase 2" combinations (vector, wms, wmts), but for now there's a lot of already identical implementations for an easy "phase 1":

But at the moment, you can not even say what the map projection/resolution is.

indeed, it's the main obstacle for "greying out" incompatible maps... which is the main blocker for thinking about merging it?

Several options come to mind

I don't think you want such hacks, so what about adding new APIs to the Map class:

* a `getType()` that would return 'mbtiles', 'online' etc. for the "phase 1"
* `projection()` (for everyone): I think this would be needed for other "phase 2" combinations, eg for WMS+WMS ; 
    * not sure if maps with "magic black box" projections should return 0
    * I feel like it's possible because all maps even WM(T)S parse the projection on app startup (even if parseCapabilities is asynchronous)
 * something else may be needed to get vector map to conform to a raster's projections, maybe `isProjectionSupported`/`setProjection`.... but not yet thinking about this :D

Is it acceptable to add these at least getType() to all maps for now? Do you have a better idea?

tumic0 commented 1 year ago

transparency setting of overlay not being applied to the pdf

AFAIK the transparency does not work for PDF export in general, so this is expected.

Indeed, this is possible with tricky change for a lot of "phase 2" combinations (vector, wms, wmts), but for now there's a lot of already identical implementations for an easy "phase 1":

As already mentioned, I'm really not interested in some "phase 1" that randomly works for some random combinations. There are only two solutions I can imagine:

  1. A simple per-map provider layer mechanism in the online maps like it is the case in IMG maps
  2. A "full" solution that is based on Projection/Transform comparison. This means you have to transform all* the map providers so that they have ll2xy()/xy2ll() look like:
QPointF ll2xy(const Coordinates &c) {return _transform.proj2img(_projection.ll2xy(c));}
Coordinates xy2ll(const QPointF &p) {return _projection.xy2ll(_transform.img2proj(p));}

[*] except those one, where it is not possible by definition, i.e. QCT maps.

I don't think you want such hacks, so what about adding new APIs to the Map class:

As already mentioned above, the way to go is to introduce the getProjection() and getTransform methods and base the map compare on them. I really do not want to fiddle with some map type in any way when comparing the maps compatibility.

Btw., I have compiled your "overlay" branch and was trying to launch it, but it crashes during the start somewhere in the map loading/initialization phase. Didn't fiddle with it more as I have no idea in what state your code should be.

eddy-geek commented 1 year ago

crashes during the start somewhere in the map loading/initialization phase

9443881 is what I'm running locally as my "daily driver" but of course it means nothing as it's always the same couple of mbtiles maps.

I suspect you have some maps I don't, can you send the stack?

Actually this last commit contains was a bunch of fixes to initialization code without overlay. I just checked starting without an overlay selected does not crash (and it's remembered across sessions ; although to remove you need to remove overlay=... from gpxsee.conf, I'll fix that)

As already mentioned, I'm really not interested in some "phase 1"

OK well that's more explicit this time :smile:

  1. A simple per-map provider layer mechanism in the online maps like it is the case in IMG maps
  2. A "full" solution that is based on Projection/Transform comparison

As a user what I want is both :smile:

I want (1) because I have mbtiles topo maps split in 2 GB files. Why is it implemented in a IMG-specific way? couln't it apply to any mono-projection map with .N naming scheme? but as said above I don't believe it really answers the overlay case. eg how would it handle transparency?

transform all* the map

that's a clean if herculean solution :wink:

so we'd add operator= in transform.h delegating to QTransform equality ? and then expose that and rely on map.transform == overlay.transform() for eligibility?

things like QCTMap return the empty, invalid Transform() to signify incompatibility?

can we at least do that incrementally? ie release that for WMS/WMTS with everyone else returning Transform() for now?

tumic0 commented 1 year ago

I suspect you have some maps I don't, can you send the stack?

It's several GB of various offline maps and a quiet rich set of online maps. For various reasons, I'm not able to share many of the maps. For the online maps, the GPXSee-maps repo should be a representative sample. For the offline maps, you should get at least some samples of the "easy accessible" formats like IMG, Mapsforge, ENC, KMZ, GeoTIFF and MBtiles. For all of these there are "nice" free samples on the Internet. you can generate most of the rest yourself using MOBAC.

As a user what I want is both

When you implement [2], you also get [1] by definition. When there is some overlay folder that works for all maps than all the per-map solutions will vanish.

Why is it implemented in a IMG-specific way?

IMG maps often come split into the map and contours (or some other layer). GARMIN devices can combine multiple maps so map makers for GARMIN use this feature and people expect it to work everywhere.

couln't it apply to any mono-projection map with .N naming scheme?

Well, if it could, we would not discuss the whole thing right now as it would practically be done... But it is only local for the IMG map provider. It is implemented more like a data layer than an overlay to properly work with every projection of the "base map".

that's a clean if herculean solution

You have been warned...

so we'd add operator= in transform.h delegating to QTransform equality ? and then expose that and rely on map.transform == overlay.transform() for eligibility?

Yes, that's the plan. But that does not mean that when implementing it, one will not find out that there are some obstacles...

things like QCTMap return the empty, invalid Transform() to signify incompatibility?

Yes. But again, that's the theory...

can we at least do that incrementally? ie release that for WMS/WMTS with everyone else returning Transform() for now?

No. I want to see how it works for all the maps. Because different maps may have different problems when converting them to the "unified API" and various hacks may be required. It is also possible that there will be so many problems that it is not worth it and I really do not want to end with some half-working mess on the way to the final solution that will never happen. If it can not be done properly or ends with an unmaintainable mess full of hacks, than I will rather not have this feature at all or go for the simple variant of online maps (OSM) overlays only. This will fulfill the needs of most of the users looking for overlay support with a much less intervention into the existing code.

eddy-geek commented 1 year ago

I'm not able to share many of the maps.

Yes yes I was asking for the stacktrace (eg something like gdb ./gpxsee ; run ; bt)

When you implement [2], you also get [1]

... only from rendering perspective, and only if we support arbitrary # layers -- this PR has only 1 overlay, and more would need to move from a submenu to a real side panel? but there would stilll be need for the ".N filename magic number detection", otherwise for a map split in 12 files, switching maps would be really cumbersome!

that's the plan

OK, I'm thinking of starting with online & mbtiles which are the easiest / the ones I know best.

I had a look at wmts implementation and although the code is razor sharp, it seems your code formatter is misconfigured to remove all comments :rofl:

On the plus side, the tile numbering scheme of an EPSG:3857 WMTS map (like Swisstopo-25k.xml from your samples) is the same as an "online map" (TMS) -- for example for zoom 8, Lake Leman, which is TMS 8-132-90 according to maptiler, I get the same from the gpxsee tile cache for Swisstopo and 4UMaps:

8-132-90 8-132-90

... so clearly there's some way reuse a lot of code for OnlineMap, which is then a WMTS with predefined projection (and maybe I don't have to dive into math right away :tada: ).

I'm not sure the right abstraction for reusing code between them thinking also of the similar on-disk formats (mbtiles etc.) -- it seems it could all fit in the current "TileLoader" with disk cache toggled off for the on-disk formats (and some abstractin for "url" part); then the initialization (the part fetching WMTS Capabilities / reading mbtiles meta table) would be done in the subclasses, and it would return a Metadata object like the current WMTS object but "smarter" with eg bounds() and zoomlevels() method. Does that seem right?

eddy-geek commented 1 year ago

to be clearer I think that WMTSMap is already very very close to a generic tiled map implementation, so what I'm thinking is to start from it and split it into

tumic0 commented 1 year ago

Yes yes I was asking for the stacktrace (eg something like gdb ./gpxsee ; run ; bt)

Thread 1 "gpxsee" received signal SIGSEGV, Segmentation fault.
0x0000000000000018 in ?? ()
(gdb) bt
#0  0x0000000000000018 in ?? ()
#1  0x00005555555be719 in GUI::createMapActionsNode (this=0x555555bbef00, node=..., actionGroup=0x5555560b7520) at src/GUI/gui.cpp:137
#2  0x00005555555dd2d1 in GUI::loadInitialMaps (this=0x555555bbef00, selected=..., selectedOverlay=...) at src/GUI/gui.cpp:2975
#3  0x00005555555be229 in GUI::GUI (this=0x555555bbef00) at src/GUI/gui.cpp:113
#4  0x00005555555bd220 in App::App (this=0x7fffffffe750, argc=@0x7fffffffe74c: 1, argv=0x7fffffffe898) at src/GUI/app.cpp:73
#5  0x000055555559bed1 in main (argc=1, argv=0x7fffffffe898) at src/main.cpp:15
tumic0 commented 1 year ago

I had a look at wmts implementation and although the code is razor sharp, it seems your code formatter is misconfigured to remove all comments rofl

Well, the "coding style" is to comment only stuff, that is not obvious or for some reason (e.g. performance) tricky. This means there shall be ideally no comments at all ;-) It is however expected, that someone fiddling with the code knows the "theory" behind the code, i.e. the map format specification. But I admit that for some maps this is hard to achieve as the GPXSee code itself is the only public available "documentation" in the world...

On the plus side, the tile numbering scheme of an EPSG:3857 WMTS map (like Swisstopo-25k.xml from your samples) is the same as an "online map" (TMS) -- for example for zoom 8, Lake Leman, which is TMS 8-132-90 according to maptiler, I get the same from the gpxsee tile cache for Swisstopo and 4UMaps:

The map tiles are the same (= cover the same area), but the coordinate system is different and the zoom/layer numbering is often also different. And while in this/most cases the "Google scales" are used in EPSG:3857 projected WMTS maps, the layers may have different scales and still be EPSG:3857 projected. In the implementation you have to handle all of this.

to be clearer I think that WMTSMap is already very very close to a generic tiled map implementation

I don't think so. The WMTS map implementation is very WMTS-centric. It is one of the most complex, but I would not call it generic... And it definitely will not fit any offline map. This is why you have to go through all the formats to see that different map format authors used very different approaches (many of them completely wrong...) when designing the maps.

Even the current "independent" map system is tricky in some aspects as there are always some maps that do not fit some generic rules (do not have a known projection, do not have known bounds, ...) and it took several iterations (see the latest map API refactoring as an example) to make it not a complete mess.

So what I'm trying to say - you have to go through all of the map formats, understand how and why they work the way they work and then try to design some generic system. Starting with only WMTS/OSM maps and MBTiles is simply not enough.

eddy-geek commented 1 year ago

it took several iterations (see the latest map API refactoring as an example) to make it not a complete mess.

I guess you're talking about https://github.com/tumic0/GPXSee/commit/97e12d809f4a129c454587c98a76ca447aa59cb5 ? I wanted to ask, why move the hidpi change logic into load()? BTW it seems to have broken non-hidpi mode for me (opened #502 as this thread is long enough :stuck_out_tongue: )

The map tiles are the same (= cover the same area), but the coordinate system is different and the zoom/layer numbering is often also different

Yes, zoom is the one mess we still haven't discussed at all. What does it mean for two maps to be compatible layers? if we just compare projection and transform then the comparison is only valid at a single zoom-level, is that acceptable? otherwise do we need each map to provide an array of transforms per zoom-level? (& even all projections to handle case 2c below?)

In that case how to we assess compatibility of different set of scales / zoom levels? based on my use-case I would say

you have to go through all of the map formats, understand how and why they work the way they work and then try to design some generic system

OK, trying to progress, I had a look at all the ~20 supported maps and, well, the creativity (or NIH syndrome?) of format "inventors" is astounding :-)

Trying to sort them in groups:

  1. Raster map in EPSG:3857, all tiled: OnlineMap, AQMMap, GEMFMap, MBTilesMap, OsmdroidMap, SqliteMap
  2. Raster maps with a custom projection: BSBMap GeoTIFFMap OruxMap OziMap/Atlas RMap WMTS WMS WorldFileMap+prj a. Most are tiled ; BSBMap and GeoTIFFMap are not ; OziMap/Atlas can be either b. BSBMap, GeoTIFFMap, OziMap have only one layer c. OruxMap, Atlas can technically have different projections per-zoom-level even if probably not in practice
  3. Vector maps: Garmin IMG, GMAP, Mapsforge
  4. Raster map with no projection information: QCTMap
  5. Maps with the projection in settings: KMZMap, JNXMap, WorldFileMap
eddy-geek commented 1 year ago

I have also added a few samples from MOBAC, and may have reproduced your crash?

In my case it involves an OruxMap folder, somehow the subfolders are not found and map->isValid() is corrupted ?:

/home/me/.local/share/gpxsee/maps/samples/sample-orux-sqlite.otrk2.xml:
  otrk2.xml: No usable zoom level found

Thread 1 "gpxsee" received signal SIGSEGV, Segmentation fault.
0x00005555555c3405 in GUI::createMapActionsNode (this=0x555555b47840, node=..., actionGroup=0x555555c36880) at src/GUI/gui.cpp:137
137                     if (map && map->isValid()) {
(gdb) bt
#0  0x00005555555c3405 in GUI::createMapActionsNode (this=0x555555b47840, node=..., actionGroup=0x555555c36880) at src/GUI/gui.cpp:137
#1  0x00005555555e214d in GUI::loadInitialMaps (this=0x555555b47840, selected=..., selectedOverlay=...) at src/GUI/gui.cpp:2975
#2  0x00005555555c2f41 in GUI::GUI (this=0x555555b47840) at src/GUI/gui.cpp:113
#3  0x00005555555c1f4e in App::App (this=0x7fffffffc9e0, argc=@0x7fffffffc9dc: 1, argv=0x7fffffffcb28) at src/GUI/app.cpp:73
#4  0x00005555555a0c15 in main (argc=1, argv=0x7fffffffcb28) at src/main.cpp:15
(gdb) p map
$1 = (Map *) 0x555555a15f40
(gdb) p *map
$2 = {<QObject> = {<No data fields>}, static staticMetaObject = {d = {superdata = {direct = 0x7ffff7271fe0 <QObject::staticMetaObject>}, 
      stringdata = 0x555555907d00 <qt_meta_stringdata_Map>, data = 0x555555907d80 <qt_meta_data_Map>, 
      static_metacall = 0x555555805594 <Map::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0, 
      extradata = 0x0}}, _path = {d = 0x0}}
(gdb) p map->isValid()
Cannot access memory at address 0x70

I don't really see what could cause this, but I remember adding the if(map) check because of similar corrupted map issues... although when looking at maplist.cpp it can never return a non-Map of course.

tumic0 commented 1 year ago

I wanted to ask, why move the hidpi change logic into load()?

The primary motivation were the projections, but hidpi is almost the same case. You need all that info when loading the maps to build the coordinates system and when it changes, you have to recompute/re-read a lot of stuff in some maps. So when the calls were separated, you had several re-reading of the same map data during the map load.

Yes, zoom is the one mess we still haven't discussed at all. What does it mean for two maps to be compatible layers? if we just compare projection and transform then the comparison is only valid at a single zoom-level, is that acceptable? otherwise do we need each map to provide an array of transforms per zoom-level?

My imagination is, you always try to match the overlay to the current map's zoom. When there is no match, than the overlay is simply not rendered. To match the map zoom, you can simply loop over the overlay map zooms and check if the current zoom fits. However, it may turn out that this is too slow for some map type and some kind of transformations (zooms) propagation will be required.

OziMap have only one layer

OziExplorer maps have more layers when the OZF format is used for the "image".

OruxMap, Atlas can technically have different projections per-zoom-level even if probably not in practice

But you still have to be prepared for that. And I even think I have seen such a map.

Vector maps: Garmin IMG, GMAP, Mapsforge

And ENC maps. And MVT/vector MBTiles are also a kind of vector maps although they are handled as tiles at the moment. But this has its limitations and with the introduction of Qt Protobuf in Qt 6.5 I'm toying with the idea to move from QtPBFImagePlugin to a "native" implementation.

(3) I didn't have a close look, but the zoom levels are an issue there too

The problem with vector maps is, they should fit their projection to the projection of the overlay (or vice versa).

KMZ has no information about zooms scale or projection, only a LL bounding box for raster images, but from that + input projection from settings, we technically could compute a scale? then we're back with (2)

Except of QCT maps you can IMHO always get a projection and transformation.

tumic0 commented 1 year ago

I don't really see what could cause this, but I remember adding the if(map) check because of similar corrupted map issues...

The map pointer is not valid/points to some garbage in memory. But that's all I know, didn't search for how you get it in your code. In the original code, this does never happen.

eddy-geek commented 1 year ago

The map pointer is not valid/points to some garbage in memory.

I'm now pretty sure if (map->isValid()) { ...} else delete map in createMapActionsNode cannot work if called twice with the same invalid map, duh. I will try to fix my branch next week.

Of course delete'ing an external object (not RAII) and without setting map=0 is a bit of a footgun ;-) (Today I learned Why doesn't delete zero out its operand?)

edit: temporary fix https://github.com/eddy-geek/GPXSee/commit/815f83d25042bda06fb5381f2a82be033c0e0610 is online if you want to retry

(or you could remove invalid maps from you map folder :angel: )