Rendering Large High Res Maps to PNG Images
Intro
One of the default mapping applications people need is rendering out large high resolution maps based on a bounding box at a zoom and detail greater than when just viewing within the web map. This behavior of wanting more resolution and features to be rendered out from a given map occurs due to the fact we often use PDF/PNG renders as their own map, zooming in at greater detail to the features we need.
Often times the resolution these maps get rendered out far exceeds any monitor on your desktop also adding to the difficulty. How can we fix this pretty annoying issue for web maps so that a user can render out a high resolution image easily?
Open Source Tooling
I first looked for open source libraries that do what I want but I got varied results most libraries used an electron/headless chrome or some other sort of browser automation to actually render out images. However these images, do not have the higher zoom behavior I desired for my output maps. Meaning, the box selected to make a map from was very large causing features at the higher zooms to be dropped as that is the only way the bounding box would fit on the map.
Map Canvas PNG
I did however find a way to render out a data URL png for the exact map that is rendered on the screen at any given time. This map interface would be the basis of how I would abstract out rendering at a higher resolution.
Tiling - Keep it Simple
As you might have guessed the basis of what I did was get the tiles intersecting my desired bounding box. For each tile I gave it a x and y coordinate relative to its place in the global image. If either width or height was greater than a maximum dimension drop one zoom and try again.
After your tile grid is established you shrink the map on the client to 512x512 exactly. (the actual size of a tile at an exact zoom) Then you can walk through each tile zooming to the center and setting the zoom calculated. After we wait for the tile data to load for a second, we get that canvas image and store it in the tile object along with other metadata. (x and y position, center coordinate etc.)
After that, we have all our sub images in base64 png urls, we create a png that is the size of our x dimension image width512 and our y dimension image width512. So a 3x6 tile grid would be 1536px by 3072px. After that we walk through each tile reading in the tile data. Then iterating through each pixel in that tile image, and setting the global combined image pixel that corresponds to that particular pixel in that subtile. This process relies on some very simple arithmetic, based on the relative x/y subtile position. Finally rendering out a combined png.
Previous Problems
Bounding Box Trimming - Fixed
Implemented bounding box cropping after I get the combined PNG
The first omission is that I don't trim the total image once I'm finished. So we are really not trimming by a bounding box but the tiles intersecting that bounding box. Which is always a little bigger. This can be fixed, just not worth the trouble currently.
Label Text Clipping - Fixed
The next issue is a little more nuanced. Sometimes when a label is at an edge of the map that was taken for the sub map it truncates a label or a piece of text off the edge leaving a weird place where the images are spliced together where half a label appears. This can probably be fixed by editing the map style when I'm rendering out these sub map images.
Solution
This solution was a true dark to do correctly basically the key was to make your actual width a little bigger on the right say 64 pixels then have a map line layer represent the tile boundaries, then put an invisible text label basically all the way along that invisible tile boundary. This prevented any labels or symbology from cross that boundary!