From 6fa1aa702533cca3fc3330eef95ae70fc686a60b Mon Sep 17 00:00:00 2001 From: konggdev Date: Thu, 16 Apr 2026 17:25:14 +0200 Subject: [PATCH] Abstract style model further from MapLibre --- .gitignore | 1 + .../assets/bundled/style/satelite.style.json | 3 +- .../strikemaps/data/helper/FileHelper.java | 2 +- .../data/helper/UserPrefsHelper.java | 2 +- .../strikemaps/map/layer/MapLayer.java | 17 ++--- .../strikemaps/map/layer/SourcedMapLayer.java | 16 ++++ .../strikemaps/map/overlay/MapOverlay.java | 4 +- .../implementation/LocationOverlay.java | 76 +++++++++++++------ .../implementation/PointSelectionOverlay.java | 3 +- .../strikemaps/map/renderer/MapRenderer.java | 1 + .../MapLibreNativeRenderer.java | 41 ++++++---- .../strikemaps/map/style/MapStyle.java | 5 +- 12 files changed, 115 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/eu/konggdev/strikemaps/map/layer/SourcedMapLayer.java diff --git a/.gitignore b/.gitignore index 825f06d..efcbf62 100755 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ DS_Store .cxx local.properties /legacy_code/ +maptiler* diff --git a/app/src/main/assets/bundled/style/satelite.style.json b/app/src/main/assets/bundled/style/satelite.style.json index bf2e166..a24d4b7 100644 --- a/app/src/main/assets/bundled/style/satelite.style.json +++ b/app/src/main/assets/bundled/style/satelite.style.json @@ -5,6 +5,7 @@ "sources": { "satelite": { "type": "raster", + "schema" : "raster", "tiles": [ "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" ], @@ -14,8 +15,6 @@ "tileSize": 512 } }, - "sprite": "", - "glyphs": "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf", "layers": [ { "id": "satelite", diff --git a/app/src/main/java/eu/konggdev/strikemaps/data/helper/FileHelper.java b/app/src/main/java/eu/konggdev/strikemaps/data/helper/FileHelper.java index 32ffaaa..6d323d0 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/data/helper/FileHelper.java +++ b/app/src/main/java/eu/konggdev/strikemaps/data/helper/FileHelper.java @@ -106,4 +106,4 @@ public final class FileHelper { return fileList.toArray(new String[0]); } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/konggdev/strikemaps/data/helper/UserPrefsHelper.java b/app/src/main/java/eu/konggdev/strikemaps/data/helper/UserPrefsHelper.java index 085de01..0192158 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/data/helper/UserPrefsHelper.java +++ b/app/src/main/java/eu/konggdev/strikemaps/data/helper/UserPrefsHelper.java @@ -37,4 +37,4 @@ public final class UserPrefsHelper { public static boolean lastLocationEnabled(SharedPreferences prefs, boolean status) { return prefs.edit().putBoolean(KEY_LAST_LOCATION_ENABLED, status).commit(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/layer/MapLayer.java b/app/src/main/java/eu/konggdev/strikemaps/map/layer/MapLayer.java index 0b5ad2d..a9d9970 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/layer/MapLayer.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/layer/MapLayer.java @@ -1,16 +1,11 @@ package eu.konggdev.strikemaps.map.layer; -import org.maplibre.android.style.layers.Layer; -import org.maplibre.android.style.sources.GeoJsonSource; +import com.fasterxml.jackson.databind.JsonNode; - -//FIXME: Get rid of reliance on MapLibre! -//Most likely implement an "AdditionalMapLayer" or something of that sorts (?) public class MapLayer { - public GeoJsonSource source; - public Layer layer; - public MapLayer(GeoJsonSource source, Layer layer) { - this.source = source; - this.layer = layer; + public JsonNode layer; + + public MapLayer(JsonNode layer) { + this.layer = layer; } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/layer/SourcedMapLayer.java b/app/src/main/java/eu/konggdev/strikemaps/map/layer/SourcedMapLayer.java new file mode 100644 index 0000000..44a4237 --- /dev/null +++ b/app/src/main/java/eu/konggdev/strikemaps/map/layer/SourcedMapLayer.java @@ -0,0 +1,16 @@ +package eu.konggdev.strikemaps.map.layer; + +import com.fasterxml.jackson.databind.JsonNode; +import eu.konggdev.strikemaps.map.source.MapSource; + +public class SourcedMapLayer { + public String key; + public MapSource source; + public JsonNode layer; + + public SourcedMapLayer(String key, MapSource source, JsonNode layer) { + this.key = key; + this.source = source; + this.layer = layer; + } +} diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/overlay/MapOverlay.java b/app/src/main/java/eu/konggdev/strikemaps/map/overlay/MapOverlay.java index 3d55939..957d6c5 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/overlay/MapOverlay.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/overlay/MapOverlay.java @@ -1,8 +1,8 @@ package eu.konggdev.strikemaps.map.overlay; -import eu.konggdev.strikemaps.map.layer.MapLayer; +import eu.konggdev.strikemaps.map.layer.SourcedMapLayer; /* More or less a data-driven layer factory */ public interface MapOverlay { - public MapLayer makeLayer(); + public SourcedMapLayer makeLayer(); } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/LocationOverlay.java b/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/LocationOverlay.java index 64922cf..f45f643 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/LocationOverlay.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/LocationOverlay.java @@ -6,18 +6,18 @@ import android.location.LocationListener; import androidx.annotation.NonNull; import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.map.MapComponent; -import eu.konggdev.strikemaps.map.layer.MapLayer; - +import eu.konggdev.strikemaps.map.layer.SourcedMapLayer; import eu.konggdev.strikemaps.map.overlay.MapOverlay; +import eu.konggdev.strikemaps.map.source.MapSource; + import eu.konggdev.strikemaps.data.provider.LocationDataProvider; -import org.maplibre.android.style.layers.CircleLayer; -import org.maplibre.android.style.layers.Property; -import org.maplibre.android.style.sources.GeoJsonSource; import org.maplibre.geojson.Feature; import org.maplibre.geojson.FeatureCollection; import org.maplibre.geojson.Point; -import static org.maplibre.android.style.layers.PropertyFactory.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.ArrayNode; public class LocationOverlay implements MapOverlay, LocationListener { LocationDataProvider locationDataProvider; @@ -33,25 +33,55 @@ public class LocationOverlay implements MapOverlay, LocationListener { } @Override - public MapLayer makeLayer() { - GeoJsonSource source = new GeoJsonSource( - "location", - FeatureCollection.fromFeatures(new Feature[]{}) // empty - ); + public SourcedMapLayer makeLayer() { + MapSource source = new MapSource(); - if (currentLocation != null) - source.setGeoJson(Feature.fromGeometry(Point.fromLngLat(currentLocation.getLongitude(), currentLocation.getLatitude()))); + source.type = "geojson"; - CircleLayer layer = new CircleLayer("location", "location"); - layer.setProperties( - circleRadius(5f), - circleColor(Color.parseColor("#1E88E5")), - circleStrokeColor(Color.WHITE), - circleStrokeWidth(1.5f), - circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP) - ); + ObjectMapper mapper = new ObjectMapper(); + try { + ObjectNode data = mapper.createObjectNode(); + data.put("type", "Feature"); - return new MapLayer(source, layer); + if(currentLocation != null) { + ObjectNode geometry = mapper.createObjectNode(); + geometry.put("type", "Point"); + + ArrayNode coordinates = mapper.createArrayNode(); + coordinates.add(currentLocation.getLongitude()); + coordinates.add(currentLocation.getLatitude()); + + geometry.set("coordinates", coordinates); + data.set("geometry", geometry); + data.set("properties", mapper.createObjectNode()); + } + source.data = data; + } catch (Exception e) { + e.printStackTrace(); + } + + ObjectNode layer = mapper.createObjectNode(); + layer.put("id", "location"); + layer.put("type", "circle"); + layer.put("source", "location"); + + ObjectNode paint = mapper.createObjectNode(); + paint.put("circle-radius", 5); + paint.put("circle-color", "#1E88E5"); + paint.put("circle-stroke-color", "#FFFFFF"); + paint.put("circle-stroke-width", 1.5); + + layer.set("paint", paint); + + ObjectNode layout = mapper.createObjectNode(); + layout.put("circle-pitch-alignment", "map"); + + layer.set("layout", layout); + + ArrayNode layers = mapper.createArrayNode(); + layers.add(layer); + + return new SourcedMapLayer("location", source, layers); } @Override @@ -59,4 +89,4 @@ public class LocationOverlay implements MapOverlay, LocationListener { this.currentLocation = location; map.onOverlayUpdate(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/PointSelectionOverlay.java b/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/PointSelectionOverlay.java index 152f480..3fcd503 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/PointSelectionOverlay.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/overlay/implementation/PointSelectionOverlay.java @@ -1,11 +1,12 @@ package eu.konggdev.strikemaps.map.overlay.implementation; import eu.konggdev.strikemaps.map.layer.MapLayer; +import eu.konggdev.strikemaps.map.layer.SourcedMapLayer; import eu.konggdev.strikemaps.map.overlay.MapOverlay; public class PointSelectionOverlay implements MapOverlay { @Override - public MapLayer makeLayer() { + public SourcedMapLayer makeLayer() { return null; } } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/MapRenderer.java b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/MapRenderer.java index e77613d..55f086c 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/MapRenderer.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/MapRenderer.java @@ -15,5 +15,6 @@ public interface MapRenderer { View getView(); + //TODO: Get rid of MapLibre Feature class dependence List featuresAtPoint(LatLng point); } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java index 83be451..e12445c 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java @@ -4,11 +4,13 @@ import android.view.View; import androidx.annotation.NonNull; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import eu.konggdev.strikemaps.data.helper.UserPrefsHelper; import eu.konggdev.strikemaps.map.overlay.MapOverlay; -import eu.konggdev.strikemaps.map.layer.MapLayer; +import eu.konggdev.strikemaps.map.layer.SourcedMapLayer; import eu.konggdev.strikemaps.map.renderer.MapRenderer; import eu.konggdev.strikemaps.map.style.MapStyle; import org.maplibre.android.MapLibre; @@ -39,25 +41,38 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { mapView.getMapAsync(this); } - void passLayer(MapLayer layer) { - map.getStyle().addSource(layer.source); - map.getStyle().addLayer(layer.layer); - } - @Override public void reload() { ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); MapStyle style = controller.style; try { + /* Take metadata from MapStyle + everything outside sources, layers */ ObjectNode root = style.metadata.deepCopy(); - root.set("sources", mapper.valueToTree(style.sources)); - root.set("layers", style.layerDefinitions); - map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(root)), intStyle -> { - for(MapOverlay overlay : controller.overlays.values()) { - passLayer(overlay.makeLayer()); - } - }); + + //Sources + ObjectNode sources = mapper.createObjectNode(); + style.sources.forEach((k, v) -> sources.set(k, mapper.valueToTree(v))); + + //Layers + ArrayNode layers = mapper.createArrayNode(); + layers.addAll((ArrayNode) style.layerDefinitions); + + //Overlays + for (MapOverlay overlay : controller.overlays.values()) { + SourcedMapLayer overlayLayer = overlay.makeLayer(); + sources.set(overlayLayer.key, mapper.valueToTree(overlayLayer.source)); + layers.addAll((ArrayNode) overlayLayer.layer); + } + + //Set all to root + root.set("sources", sources); + root.set("layers", layers); + + map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(root))); } catch (Exception e) { + app.logcat("Failed to reload Map"); e.printStackTrace(); } } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java b/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java index 379ee7a..3bd701a 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.data.helper.FileHelper; import eu.konggdev.strikemaps.map.source.MapSource; @@ -19,7 +20,7 @@ public class MapStyle { public JsonNode metadata; // everything except layers + sources public Map sources; - public JsonNode layerDefinitions; // the "layers" array + public ArrayNode layerDefinitions; // the "layers" array //FIXME public static MapStyle fromMapLibreJsonFile(String filename, AppController app) { @@ -40,7 +41,7 @@ public class MapStyle { new TypeReference>() {} ); - style.layerDefinitions = root.path("layers"); + style.layerDefinitions = root.withArray("layers"); ObjectNode metadata = root.deepCopy(); metadata.remove("layers");