Repository navigation
XYZ Tiles provider - #209
naulan-chrzaszcz wants to merge 3 commits into
Conversation
|
This feature is pretty raw; it was a prototype I quickly put together while I was in Albania, using the most powerful Claude Code model. |
|
I did not read the code in details, but just from a quick overview, it seems like you can quite drastically simplify it, especially by removing the re-projection. In the WMS provider, we have a CRS in the params because we need to tell to the GeoServer in which CRS we want our data. It is intended that the user inputs either the native data CRS (so the server don't reproject and our In your case, it seems like the data will always be in Additionally, keep in mind that it is completely OK for a provider to return multiple results. Even with matrices, it looks like you could return one matrix per tile instead of having to handle the merging yourself. Claude might be good at writing working code, but it's up to you to integrate it properly into the codebase (because it doesn't know about everything that is already handled by another component). If you were to write such code by hand, would you have really implemented re-projection and tile merging yourself? Probably not, because you would have known that it was tedious to do and also unnecessary ;) |
|
This feature is interesting. You should not limit it to Terrarium, many other data are provided using XYZ tiles. And as said Theo, this could be very simplified and made more Voxatile-like:
|
[Maven Build Status]📑 Commit: 📦 Download artifact: voxatile.jar |
f0860db to
95bae7f
Compare
5f62b48 to
ec221a7
Compare
| double maxY = data.offsetY() + data.sizeY() * data.cellSizeY() - 1.0; | ||
| double maxX = data.offsetX() + (data.sizeX() - 1) * data.cellSizeX(); | ||
| double maxY = data.offsetY() + (data.sizeY() - 1) * data.cellSizeY(); | ||
|
|
There was a problem hiding this comment.
Hotfix for WG84 CRS
| float x = (float) ((coordinates.x() - data.offsetX()) / data.cellSizeX()); | ||
| float y = (float) ((coordinates.y() - data.offsetY()) / data.cellSizeY()); |
There was a problem hiding this comment.
Hotfix for WG84 CRS
| <!-- TwelveMonkeys library for WebP support in ImageIO --> | ||
| <dependency> | ||
| <groupId>com.twelvemonkeys.imageio</groupId> | ||
| <artifactId>imageio-webp</artifactId> | ||
| <version>3.9.4</version> | ||
| </dependency> |
There was a problem hiding this comment.
The new endpoint of Mapterhorn use WebP.
|
Feel free to give some reviews if you'd like, @ignfab/minalac |
ec221a7 to
e2c12fe
Compare
e2c12fe to
67fdc42
Compare
|
|
||
| **Extra parameters**: | ||
| - `url` (required): URL containing the `{z}`, `{x}`, and `{y}` placeholders. Example : `https://tiles.mapterhorn.com/{z}/{x}/{y}.webp` | ||
| - `zoom` (required): Zoom level for the tiles (must be a non-negative integer) |
There was a problem hiding this comment.
Why we have to specify that ? What zoom value should I put ?
Voxatile should know better than me what zoom to use.
|
Okay, this is promising. Let's see some high-level considerations first. So, after reading your code, here is how I see it: First of all, is that correct? Let me know if I missed something important. Now, when I think about this topic, I see a different approach: So, now I have a few questions for you:
Please note that I'm asking you to write a message with your answers, not to code anything right now! |
That's important to say :D Your proposal is interesting (and promising). But before that, I think there is a serious issue with zoom that should be fixed. |
Indeed, this is completely unrelated. Whatever the global design is, the same issues about the actual XYZ providing logic will need to be considered, such as the zoom parameter you mentioned, or the tiles streaming instead of buffering... but those can be handled separately, either before, in parallel, or after. Considering I (only) asked for critical thinking and analysis, I think it can be performed in parallel to addressing initial code reviews, yes! |
|
Just a side note regarding the analysis I asked you, if it's easier for you, I don't mind if you do it in French. Doing it in English would be a good exercise as well, but I prefer you focus on quality of content rather than language. |
ca8e6b2 to
6930da0
Compare
Okay, i did it in english with a fix with ChatGPT for some mistake. Don't be mean with me. |
What do you mean by "tiles streaming instead of buffering" ? Do you have an example ? |
|
Thanks for the answer, it's a good start. However, I don't really see the answers to questions 2 and 3 (especially 3, question 2 is more to help you to understand). Since you started to code it, you can try to compare your two attempts (initial version vs new version). Don't forget to be critical (i.e. point out what you think is good vs what is bad!). Please try not to use LLMs, even if that means doing it in French, it's not a problem! ;) Regarding your last question, currently you do N requests (1 for each tile), accumulating tiles in a collection, then return the result with an iterator over that collection. This loads everything before the first tile can be processed. |
Donc, je préfère la nouvelle version, car, comme expliqué dans le tableau que j'ai créé, le provider n'est pas bridé à un seul cas d'usage, et permet, avec juste l'ajout d'autres paramètres, de créer d'autres cas d'usage de ce provider. |
Bon, tu m'excuseras, j'ai quand même testé (et push) la version dont tu as parlé dans ton message, c'était obvious que c'était mieux. |
4715b0e to
32bc8e2
Compare
32bc8e2 to
b05c64e
Compare
|
Okay, thank you for this analysis, I hope you understand the interest of doing it, and usefulness of comparing different solutions to find the most suitable one. And in the future, I encourage you to do similar things, for example when writing your PR body text, in the "reason" section, try to state what key advantages made you choose that solution over others. You can take inspiration from my PRs where I try to do that (for example #172). But this is not something to always do, it's only useful when the choices are not obvious. Anyways... Now, I'd like to point out something in your new version, to challenge it a little bit: The processor copies data from the integer matrix to a float matrix. What do you think about that? |
Thank you for the example!
Il copie pas vraiment la valeur vers la cible telle qu'il la reçoit. Vu que le processor est responsable de la transformation d'une donnée traitable vers un model, il y a pas juste une simple copie, il y a de l'adaptation ou/et de la traduction. Par exemple dans mon cas, je traduit mes valeurs RGB en Float. Donc, c'est pas juste un simple copie. |
|
Ok, donc tu dis qu'il y a une transformation + une copie. Est-ce que ces deux opérations sont indissociables ? Est-ce que les deux sont nécessaires ? La situation exacte est que : tu fais un processeur qui doit prendre en entrée des matrices géographiques d'entiers, et les transformer en modèles matrices de flottants. Dans un soucis d'optimisation, si on peut éviter de recopier des données, on préfère. Est-ce que cette situation te parait similaire à une question que tu as déjà traité à un moment donné ? Est-ce que tu pourrais t'en inspirer pour trouver une solution pour celle-ci ? |
| area: | ||
| center: | ||
| latitude: -17.995 | ||
| longitude: 178.000 |
There was a problem hiding this comment.
Attention, ici tu ne verras pas le bug du "tour du monde" car +/- 1km ne te feront pas dépasser la barrière de 180°.
| int maxY = latitudeToY(envelope.getMinY(), numTiles); | ||
|
|
||
| /* | ||
| * Handle International Date Line crossing. |
| */ | ||
| int countX = (maxX >= minX) ? (maxX - minX + 1) : (numTiles - minX + maxX + 1); | ||
|
|
||
| List<IntegerGeographicDataMatrix2d> tiles = new ArrayList<>(); |
There was a problem hiding this comment.
C'est ici que tu fais le choix de tout lire avant de pouvoir traiter la première tuile. Ce qui n'est pas très optimisé.
| } | ||
| } | ||
|
|
||
| return new SimpleResult<>(CRS, tiles.iterator()); |
There was a problem hiding this comment.
Et là, tu retourne un itérateur (ce qui est demandé), mais on n'a l'itérateur qu'une fois la dernière tuile lue, alors que le premier next n'en a pas besoin.
| * It mathematically breaks beyond ~85.0511 degrees latitude (Logarithm of zero/infinity). | ||
| * So we clamp the latitude to this exact maximum limit. | ||
| */ | ||
| double clampedLat = Math.max(-MAX_MERCATOR_LATITUDE, Math.min(MAX_MERCATOR_LATITUDE, lat)); |
There was a problem hiding this comment.
Attention, ici au lieu de dire "je n'ai pas de données au delà de 85°", tu dis "Ok voilà les données" en revoyant une autre latitude.
| int tileSize; | ||
| try (InputStream stream = URI.create(url.replace("{z}", String.valueOf(zoomMin)) | ||
| .replace("{x}", "0") | ||
| .replace("{y}", "0")).toURL().openStream()) { | ||
| tileSize = ImageIO.read(stream).getWidth(); | ||
| } catch (Exception e) { | ||
| throw new GenerationFailedException(e); | ||
| } |
There was a problem hiding this comment.
Remplacer ca pour un fallback du zoom jusqu'à trouver celui qui fonctionne
There was a problem hiding this comment.
Il va y avoir une question de priorité de traitement des modèles dans le populateHeightmap ensuite puisque tu risques d'avoir des tuiles qui se recouvrent si tu as différents niveaux de zoom.
| int x = (minX + stepX) % numTiles; | ||
|
|
||
| BufferedImage image; | ||
| try (InputStream stream = URI.create(url.replace("{x}", String.valueOf(x)) |
There was a problem hiding this comment.
Before trying to fetch tile, it could be a good thing to check it actually intersects envelope. If not, proceed to next one, that will avoid useless network and memory consumption.



Changes
Add a new provider which handle a new data source which provide images given the elevations with special encoding and with XYZ Tiles endpoint.
Technical changes
Add a new provider
XYZTilesProviderwith her parameters, which download images and store it into a newFloatBufferedImageMatrixmodel which can handle many format to get elevations.This model can only supported (for now) the terrarium format handle by
TerrariumElevationDecoderclass.Showcase
Reason
For Albanian and world wide DTM rendering.
TODOs
Self-checks
/docsfolder has been updatedexamples/work the same (or have been adapted if subject to changes in this PR)