Adaptive hill shading#1592
Conversation
* Adaptive hill shading (AdaptiveClasyHillShading): Hill shading can now be rendered efficiently at all zoom levels (screenshots below).
- Adaptive hill shading algorithm autonomously decides min/max zoom levels by default (others continue to work as before)
- It will dynamically decide on the resolution and quality of the output depending on the display parameters, to maximize efficiency
- Min/max zoom levels can be enforced via code
* Hill shading color feature. Color for the hill shading can now be set via render theme, or overriden by the code.
* New <hillshading> render theme element attribute: "color".
* Deprecated the "always" attribute of the <hillshading> render theme element, it's obsolete.
* Significantly improved HgtCache: It will now constraint based on memory usage and also prevent unconstrained cache hammering and resulting excessive memory usage and possible OOM exceptions in extreme situations.
* HgtFileInfo class was made a standalone class.
* AbsShadingAlgorithmDefaults name was simplified (renamed) to AShadingAlgorithm.
* Hill shading bitmaps are now synchronized where needed (prevents various artifacts, at least on Android).
* Fix (AWT): Hill shading does not use AwtLuminanceShadingComposite any more, it's obsolete. It was used in the past to reduce the effect of the "neutral hills" gray overlay, which is not present any more. This change also improves performance of the hill shading on AWT, and restores symmetry with Android.
* Multithreaded indexing of DEM files
| */ | ||
| package org.mapsforge.core.graphics; | ||
|
|
||
| public abstract class ABitmap implements Bitmap { |
There was a problem hiding this comment.
@Sublimis Let's use a better name, like BaseBitmap.
|
|
||
| public abstract class ABitmap implements Bitmap { | ||
|
|
||
| protected final Object Mutex = new Object(); |
| private Point origin; | ||
| private volatile BoundingBox boundingBox; | ||
| private volatile Point origin; | ||
| private transient final Object mSync = new Object(); |
There was a problem hiding this comment.
@Sublimis We do not use the prefix m in names in Mapsforge.
| double sinLatitude = Math.sin(Math.toRadians(latitude)); | ||
| // FIXME improve this formula so that it works correctly without the clipping | ||
| double pixelY = (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) * mapSize; | ||
| // double pixelY = (0.5 - Math.log(Math.tan(Math.toRadians(45 + latitude / 2))) / (2 * Math.PI)) * mapSize; |
There was a problem hiding this comment.
Some leftover, sorry, while trying to fix something. It is just another way of doing the exact same calculation in the line above.
| * (2024: Deprecated, should not be used) | ||
| */ | ||
| public static boolean LUMINANCE_COMPOSITE = true; | ||
| public static final boolean LUMINANCE_COMPOSITE = false; |
There was a problem hiding this comment.
@Sublimis Parameters cannot be final. Either keep it non final or remove the LUMINANCE_COMPOSITE.
| public static final boolean LUMINANCE_COMPOSITE = false; | ||
|
|
||
| public static final int HILLSHADING_MAGNITUDE_DEFAULT = 128; | ||
| public static final int HILLSHADING_COLOR_DEFAULT = 0xff000000; |
There was a problem hiding this comment.
@Sublimis These are not parameters, but constants. They should be moved to the class that uses them.
| */ | ||
| public static int NUMBER_OF_THREADS = Runtime.getRuntime().availableProcessors() + 1; | ||
|
|
||
| public static final long MAX_MEMORY_MB = Runtime.getRuntime().maxMemory() / 1000 / 1000; |
There was a problem hiding this comment.
@Sublimis This is not a parameter, but a calculated constant. It should be moved to the class that uses it.
Also, shouldn't the calculation be done with `1024'?
There was a problem hiding this comment.
What if more than one class wants to use a constant, shouldn't there be some class holding constants?
Can we create it?
https://en.wikipedia.org/wiki/Megabyte
There was a problem hiding this comment.
@Sublimis Parameters is not the class to hold constants.
We can create a Constants class for such purpose.
|
|
||
| // One 1" HGT file converted to a same-sized bitmap is about 13 MB, for high-quality this is 52 MB. | ||
| // For ultra-low-quality while rendering wide zoom in adaptive mode, bitmap size per 1" HGT file can be as low as a few hundred bytes. | ||
| protected final long CacheMaxBytes = Parameters.MAX_MEMORY_MB * 1000 * 1000 / 10; |
There was a problem hiding this comment.
@Sublimis Shouldn't the calculation be done with `1024'?
There was a problem hiding this comment.
https://en.wikipedia.org/wiki/Megabyte
It also doesn't matter what is the exact constant, as those are all just rules of thumbs.
| byte layer = 5; | ||
| short magnitude = 128; | ||
| boolean always = false; | ||
| int magnitude = Parameters.HILLSHADING_MAGNITUDE_DEFAULT; |
There was a problem hiding this comment.
@Sublimis RenderThemeHandler.HILLSHADING_MAGNITUDE should also become int.
There was a problem hiding this comment.
It is an int, public static final int HILLSHADING_MAGNITUDE_DEFAULT = 128; ?
There was a problem hiding this comment.
@Sublimis I wrote about the RenderThemeHandler.HILLSHADING_MAGNITUDE to become an int:
public static short HILLSHADING_MAGNITUDE = -1;| private static final String demUseFiles = "demFolderFiles"; | ||
| private static final int SELECT_DEM_FOLDER = 0; | ||
|
|
||
| @SuppressWarnings("deprecation") |
| } | ||
|
|
||
| private void customizeConfig(MemoryCachingHgtReaderTileSource hillTileSource) { | ||
| hillTileSource.setEnableInterpolationOverlap(true); |
There was a problem hiding this comment.
@Sublimis This empty method should be removed.
| </xs:attribute> | ||
| <!-- customization unlikely to make any sense --> | ||
| <xs:attribute name="layer" default="5" type="xs:unsignedByte" use="optional" /> | ||
| <xs:attribute name="always" default="false" type="xs:boolean" use="optional"> |
There was a problem hiding this comment.
@Sublimis Let's remove this non working attribute.
XML Schema Definition should only contain the working attributes.
There was a problem hiding this comment.
This would immediately break the validity of all render themes that already use the "always" attribute, wouldn't it? That's why there should be some grace period...
There was a problem hiding this comment.
@Sublimis It doesn't matter, as render themes should still work.
XML validation can be checked at any time by the theme authors.
| POLYGON_EXCEPTIONS.add(new Tag("freizeitkarte", "land")); | ||
| } | ||
|
|
||
| public static final class Constants { |
There was a problem hiding this comment.
@Sublimis Let's make Constants a separate class.
| * Use AwtLuminanceShadingComposite or AlphaComposite. | ||
| * (2024: Deprecated, should not be used) | ||
| */ | ||
| public static final boolean LUMINANCE_COMPOSITE = false; |
There was a problem hiding this comment.
@Sublimis As it cannot be used anymore, we can remove it.
And also remove the AwtLuminanceShadingComposite class.
|
@Sublimis Thanks, very nice work! |
|
Should we do another PR for those final details about constants etc. or do you think it's fine the way it is now? |
|
Screenshots update |
@Sublimis Thanks, very nice! 👍 |
|
There are reports for ANR or crash with the hillshading lately. Although we have not seen anything strange in Cruiser so far. |
fyi, at least that one is from before Adaptive hillshading was merged @Sublimis however I found some other issues with adaptive hillshading, documented in cgeo/cgeo#16438 (comment), maybe you could take a look / make a statement on that? |
AdaptiveClasyHillShading): Hill shading can now be rendered efficiently at all zoom levels (screenshots below).<hillshading>render theme element attribute:"color"."always"attribute of the<hillshading>render theme element, it's obsolete.HgtCache: It will now constraint based on memory usage and also prevent unconstrained cache hammering and resulting excessive memory usage and possible OOM exceptions in extreme situations.HgtFileInfoclass was made a standalone class.AbsShadingAlgorithmDefaultsname was simplified (renamed) toAShadingAlgorithm.AwtLuminanceShadingCompositeany more, it's obsolete. It was used in the past to reduce the effect of the "neutral hills" gray overlay, which is not present any more. This change also improves performance of the hill shading on AWT, and restores symmetry with Android.