From 35c8ec3a6837498317bb404a926ecd1e439610a2 Mon Sep 17 00:00:00 2001 From: konggdev Date: Thu, 16 Apr 2026 00:49:02 +0200 Subject: [PATCH] Further work on MapStyle --- .../konggdev/strikemaps/map/MapComponent.java | 5 +- .../strikemaps/map/layer/MapLayer.java | 16 ++++++ .../MapLibreNativeRenderer.java | 25 ++++++--- .../strikemaps/map/source/MapSource.java | 13 ++--- .../strikemaps/map/style/MapStyle.java | 52 +++++++++++-------- .../ui/element/item/GenericItem.java | 30 +++-------- .../popup/FragmentMapChangePopup.java | 14 ++--- 7 files changed, 86 insertions(+), 69 deletions(-) create mode 100644 app/src/main/java/eu/konggdev/strikemaps/map/layer/MapLayer.java diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java b/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java index 422c4cf..0d807fd 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java @@ -5,6 +5,7 @@ import eu.konggdev.strikemaps.Component; import eu.konggdev.strikemaps.factory.AlertDialogFactory; import eu.konggdev.strikemaps.helper.UserPrefsHelper; import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer; +import eu.konggdev.strikemaps.map.style.MapStyle; import org.maplibre.android.geometry.LatLng; import org.maplibre.geojson.Feature; @@ -18,7 +19,7 @@ public class MapComponent implements Component { MapRenderer mapRenderer; AppController app; - public String style; + public MapStyle style; public Map, MapOverlay> overlays = new HashMap<>(); public MapComponent(AppController ref) { this.app = ref; @@ -37,7 +38,7 @@ public class MapComponent implements Component { return new FragmentLayoutContentMap(mapRenderer.getView()); } - public void setStyle(String style) { + public void setStyle(MapStyle style) { this.style = style; mapRenderer.reload(); } 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 new file mode 100644 index 0000000..900259e --- /dev/null +++ b/app/src/main/java/eu/konggdev/strikemaps/map/layer/MapLayer.java @@ -0,0 +1,16 @@ +package eu.konggdev.strikemaps.map.layer; + +import org.maplibre.android.style.layers.Layer; +import org.maplibre.android.style.sources.GeoJsonSource; + + +//FIXME: Get rid of reliance on MapLibr +//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; + } +} \ No newline at end of file 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 a8da472..2b09d40 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,14 @@ import android.view.View; import androidx.annotation.NonNull; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import eu.konggdev.strikemaps.helper.FileHelper; import eu.konggdev.strikemaps.helper.UserPrefsHelper; import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.layer.MapLayer; import eu.konggdev.strikemaps.map.renderer.MapRenderer; +import eu.konggdev.strikemaps.map.style.MapStyle; import org.maplibre.android.MapLibre; import org.maplibre.android.geometry.LatLng; import org.maplibre.android.maps.MapLibreMap; @@ -44,11 +47,20 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { @Override public void reload() { - map.setStyle(new Style.Builder().fromJson(controller.style), style -> { - for(MapOverlay overlay : controller.overlays.values()) { - passLayer(overlay.makeLayer()); - } - }); + ObjectMapper mapper = new ObjectMapper(); + MapStyle style = controller.style; + try { + 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()); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } } @Override @@ -65,7 +77,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { public void onMapReady(@NonNull MapLibreMap maplibreMap) { this.map = maplibreMap; - controller.style = FileHelper.loadStringFromAssetFile(UserPrefsHelper.startupMapStyle(app.getPrefs()), app); + controller.setStyle(MapStyle.fromMapLibreJsonFile(UserPrefsHelper.startupMapStyle(app.getPrefs()), app)); //I have my own implementation of attribution that credits MapLibre among others, it's not as bad as it looks :) map.getUiSettings().setLogoEnabled(false); @@ -73,6 +85,5 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { map.addOnMapClickListener(point -> controller.onMapClick(point)); map.addOnMapLongClickListener(point -> controller.onMapLongClick(point)); - this.reload(); } } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java b/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java index 22fcb50..0aba3c4 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java @@ -1,12 +1,9 @@ package eu.konggdev.strikemaps.map.source; public class MapSource { - public final String url; - public final String type; - public final String schema; - public MapSource(String url, String type, String schema) { - this.url = url; - this.type = type; - this.schema = schema; - } + public String url; + public String type; + public String schema; + + public MapSource() { } } 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 943bfd6..76e8cd5 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 @@ -1,25 +1,27 @@ package eu.konggdev.strikemaps.map.style; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import com.fasterxml.jackson.annotation.JsonIgnore; 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 eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.helper.FileHelper; -import eu.konggdev.strikemaps.map.layer.MapLayer; import eu.konggdev.strikemaps.map.source.MapSource; +import org.json.JSONObject; + import java.util.*; - public class MapStyle { - public int version; + //Only local data public String name; - public String icon; - public String description; + public Bitmap icon; + public JsonNode metadata; // everything except layers + sources public Map sources; - public List layers; - - public JsonNode raw; + public JsonNode layerDefinitions; // the "layers" array //FIXME public static MapStyle fromMapLibreJsonFile(String filename, AppController app) { @@ -30,34 +32,38 @@ public class MapStyle { ObjectMapper mapper = new ObjectMapper(); try { JsonNode root = mapper.readTree(styleContents); - MapStyle style = new MapStyle(); - style.version = root.path("version").asInt(); + MapStyle style = new MapStyle(); style.name = root.path("name").asText(); - style.icon = root.path("icon").asText(); - style.description = root.path("description").asText(); + style.icon = getIcon(root.path("icon").asText(), app); style.sources = mapper.convertValue( root.path("sources"), new TypeReference>() {} ); - style.layers = new ArrayList<>(); - for (JsonNode layerNode : root.path("layers")) { + style.layerDefinitions = root.path("layers"); - MapLayer layer = mapper.treeToValue(layerNode, MapLayer.class); - - layer.raw = layerNode; // IMPORTANT - - style.layers.add(layer); - } - - style.raw = root; // full backup + ObjectNode metadata = root.deepCopy(); + metadata.remove("layers"); + metadata.remove("sources"); + style.metadata = metadata; return style; - } catch ( Exception e ) { + } catch (Exception e) { e.printStackTrace(); } return null; } + + public static Bitmap getIcon(String iconLocator, AppController app) { + switch(iconLocator.split("//")[0]) { + //TODO: https + case "assets:": + return BitmapFactory.decodeStream(FileHelper.openAssetStream("bundled/icon/" + iconLocator.split("//")[1], app)); + default: + app.logcat("Unimplemented icon locator space: " + iconLocator); + return null; + } + } } diff --git a/app/src/main/java/eu/konggdev/strikemaps/ui/element/item/GenericItem.java b/app/src/main/java/eu/konggdev/strikemaps/ui/element/item/GenericItem.java index af2512e..ecbeb43 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/ui/element/item/GenericItem.java +++ b/app/src/main/java/eu/konggdev/strikemaps/ui/element/item/GenericItem.java @@ -11,8 +11,8 @@ import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.helper.FileHelper; import eu.konggdev.strikemaps.map.MapComponent; +import eu.konggdev.strikemaps.map.style.MapStyle; import eu.konggdev.strikemaps.ui.UIComponent; -import org.json.JSONObject; import java.io.InputStream; @@ -21,6 +21,7 @@ public class GenericItem implements UIItem { public Bitmap image; public Runnable onClick; boolean hasImage; + public GenericItem(String refName) { this.name = refName; hasImage = false; @@ -42,28 +43,11 @@ public class GenericItem implements UIItem { hasImage = true; } - //FIXME: Ugly glue static constructor - public final static GenericItem fromStyle(String style, AppController app, MapComponent map) { - try { - JSONObject styleJson = new JSONObject(style); - String name = "Unknown"; //Fallback name - if (styleJson.has("name")) name = styleJson.getString("name"); - if (styleJson.has("icon")) { - switch(styleJson.getString("icon").split("//")[0]) { - //TODO: https - case "assets:": - Bitmap icon = BitmapFactory.decodeStream(FileHelper.openAssetStream("bundled/icon/" + styleJson.getString("icon").split("//")[1], app)); - return new GenericItem(name, icon, () -> map.setStyle(style)); - default: - app.logcat("Unimplemented icon source requested in style: " + name); - return new GenericItem(name, () -> map.setStyle(style)); - } - } - return new GenericItem(name, () -> map.setStyle(style)); - } catch (Exception e) { - e.printStackTrace(); - return new GenericItem("Exception!", () -> map.setStyle(style)); - } + public final static GenericItem fromStyle(MapStyle style, MapComponent map) { + if(style == null) return new GenericItem("Unknown"); + if(style.icon != null) + return new GenericItem(style.name, style.icon, () -> map.setStyle(style)); + return new GenericItem(style.name, () -> map.setStyle(style)); } public View makeView(UIComponent spawner) { View v = spawner.inflateUi(R.layout.item_generic); diff --git a/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java b/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java index 8bf547f..7e28898 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java +++ b/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java @@ -13,11 +13,13 @@ import eu.konggdev.strikemaps.helper.FileHelper; import eu.konggdev.strikemaps.factory.AlertDialogFactory; import eu.konggdev.strikemaps.map.MapComponent; +import eu.konggdev.strikemaps.map.style.MapStyle; import eu.konggdev.strikemaps.ui.UIComponent; import eu.konggdev.strikemaps.ui.element.item.GenericItem; import org.apache.commons.lang3.ArrayUtils; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; @@ -35,6 +37,7 @@ public class FragmentMapChangePopup extends Fragment implements Popup { this.ui = app.getUi(); this.region = region; } + @Override public Integer getRegion() { return region; @@ -50,12 +53,11 @@ public class FragmentMapChangePopup extends Fragment implements Popup { //FIXME setupButton(view, R.id.closeButton, click(() -> ui.getCurrentScreen().closePopup())); setupDragHandle(view, view, () -> ui.getCurrentScreen().closePopup()); - String[] stylePaths = ArrayUtils.addAll(FileHelper.getAssetFiles("bundled/style", ".style.json", app), FileHelper.getUserFiles("style", ".style.json", app)); - List views = new ArrayList<>(); + List stylePaths = new ArrayList<>(); + stylePaths.addAll(Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app))); + stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app))); LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout); - for(String i : stylePaths) { - if(i.startsWith("/storage")) stylesLayout.addView(GenericItem.fromStyle(FileHelper.loadStringFromUserFile(i), app, map).makeView(ui)); - else stylesLayout.addView(GenericItem.fromStyle(FileHelper.loadStringFromAssetFile(i, app), app, map).makeView(ui)); - } + for (String style : stylePaths) + stylesLayout.addView(GenericItem.fromStyle(MapStyle.fromMapLibreJsonFile(style, app), map).makeView(ui)); } }