Seperate style source fields from MapSource
This commit is contained in:
@@ -61,6 +61,7 @@ dependencies {
|
|||||||
implementation("com.caverock:androidsvg:1.4")
|
implementation("com.caverock:androidsvg:1.4")
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.0")
|
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.0")
|
||||||
|
implementation("commons-codec:commons-codec:1.17.2")
|
||||||
implementation("org.apache.commons:commons-lang3:3.14.0")
|
implementation("org.apache.commons:commons-lang3:3.14.0")
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource.MapSourceContractType;
|
import eu.konggdev.strikemaps.map.source.tiles.SourceTiles;
|
||||||
import eu.konggdev.strikemaps.map.source.model.TileSource;
|
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
|
||||||
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
|
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
|
||||||
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -36,11 +35,15 @@ public final class UserPrefsHelper {
|
|||||||
public static Map<Integer, MapStyle> DEFAULT_STYLES(AppController app) {
|
public static Map<Integer, MapStyle> DEFAULT_STYLES(AppController app) {
|
||||||
Map<Integer, MapStyle> styles = new HashMap<>();
|
Map<Integer, MapStyle> styles = new HashMap<>();
|
||||||
String[] styleAssets = FileHelper.getAssetFiles("bundled/style", ".style.json", app);
|
String[] styleAssets = FileHelper.getAssetFiles("bundled/style", ".style.json", app);
|
||||||
for (int i = 0; i < styleAssets.length; i++) { styles.put( i,
|
for (int i = 0; i < styleAssets.length; i++) {
|
||||||
|
String styleContents = FileHelper.loadStringFromAssetFile(styleAssets[i], app);
|
||||||
|
styles.put( i,
|
||||||
new MapStyle(
|
new MapStyle(
|
||||||
FileHelper.loadStringFromAssetFile(styleAssets[i], app),
|
styleContents,
|
||||||
new StyleOptions(),
|
new StyleOptions(),
|
||||||
new StyleManagementMetadata()
|
new StyleManagementMetadata( //Set the style to automatically update from assets
|
||||||
|
false, true, true,
|
||||||
|
"assets://" + styleAssets[i], DigestUtils.sha256Hex(styleContents))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return styles;
|
return styles;
|
||||||
@@ -48,16 +51,14 @@ public final class UserPrefsHelper {
|
|||||||
|
|
||||||
private static final Map<Integer, MapSource> DEFAULT_SOURCES = Map.of(
|
private static final Map<Integer, MapSource> DEFAULT_SOURCES = Map.of(
|
||||||
0, new MapSource(
|
0, new MapSource(
|
||||||
MapSourceContractType.DEFINITION,
|
|
||||||
"Strike Maps Planet",
|
"Strike Maps Planet",
|
||||||
new TileSource("https://tiles.strikemaps.eu/planet"),
|
new SourceTiles("https://tiles.strikemaps.eu/planet"),
|
||||||
"vector",
|
"vector",
|
||||||
"smts"
|
"smts"
|
||||||
),
|
),
|
||||||
1, new MapSource(
|
1, new MapSource(
|
||||||
MapSourceContractType.DEFINITION,
|
|
||||||
"ArcGIS Imagery",
|
"ArcGIS Imagery",
|
||||||
new TileSource(new String[]{"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}),
|
new SourceTiles(new String[]{"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}),
|
||||||
"raster",
|
"raster",
|
||||||
"raster"
|
"raster"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ public class MapComponent implements Component {
|
|||||||
private final MapRenderer mapRenderer;
|
private final MapRenderer mapRenderer;
|
||||||
private final AppController app;
|
private final AppController app;
|
||||||
|
|
||||||
public MapStyle style;
|
public Integer styleId;
|
||||||
|
|
||||||
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
|
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
|
||||||
|
|
||||||
public MapComponent(AppController ref) {
|
public MapComponent(AppController ref) {
|
||||||
@@ -48,9 +49,8 @@ public class MapComponent implements Component {
|
|||||||
return new FragmentLayoutContentMap(mapRenderer.getView());
|
return new FragmentLayoutContentMap(mapRenderer.getView());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStyle(MapStyle style) {
|
public void setStyle(Integer id) {
|
||||||
this.style = style;
|
styleId = id;
|
||||||
mapRenderer.styleUpdate(style.effectiveDocument());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchOverlay(MapOverlay overlay) {
|
public void switchOverlay(MapOverlay overlay) {
|
||||||
@@ -102,9 +102,7 @@ public class MapComponent implements Component {
|
|||||||
|
|
||||||
public void onMapInit() {
|
public void onMapInit() {
|
||||||
setStyle(
|
setStyle(
|
||||||
app.getRegistry().getStyle(
|
UserPrefsHelper.startupMapStyle(app.getPrefs())
|
||||||
UserPrefsHelper.startupMapStyle(app.getPrefs())
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -17,8 +17,8 @@ import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
|||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
|
||||||
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
|
import eu.konggdev.strikemaps.map.style.source.StyleSource;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
@@ -72,8 +72,9 @@ public class MapLibreGLJSRenderer implements MapRenderer {
|
|||||||
//Sources
|
//Sources
|
||||||
ObjectNode sources = mapper.createObjectNode();
|
ObjectNode sources = mapper.createObjectNode();
|
||||||
if (style.sources != null)
|
if (style.sources != null)
|
||||||
for (MapSource source : style.sources)
|
for (StyleSource source : style.sources)
|
||||||
sources.set(source.name, source.makeJson());
|
sources.set(source.key,
|
||||||
|
source.current.makeJson());
|
||||||
|
|
||||||
//Layers
|
//Layers
|
||||||
ArrayNode layers = mapper.createArrayNode();
|
ArrayNode layers = mapper.createArrayNode();
|
||||||
|
|||||||
+9
-3
@@ -10,16 +10,19 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
||||||
|
import eu.konggdev.strikemaps.map.offline.OfflineTileResolver;
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
|
||||||
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
|
import eu.konggdev.strikemaps.map.style.source.StyleSource;
|
||||||
import org.maplibre.android.MapLibre;
|
import org.maplibre.android.MapLibre;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.android.maps.MapLibreMap;
|
import org.maplibre.android.maps.MapLibreMap;
|
||||||
import org.maplibre.android.maps.MapView;
|
import org.maplibre.android.maps.MapView;
|
||||||
import org.maplibre.android.maps.OnMapReadyCallback;
|
import org.maplibre.android.maps.OnMapReadyCallback;
|
||||||
import org.maplibre.android.maps.Style;
|
import org.maplibre.android.maps.Style;
|
||||||
|
import org.maplibre.android.module.http.HttpRequestUtil;
|
||||||
|
import org.maplibre.android.offline.OfflineManager;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -39,7 +42,9 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
MapLibre.getInstance(app.getActivity());
|
MapLibre.getInstance(app.getActivity());
|
||||||
this.mapView = new MapView(app.getActivity());
|
this.mapView = new MapView(app.getActivity());
|
||||||
|
//TODO: Investigate if we might want to restore any savedInstanceState for MapLibre
|
||||||
mapView.onCreate(null);
|
mapView.onCreate(null);
|
||||||
|
//We need this to get the onMapReady callback
|
||||||
mapView.getMapAsync(this);
|
mapView.getMapAsync(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +61,9 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
//Sources
|
//Sources
|
||||||
ObjectNode sources = mapper.createObjectNode();
|
ObjectNode sources = mapper.createObjectNode();
|
||||||
if (style.sources != null)
|
if (style.sources != null)
|
||||||
for (MapSource source : style.sources)
|
for (StyleSource source : style.sources)
|
||||||
sources.set(source.name, source.makeJson());
|
sources.set(source.key,
|
||||||
|
source.current.makeJson());
|
||||||
|
|
||||||
//Layers
|
//Layers
|
||||||
ArrayNode layers = mapper.createArrayNode();
|
ArrayNode layers = mapper.createArrayNode();
|
||||||
|
|||||||
@@ -1,52 +1,14 @@
|
|||||||
package eu.konggdev.strikemaps.map.source;
|
package eu.konggdev.strikemaps.map.source;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import eu.konggdev.strikemaps.map.source.model.TileSource;
|
import eu.konggdev.strikemaps.map.source.tiles.SourceTiles;
|
||||||
|
|
||||||
public class MapSource {
|
public class MapSource {
|
||||||
public enum MapSourceContractType {
|
|
||||||
REQUEST,
|
|
||||||
DEFINITION
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Contract describes the "purpose" of the source.
|
|
||||||
*
|
|
||||||
* This exists because we effectively have two types
|
|
||||||
* of sources:
|
|
||||||
* - Sources that are the style asking for a specific type of source
|
|
||||||
* and defining a fallback, which is also used as a default for
|
|
||||||
*
|
|
||||||
* and
|
|
||||||
* - Sources that are the actual source
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public final MapSourceContractType contract;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For a request contract:
|
|
||||||
* name is the internal key used by the style.
|
|
||||||
*
|
|
||||||
* For a definition contract:
|
|
||||||
* name is the user-facing source name
|
|
||||||
* (e.g. "ArcGIS Imagery").
|
|
||||||
*
|
|
||||||
* When converting a request into a definition
|
|
||||||
* (when importing a style whose requests
|
|
||||||
* cannot be satisfied), we look for a name
|
|
||||||
* field and fall back to normalizing the key instead
|
|
||||||
*/
|
|
||||||
public String name;
|
public String name;
|
||||||
|
|
||||||
/*
|
public SourceTiles tileSource;
|
||||||
* For a request contract, this is the fallback
|
|
||||||
* for when we absolutely cannot satisfy the requirement,
|
|
||||||
* or the defaults for converting into a definition contract.
|
|
||||||
*/
|
|
||||||
public TileSource tileSource;
|
|
||||||
|
|
||||||
public String type;
|
public String type;
|
||||||
public String schema;
|
public String schema;
|
||||||
@@ -59,41 +21,33 @@ public class MapSource {
|
|||||||
|
|
||||||
public String encoding;
|
public String encoding;
|
||||||
|
|
||||||
public MapSource(@NonNull MapSourceContractType contract, String name, TileSource tileSource, String type, String schema) {
|
public MapSource(String name, SourceTiles tileSource, String type, String schema) {
|
||||||
this.contract = contract;
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.tileSource = tileSource;
|
this.tileSource = tileSource;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapSource(@NonNull MapSourceContractType contract) {
|
public MapSource(String key, JsonNode sourceNode) {
|
||||||
this.contract = contract;
|
this.name = key;
|
||||||
|
|
||||||
|
this.type = sourceNode.path("type").asText(null);
|
||||||
|
this.schema = sourceNode.path("schema").asText(null);
|
||||||
|
this.scheme = sourceNode.path("scheme").asText(null);
|
||||||
|
this.encoding = sourceNode.path("encoding").asText(null);
|
||||||
|
|
||||||
|
this.minzoom = sourceNode.path("minZoom").asInt(0);
|
||||||
|
this.maxzoom = sourceNode.path("maxZoom").asInt(24);
|
||||||
|
this.tileSize = sourceNode.path("tileSize").asInt(256);
|
||||||
|
|
||||||
|
this.tileSource = handleJsonTileSource(sourceNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MapSource fromJson(MapSourceContractType contract, String key, JsonNode sourceNode) {
|
private static SourceTiles handleJsonTileSource(JsonNode sourceNode) {
|
||||||
MapSource result = new MapSource(contract);
|
|
||||||
result.name = key;
|
|
||||||
|
|
||||||
result.schema = sourceNode.path("schema").asText(null);
|
|
||||||
result.scheme = sourceNode.path("scheme").asText(null);
|
|
||||||
result.encoding = sourceNode.path("encoding").asText(null);
|
|
||||||
result.type = sourceNode.path("type").asText(null);
|
|
||||||
|
|
||||||
result.minzoom = sourceNode.path("minZoom").asInt(0);
|
|
||||||
result.maxzoom = sourceNode.path("maxZoom").asInt(24);
|
|
||||||
result.tileSize = sourceNode.path("tileSize").asInt(256);
|
|
||||||
|
|
||||||
result.tileSource = handleJsonTileSource(contract, sourceNode);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TileSource handleJsonTileSource(MapSourceContractType contract, JsonNode sourceNode) {
|
|
||||||
// By design, a source must use either "url" or "tiles", never both
|
// By design, a source must use either "url" or "tiles", never both
|
||||||
// In case both are present, we prefer URL over tiles... because I don't know, we just do, m'kay?
|
// In case both are present, we prefer URL over tiles... because I don't know, we just do, m'kay?
|
||||||
if (sourceNode.has("url"))
|
if (sourceNode.has("url"))
|
||||||
return new TileSource(sourceNode.get("url").asText());
|
return new SourceTiles(sourceNode.get("url").asText());
|
||||||
|
|
||||||
if (sourceNode.has("tiles")) {
|
if (sourceNode.has("tiles")) {
|
||||||
String[] tiles = new String[0];
|
String[] tiles = new String[0];
|
||||||
@@ -105,15 +59,12 @@ public class MapSource {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TileSource(tiles);
|
return new SourceTiles(tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contract == MapSourceContractType.REQUEST) //No tile source is only acceptable for a request contract
|
|
||||||
//TODO: Define an empty tile source?
|
|
||||||
return null;
|
|
||||||
|
|
||||||
//TODO: Decide what to do when we have a definition that doesn't define the most important part - the tile source
|
//TODO: Decide what to do when we have a definition that doesn't define the most important part - the tile source
|
||||||
//Maybe throwing some custom exception, catching it in fromJson calls and propagating it back to the user would be appropriate
|
//Maybe throwing some custom exception and propagating it back to the user would be appropriate
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package eu.konggdev.strikemaps.map.source.properties;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
public class SourceProperties {
|
||||||
|
public String type;
|
||||||
|
public String schema;
|
||||||
|
|
||||||
|
// Json constructor
|
||||||
|
public SourceProperties(JsonNode sourceNode) {
|
||||||
|
this.type = sourceNode.path("type").asText(null);
|
||||||
|
this.schema = sourceNode.path("schema").asText(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-10
@@ -1,35 +1,36 @@
|
|||||||
package eu.konggdev.strikemaps.map.source.model;
|
package eu.konggdev.strikemaps.map.source.tiles;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
public class TileSource {
|
public class SourceTiles {
|
||||||
public enum TileSourceType {
|
public enum TilesType {
|
||||||
URL,
|
URL,
|
||||||
TILES,
|
TILES,
|
||||||
DATA
|
DATA
|
||||||
}
|
}
|
||||||
private TileSourceType type;
|
|
||||||
|
private final TilesType type;
|
||||||
private String url;
|
private String url;
|
||||||
private String[] tiles;
|
private String[] tiles;
|
||||||
|
|
||||||
private JsonNode data;
|
private JsonNode data;
|
||||||
|
|
||||||
public TileSource(String url) {
|
public SourceTiles(String url) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.type = TileSourceType.URL;
|
this.type = TilesType.URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileSource(String[] tiles) {
|
public SourceTiles(String[] tiles) {
|
||||||
this.tiles = tiles;
|
this.tiles = tiles;
|
||||||
this.type = TileSourceType.TILES;
|
this.type = TilesType.TILES;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileSource(JsonNode data) {
|
public SourceTiles(JsonNode data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.type = TileSourceType.DATA;
|
this.type = TilesType.DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeJson(ObjectMapper mapper, ObjectNode node) {
|
public void makeJson(ObjectMapper mapper, ObjectNode node) {
|
||||||
@@ -2,9 +2,11 @@ package eu.konggdev.strikemaps.map.style;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
|
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
|
||||||
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
||||||
|
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
|
||||||
|
|
||||||
public class MapStyle {
|
public class MapStyle {
|
||||||
public static final class StoredRepresentation {
|
public static final class StoredRepresentation {
|
||||||
@@ -20,13 +22,11 @@ public class MapStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MapStyle restore() {
|
public MapStyle restore() {
|
||||||
MapStyle style = new MapStyle(
|
return new MapStyle(
|
||||||
json,
|
json,
|
||||||
options,
|
options,
|
||||||
managementMetadata
|
managementMetadata
|
||||||
);
|
);
|
||||||
|
|
||||||
return style;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,6 @@ public class MapStyle {
|
|||||||
// Original json representation of the style document, as we got it
|
// Original json representation of the style document, as we got it
|
||||||
@NonNull public final String json;
|
@NonNull public final String json;
|
||||||
|
|
||||||
|
|
||||||
public MapStyle(@NonNull String json, @NonNull StyleOptions styleOptions, @Nullable StyleManagementMetadata managementMetadata) {
|
public MapStyle(@NonNull String json, @NonNull StyleOptions styleOptions, @Nullable StyleManagementMetadata managementMetadata) {
|
||||||
this.json = json;
|
this.json = json;
|
||||||
this.document = new StyleDocument(json);
|
this.document = new StyleDocument(json);
|
||||||
@@ -58,7 +57,7 @@ public class MapStyle {
|
|||||||
return new StoredRepresentation(json, options, managementMetadata);
|
return new StoredRepresentation(json, options, managementMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StyleDocument effectiveDocument() {
|
public StyleDocument effectiveDocument(RegistryStorageComponent registry) {
|
||||||
return document.effectiveDocument(options);
|
return document.effectiveDocument(options, registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
package eu.konggdev.strikemaps.map.style.document;
|
package eu.konggdev.strikemaps.map.style.document;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.helper.FileHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
||||||
|
import eu.konggdev.strikemaps.map.style.source.StyleSource;
|
||||||
|
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class StyleDocument {
|
public class StyleDocument {
|
||||||
|
|
||||||
//Only local data
|
//Only local data
|
||||||
public String name;
|
public String name;
|
||||||
public String icon;
|
public String icon;
|
||||||
|
|
||||||
public JsonNode metadata; // everything except layers + sources
|
public JsonNode metadata; // everything except layers + sources
|
||||||
public List<MapSource> sources;
|
public List<StyleSource> sources;
|
||||||
public ArrayNode layerDefinitions; // "layers" array
|
public ArrayNode layerDefinitions; // "layers" array
|
||||||
|
|
||||||
// Json constructor
|
// Json constructor
|
||||||
@@ -35,9 +33,9 @@ public class StyleDocument {
|
|||||||
this.icon = root.path("icon").asText();
|
this.icon = root.path("icon").asText();
|
||||||
|
|
||||||
JsonNode jsonSources = root.path("sources");
|
JsonNode jsonSources = root.path("sources");
|
||||||
List<MapSource> sources = new ArrayList<>();
|
List<StyleSource> sources = new ArrayList<>();
|
||||||
jsonSources.fields().forEachRemaining(entry -> {
|
jsonSources.fields().forEachRemaining(entry -> {
|
||||||
sources.add(MapSource.fromJson(MapSource.MapSourceContractType.REQUEST, entry.getKey(), entry.getValue()));
|
sources.add(new StyleSource(entry.getKey(), entry.getValue()));
|
||||||
});
|
});
|
||||||
this.sources = sources;
|
this.sources = sources;
|
||||||
|
|
||||||
@@ -47,8 +45,8 @@ public class StyleDocument {
|
|||||||
metadata.remove("layers");
|
metadata.remove("layers");
|
||||||
metadata.remove("sources");
|
metadata.remove("sources");
|
||||||
this.metadata = metadata;
|
this.metadata = metadata;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
throw new IllegalArgumentException("Invalid style document", e);
|
throw new IllegalArgumentException("Invalid style document", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +61,16 @@ public class StyleDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The style that is presented to the renderer, with its options applied
|
// The style that is presented to the renderer, with its options applied
|
||||||
public StyleDocument effectiveDocument(StyleOptions options) {
|
public StyleDocument effectiveDocument(StyleOptions options, RegistryStorageComponent registry) {
|
||||||
StyleDocument result = new StyleDocument(this); //Copy
|
StyleDocument result = new StyleDocument(this); //Copy
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
StyleSource source = sources.get(i);
|
||||||
|
MapSource effectiveSource = registry.getSource(
|
||||||
|
options.getInteger(source.key, 0)
|
||||||
|
);
|
||||||
|
if (effectiveSource != null) source.current = effectiveSource;
|
||||||
|
}
|
||||||
|
|
||||||
for (JsonNode layer : result.layerDefinitions) {
|
for (JsonNode layer : result.layerDefinitions) {
|
||||||
JsonNode option = layer.get("option");
|
JsonNode option = layer.get("option");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package eu.konggdev.strikemaps.map.style.source;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
|
import eu.konggdev.strikemaps.map.source.properties.SourceProperties;
|
||||||
|
|
||||||
|
// Stores a source currently used by a style
|
||||||
|
// Along with requirements of what source the style needs here
|
||||||
|
public class StyleSource {
|
||||||
|
public String key;
|
||||||
|
|
||||||
|
// Requirement properties
|
||||||
|
public SourceProperties properties;
|
||||||
|
|
||||||
|
// In an effective style, this is the actual source matched to the
|
||||||
|
// style's requirements according to user's preferences (StyleOptions).
|
||||||
|
// Otherwise, this is the fallback source.
|
||||||
|
// It also preserves the default source defined by the JSON for non-effective style documents
|
||||||
|
@NonNull public MapSource current;
|
||||||
|
|
||||||
|
// Json constructor
|
||||||
|
public StyleSource(String key, JsonNode sourceNode) {
|
||||||
|
this.key = key;
|
||||||
|
this.properties = new SourceProperties(sourceNode);
|
||||||
|
this.current = new MapSource(key, sourceNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.konggdev.strikemaps.storage;
|
package eu.konggdev.strikemaps.storage;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import eu.konggdev.strikemaps.Component;
|
import eu.konggdev.strikemaps.Component;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
@@ -8,6 +9,9 @@ import eu.konggdev.strikemaps.helper.UserPrefsHelper;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
public class RegistryStorageComponent implements Component {
|
public class RegistryStorageComponent implements Component {
|
||||||
private AppController app;
|
private AppController app;
|
||||||
|
|
||||||
@@ -29,6 +33,10 @@ public class RegistryStorageComponent implements Component {
|
|||||||
return sources();
|
return sources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable public MapSource getSource(Integer id) {
|
||||||
|
return sources().get(id);
|
||||||
|
}
|
||||||
|
|
||||||
private Map<Integer, MapStyle> styles() {
|
private Map<Integer, MapStyle> styles() {
|
||||||
if (styles == null) styles = UserPrefsHelper.styles(app.getPrefs(), app);
|
if (styles == null) styles = UserPrefsHelper.styles(app.getPrefs(), app);
|
||||||
return styles;
|
return styles;
|
||||||
@@ -38,7 +46,7 @@ public class RegistryStorageComponent implements Component {
|
|||||||
return styles();
|
return styles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapStyle getStyle(Integer id) {
|
@Nullable public MapStyle getStyle(Integer id) {
|
||||||
return styles().get(id);
|
return styles().get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-13
@@ -6,6 +6,8 @@ import android.os.Bundle;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
@@ -18,6 +20,7 @@ import eu.konggdev.strikemaps.R;
|
|||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
|
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
|
||||||
import eu.konggdev.strikemaps.ui.UIComponent;
|
import eu.konggdev.strikemaps.ui.UIComponent;
|
||||||
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
||||||
@@ -33,15 +36,16 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
@NonNull
|
@NonNull
|
||||||
UIComponent ui;
|
UIComponent ui;
|
||||||
@NonNull
|
@NonNull
|
||||||
|
RegistryStorageComponent registry;
|
||||||
|
@NonNull
|
||||||
final FragmentMapChangePopup mapChangePopup;
|
final FragmentMapChangePopup mapChangePopup;
|
||||||
|
|
||||||
private final MapStyle style;
|
private final Integer styleId;
|
||||||
private final Integer id;
|
|
||||||
|
|
||||||
private ActivityResultLauncher<Intent> exportLauncher;
|
private ActivityResultLauncher<Intent> exportLauncher;
|
||||||
|
|
||||||
void deleteStyle() {
|
void deleteStyle() {
|
||||||
app.getRegistry().deleteStyle(id);
|
registry.deleteStyle(styleId);
|
||||||
|
|
||||||
mapChangePopup.reloadStyles();
|
mapChangePopup.reloadStyles();
|
||||||
dismiss();
|
dismiss();
|
||||||
@@ -54,13 +58,13 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
exportLauncher.launch(intent);
|
exportLauncher.launch(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StyleDetailsBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup, MapStyle style, Integer id) {
|
public StyleDetailsBottomSheet(AppController app, FragmentMapChangePopup mapChangePopup, Integer styleId) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.map = map;
|
this.map = app.getMap();
|
||||||
this.ui = ui;
|
this.ui = app.getUi();
|
||||||
|
this.registry = app.getRegistry();
|
||||||
this.mapChangePopup = mapChangePopup;
|
this.mapChangePopup = mapChangePopup;
|
||||||
this.style = style;
|
this.styleId = styleId;
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,7 +85,8 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
.openOutputStream(uri)) {
|
.openOutputStream(uri)) {
|
||||||
|
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
out.write(style.json.getBytes());
|
String styleJson = registry.getStyle(styleId).json;
|
||||||
|
if (styleJson != null) out.write(styleJson.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -96,20 +101,27 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.dialog_style_details, container, false);
|
View view = inflater.inflate(R.layout.dialog_style_details, container, false);
|
||||||
|
|
||||||
|
MapStyle style = registry.getStyle(styleId);
|
||||||
TextView styleNameView = view.findViewById(R.id.styleName);
|
TextView styleNameView = view.findViewById(R.id.styleName);
|
||||||
|
LinearLayout styleManagementInfo = view.findViewById(R.id.styleManagementInfo);
|
||||||
TextView styleTypeView = view.findViewById(R.id.styleType);
|
TextView styleTypeView = view.findViewById(R.id.styleType);
|
||||||
|
ImageView managedStyleIndicator = view.findViewById(R.id.managedStyleIndicator);
|
||||||
|
|
||||||
TextView builtInStyleAlert = view.findViewById(R.id.builtInStyleAlert);
|
|
||||||
MaterialCardView editButtonLayout = view.findViewById(R.id.editButton);
|
MaterialCardView editButtonLayout = view.findViewById(R.id.editButton);
|
||||||
MaterialCardView copyButtonLayout = view.findViewById(R.id.copyButton);
|
MaterialCardView copyButtonLayout = view.findViewById(R.id.copyButton);
|
||||||
MaterialCardView exportButtonLayout = view.findViewById(R.id.exportButton);
|
MaterialCardView exportButtonLayout = view.findViewById(R.id.exportButton);
|
||||||
MaterialCardView deleteButtonLayout = view.findViewById(R.id.deleteButton);
|
MaterialCardView deleteButtonLayout = view.findViewById(R.id.deleteButton);
|
||||||
MaterialCardView closeButtonLayout = view.findViewById(R.id.closeButton);
|
|
||||||
|
|
||||||
styleNameView.setText(style.document.name);
|
styleNameView.setText(style.document.name);
|
||||||
|
|
||||||
if (style.managementMetadata != null) {
|
if (style.managementMetadata != null) {
|
||||||
styleTypeView.setText("Built-In Style");
|
styleTypeView.setText("Built-In Style");
|
||||||
|
managedStyleIndicator.setVisibility(View.VISIBLE);
|
||||||
|
styleManagementInfo.setOnClickListener(v ->
|
||||||
|
ui.alert(
|
||||||
|
AlertDialogFactory.styleManagementOptions(app, style.managementMetadata)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
styleTypeView.setText("User Style");
|
styleTypeView.setText("User Style");
|
||||||
}
|
}
|
||||||
@@ -118,7 +130,6 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, style.json, mapChangePopup)));
|
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, style.json, mapChangePopup)));
|
||||||
exportButtonLayout.setOnClickListener(v -> showExportDialog());
|
exportButtonLayout.setOnClickListener(v -> showExportDialog());
|
||||||
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
|
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
|
||||||
closeButtonLayout.setOnClickListener(v -> dismiss());
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-7
@@ -18,17 +18,20 @@ import eu.konggdev.strikemaps.app.AppController;
|
|||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
|
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
|
||||||
import eu.konggdev.strikemaps.ui.UIComponent;
|
import eu.konggdev.strikemaps.ui.UIComponent;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.dialog.NewStyleBottomSheet;
|
import eu.konggdev.strikemaps.ui.fragment.dialog.NewStyleBottomSheet;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.dialog.StyleDetailsBottomSheet;
|
import eu.konggdev.strikemaps.ui.fragment.dialog.StyleDetailsBottomSheet;
|
||||||
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
|
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class FragmentMapChangePopup extends Fragment implements Popup {
|
public class FragmentMapChangePopup extends Fragment implements Popup {
|
||||||
@NonNull AppController app;
|
@NonNull AppController app;
|
||||||
@NonNull MapComponent map;
|
@NonNull MapComponent map;
|
||||||
@NonNull UIComponent ui;
|
@NonNull UIComponent ui;
|
||||||
|
@NonNull RegistryStorageComponent registry;
|
||||||
|
|
||||||
private final Integer region;
|
private final Integer region;
|
||||||
|
|
||||||
@@ -49,11 +52,13 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
|
|||||||
public void reloadStyles() {
|
public void reloadStyles() {
|
||||||
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
|
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
|
||||||
stylesLayout.removeAllViews();
|
stylesLayout.removeAllViews();
|
||||||
app.getRegistry().getStyles().forEach((id, style) ->
|
app.getRegistry().getStyles().forEach((id, style) -> {
|
||||||
stylesLayout.addView(GenericItem.fromStyle(style.document, app,
|
View item = new GenericItem(
|
||||||
() -> map.setStyle(style),
|
style.document,
|
||||||
() -> this.styleDetails(style, id)).makeView(ui))
|
app,
|
||||||
);
|
() -> this.setStyle(id),
|
||||||
|
() -> this.styleDetails(id)).makeView(ui);
|
||||||
|
});
|
||||||
Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add);
|
Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add);
|
||||||
stylesLayout.addView(new GenericItem("",
|
stylesLayout.addView(new GenericItem("",
|
||||||
addNewIcon,
|
addNewIcon,
|
||||||
@@ -90,8 +95,13 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
|
|||||||
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
|
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
|
||||||
}
|
}
|
||||||
|
|
||||||
void styleDetails(MapStyle entry, Integer id) {
|
void styleDetails(Integer id) {
|
||||||
new StyleDetailsBottomSheet(app, map, ui, this, entry, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
|
new StyleDetailsBottomSheet(app, this, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStyle(Integer id) {
|
||||||
|
map.setStyle(id);
|
||||||
|
reloadStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FragmentMapChangePopup(AppController app, Integer region) {
|
public FragmentMapChangePopup(AppController app, Integer region) {
|
||||||
@@ -99,6 +109,7 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
this.map = app.getMap();
|
this.map = app.getMap();
|
||||||
this.ui = app.getUi();
|
this.ui = app.getUi();
|
||||||
|
this.registry = app.getRegistry();
|
||||||
this.region = region;
|
this.region = region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user