Rework MapRenderer abstraction
This commit is contained in:
@@ -28,8 +28,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_15
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_15
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@@ -46,6 +46,7 @@ dependencies {
|
|||||||
implementation(libs.constraintlayout)
|
implementation(libs.constraintlayout)
|
||||||
implementation(libs.viewpager2)
|
implementation(libs.viewpager2)
|
||||||
|
|
||||||
|
implementation("org.osmdroid:osmdroid-android:6.1.20")
|
||||||
implementation("org.maplibre.gl:android-sdk:11.13.0")
|
implementation("org.maplibre.gl:android-sdk:11.13.0")
|
||||||
implementation("com.github.mapsforge.vtm:vtm:0.27.0")
|
implementation("com.github.mapsforge.vtm:vtm:0.27.0")
|
||||||
implementation("com.github.mapsforge.vtm:vtm-android:0.27.0")
|
implementation("com.github.mapsforge.vtm:vtm-android:0.27.0")
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
Builds included inside /dist correspond to MapLibre GL JS version 5.24.0
|
Builds included inside /dist correspond to MapLibre GL JS version 5.24.0 <br>
|
||||||
The source for these builds is available on https://github.com/maplibre/maplibre-gl-js
|
The source for these builds is available on https://github.com/maplibre/maplibre-gl-js
|
||||||
#
|
#
|
||||||
|
|
||||||
+1
-5
@@ -21,12 +21,8 @@
|
|||||||
center: [13.388, 52.517],
|
center: [13.388, 52.517],
|
||||||
zoom: 9.5,
|
zoom: 9.5,
|
||||||
container: 'map',
|
container: 'map',
|
||||||
|
attributionControl: false
|
||||||
})
|
})
|
||||||
|
|
||||||
window.setMapStyle = function(styleUrl) {
|
|
||||||
if (!map) return;
|
|
||||||
map.setStyle(styleUrl);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package eu.konggdev.strikemaps.app.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
public final class JsonPatcher {
|
||||||
|
//Takes a json and a patch and applies it
|
||||||
|
public static JsonNode patch(JsonNode current, JsonNode patch) throws Exception {
|
||||||
|
if (current.isObject()) {
|
||||||
|
ObjectNode result = current.deepCopy();
|
||||||
|
|
||||||
|
patch.fields().forEachRemaining(entry -> { //For each
|
||||||
|
String key = entry.getKey();
|
||||||
|
JsonNode patchValue = entry.getValue();
|
||||||
|
if(!result.has(key)) { //Not at all in current
|
||||||
|
result.set(key, patchValue); //Just append it to result
|
||||||
|
} else if (result.has(key) && !patchValue.isEmpty()) { //Deeper depth possible
|
||||||
|
try {
|
||||||
|
result.set(key, patch(result.get(key), patchValue)); //Patch iteration with patch's value (traverse deeper)
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else if (result.has(key) && patchValue.isEmpty() && result.get(key).isEmpty()) { //Max depth match
|
||||||
|
result.set(key, patchValue); //Replace with patch's value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.isArray()) {
|
||||||
|
ArrayNode result = current.deepCopy();
|
||||||
|
for (JsonNode entry : patch) {
|
||||||
|
if (StreamSupport.stream(result.spliterator(), false)
|
||||||
|
.noneMatch(node -> node.equals(entry))) { //If no exact entry, add entry (prevents duplicates)
|
||||||
|
result.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Takes an original json, a patched json and a patch and removes that specific patch's changes from it
|
||||||
|
public static JsonNode unpatch(JsonNode original, JsonNode current, JsonNode patch) throws Exception {
|
||||||
|
if (current.isObject()) {
|
||||||
|
ObjectNode result = current.deepCopy(); //Current contains the patch, deepcopy it and operate on the copy
|
||||||
|
|
||||||
|
current.fields().forEachRemaining(entry -> {
|
||||||
|
String key = entry.getKey();
|
||||||
|
JsonNode value = entry.getValue();
|
||||||
|
|
||||||
|
if (!original.has(key) && patch.has(key)) { //Value is in patch and current but was not at all in the original
|
||||||
|
result.remove(key); //Just remove the value
|
||||||
|
} else if (!value.isEmpty() && patch.has(key)) { //Depper depth possible
|
||||||
|
try {
|
||||||
|
result.set(key, unpatch(original.get(key), result.get(key), patch.get(key))); //Replace with further unpatch of deeper level
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else if (value.isEmpty() && patch.has(key)) { //Max depth match
|
||||||
|
result.set(key, original.get(key)); //Replace with original unpatched value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.isArray()) {
|
||||||
|
ArrayNode result = current.deepCopy();
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
JsonNode entry = result.get(i);
|
||||||
|
if (StreamSupport.stream(original.spliterator(), false)
|
||||||
|
.noneMatch(on -> on.equals(entry)) //If not in original
|
||||||
|
&&
|
||||||
|
StreamSupport.stream(patch.spliterator(), false)
|
||||||
|
.noneMatch(rn -> rn.equals(entry))) { //But also in patch
|
||||||
|
|
||||||
|
result.remove(i); //Remove from result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package eu.konggdev.strikemaps.map;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.widget.Toolbar;
|
|
||||||
import eu.konggdev.strikemaps.Component;
|
import eu.konggdev.strikemaps.Component;
|
||||||
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
|
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
|
||||||
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
||||||
@@ -19,27 +18,28 @@ import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreNativeRenderer
|
|||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentMap;
|
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentMap;
|
||||||
|
|
||||||
public class MapComponent implements Component {
|
public class MapComponent implements Component {
|
||||||
MapRenderer mapRenderer;
|
MapRenderer mapRenderer;
|
||||||
AppController app;
|
AppController app;
|
||||||
|
|
||||||
public MapStyle style;
|
public MapStyle style;
|
||||||
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) {
|
||||||
this.app = ref;
|
this.app = ref;
|
||||||
switch(UserPrefsHelper.mapRenderer(app.getPrefs())) {
|
switch(UserPrefsHelper.mapRenderer(app.getPrefs())) {
|
||||||
case 0:
|
case 0:
|
||||||
this.mapRenderer = new MapLibreNativeRenderer(app, this);
|
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
|
this.mapRenderer = new MapLibreNativeRenderer(app, this);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this.mapRenderer = new VtmRenderer(app, this);
|
this.mapRenderer = new VtmRenderer(app, this);
|
||||||
break;
|
break;
|
||||||
default: //This shouldn't happen
|
default: //This shouldn't happen
|
||||||
Toast.makeText(app.getActivity(), "Invalid renderer value in preferences\nFalling back to MapLibre Native", Toast.LENGTH_SHORT).show();
|
Toast.makeText(app.getActivity(), "Invalid renderer value in preferences\nFalling back to MapLibre GL JS", Toast.LENGTH_SHORT).show();
|
||||||
this.mapRenderer = new MapLibreNativeRenderer(app, this);
|
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -50,13 +50,13 @@ public class MapComponent implements Component {
|
|||||||
|
|
||||||
public void setStyle(MapStyle style) {
|
public void setStyle(MapStyle style) {
|
||||||
this.style = style;
|
this.style = style;
|
||||||
mapRenderer.reload();
|
mapRenderer.styleUpdate(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchOverlay(MapOverlay overlay) {
|
public void switchOverlay(MapOverlay overlay) {
|
||||||
if (hasOverlay(overlay)) overlays.remove(overlay.getClass());
|
if (hasOverlay(overlay)) overlays.remove(overlay.getClass());
|
||||||
else overlays.put(overlay.getClass(), overlay);
|
else overlays.put(overlay.getClass(), overlay);
|
||||||
update();
|
overlayUpdate(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasOverlay(MapOverlay overlay) {
|
public boolean hasOverlay(MapOverlay overlay) {
|
||||||
@@ -71,12 +71,8 @@ public class MapComponent implements Component {
|
|||||||
//FIXME: Put back FragmentPointPreviewPopup (private code atm)
|
//FIXME: Put back FragmentPointPreviewPopup (private code atm)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOverlayUpdate() {
|
public void overlayUpdate(MapOverlay in) {
|
||||||
update();
|
mapRenderer.overlayUpdate(in);
|
||||||
}
|
|
||||||
|
|
||||||
public void update() {
|
|
||||||
if(mapRenderer != null && style != null) mapRenderer.reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onMapClick(LatLng point) {
|
public boolean onMapClick(LatLng point) {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package eu.konggdev.strikemaps.map.layer;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
|
|
||||||
public class MapLayer {
|
|
||||||
public JsonNode layer;
|
|
||||||
|
|
||||||
public MapLayer(JsonNode layer) {
|
|
||||||
this.layer = layer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package eu.konggdev.strikemaps.map.overlay;
|
package eu.konggdev.strikemaps.map.overlay;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/* More or less a data-driven layer factory */
|
/* More or less a data-driven layer factory */
|
||||||
public interface MapOverlay {
|
public interface MapOverlay {
|
||||||
public SourcedMapLayer makeLayer();
|
public JsonNode makePatch();
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-51
@@ -1,19 +1,15 @@
|
|||||||
package eu.konggdev.strikemaps.map.overlay.implementation;
|
package eu.konggdev.strikemaps.map.overlay.implementation;
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.location.LocationListener;
|
import android.location.LocationListener;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
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.layer.SourcedMapLayer;
|
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
|
import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
|
||||||
import org.maplibre.geojson.Feature;
|
|
||||||
import org.maplibre.geojson.FeatureCollection;
|
|
||||||
import org.maplibre.geojson.Point;
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -32,61 +28,61 @@ public class LocationOverlay implements MapOverlay, LocationListener {
|
|||||||
this.locationDataProvider = new LocationDataProvider(app.getActivity(), this);
|
this.locationDataProvider = new LocationDataProvider(app.getActivity(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourcedMapLayer makeLayer() {
|
public JsonNode makePatch() {
|
||||||
MapSource source = new MapSource();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
source.type = "geojson";
|
ObjectNode root = mapper.createObjectNode();
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectNode sources = mapper.createObjectNode();
|
||||||
try {
|
ObjectNode location = mapper.createObjectNode();
|
||||||
ObjectNode data = mapper.createObjectNode();
|
ObjectNode data = mapper.createObjectNode();
|
||||||
data.put("type", "Feature");
|
ObjectNode geometry = mapper.createObjectNode();
|
||||||
|
ObjectNode properties = mapper.createObjectNode();
|
||||||
|
|
||||||
if(currentLocation != null) {
|
ArrayNode coordinates = mapper.createArrayNode();
|
||||||
ObjectNode geometry = mapper.createObjectNode();
|
coordinates.add(currentLocation.getLongitude());
|
||||||
geometry.put("type", "Point");
|
coordinates.add(currentLocation.getLatitude());
|
||||||
|
|
||||||
ArrayNode coordinates = mapper.createArrayNode();
|
geometry.put("type", "Point");
|
||||||
coordinates.add(currentLocation.getLongitude());
|
geometry.set("coordinates", coordinates);
|
||||||
coordinates.add(currentLocation.getLatitude());
|
|
||||||
|
|
||||||
geometry.set("coordinates", coordinates);
|
data.put("type", "Feature");
|
||||||
data.set("geometry", geometry);
|
data.set("geometry", geometry);
|
||||||
data.set("properties", mapper.createObjectNode());
|
data.set("properties", properties);
|
||||||
}
|
|
||||||
source.data = data;
|
location.put("type", "geojson");
|
||||||
} catch (Exception e) {
|
location.set("data", data);
|
||||||
e.printStackTrace();
|
|
||||||
|
sources.set("location", location);
|
||||||
|
root.set("sources", sources);
|
||||||
|
|
||||||
|
// layers
|
||||||
|
ArrayNode layers = mapper.createArrayNode();
|
||||||
|
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);
|
||||||
|
|
||||||
|
layers.add(layer);
|
||||||
|
root.set("layers", layers);
|
||||||
|
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public void onLocationChanged(@NonNull Location location) {
|
public void onLocationChanged(@NonNull Location location) {
|
||||||
this.currentLocation = location;
|
this.currentLocation = location;
|
||||||
map.onOverlayUpdate();
|
map.overlayUpdate(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -1,12 +1,11 @@
|
|||||||
package eu.konggdev.strikemaps.map.overlay.implementation;
|
package eu.konggdev.strikemaps.map.overlay.implementation;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.map.layer.MapLayer;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
|
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
|
|
||||||
public class PointSelectionOverlay implements MapOverlay {
|
public class PointSelectionOverlay implements MapOverlay {
|
||||||
@Override
|
@Override
|
||||||
public SourcedMapLayer makeLayer() {
|
public JsonNode makePatch() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package eu.konggdev.strikemaps.map.renderer;
|
package eu.konggdev.strikemaps.map.renderer;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.map.layer.MapLayer;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import org.maplibre.android.style.layers.Layer;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface MapRenderer {
|
public interface MapRenderer {
|
||||||
void reload();
|
void styleUpdate(MapStyle style);
|
||||||
|
|
||||||
|
void overlayUpdate(MapOverlay overlay);
|
||||||
|
|
||||||
View getView();
|
View getView();
|
||||||
|
|
||||||
|
|||||||
+93
-53
@@ -3,16 +3,19 @@ package eu.konggdev.strikemaps.map.renderer.implementation;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
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.app.util.JsonPatcher;
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
|
|
||||||
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.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
@@ -25,64 +28,18 @@ import java.util.List;
|
|||||||
|
|
||||||
//Stub for now
|
//Stub for now
|
||||||
public class MapLibreGLJSRenderer implements MapRenderer {
|
public class MapLibreGLJSRenderer implements MapRenderer {
|
||||||
|
@NonNull AppController app;
|
||||||
|
|
||||||
AppController app;
|
@NonNull MapComponent controller;
|
||||||
|
|
||||||
MapComponent controller;
|
|
||||||
|
|
||||||
final WebView webView;
|
final WebView webView;
|
||||||
|
|
||||||
@Override
|
private JsonNode origin;
|
||||||
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();
|
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
webView.evaluateJavascript(
|
|
||||||
"",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
} catch (Exception e) {
|
|
||||||
app.logcat("Failed to reload Map");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView() {
|
|
||||||
return webView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Feature> featuresAtPoint(LatLng point) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
|
@SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
|
||||||
public MapLibreGLJSRenderer(AppController app, MapComponent controller) {
|
public MapLibreGLJSRenderer(AppController app, MapComponent controller) {
|
||||||
|
this.app = app;
|
||||||
|
this.controller = controller;
|
||||||
webView = new WebView(app.getActivity());
|
webView = new WebView(app.getActivity());
|
||||||
webView.setLayoutParams(
|
webView.setLayoutParams(
|
||||||
new ViewGroup.LayoutParams(
|
new ViewGroup.LayoutParams(
|
||||||
@@ -100,7 +57,90 @@ public class MapLibreGLJSRenderer implements MapRenderer {
|
|||||||
|
|
||||||
webView.addJavascriptInterface(new Bridge(), "AndroidBridge");
|
webView.addJavascriptInterface(new Bridge(), "AndroidBridge");
|
||||||
|
|
||||||
webView.loadUrl("file:///android_asset/maplibre/gl-js/index.html");
|
webView.loadUrl("file:///android_asset/library/maplibre/gl-js/index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void styleUpdate(MapStyle style) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||||
|
|
||||||
|
//Thanks to this, styleUpdate(null) can be used as an origin reload
|
||||||
|
if (style != null) {
|
||||||
|
try {
|
||||||
|
ObjectNode root = style.metadata.deepCopy();
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//Set all to root
|
||||||
|
root.set("sources", sources);
|
||||||
|
root.set("layers", layers);
|
||||||
|
this.origin = root;
|
||||||
|
} catch (Exception e) {
|
||||||
|
app.logcat("Failed to parse style: " + style.name);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final String mapped = mapper.writeValueAsString(origin);
|
||||||
|
webView.evaluateJavascript("map.setStyle(" + mapped + ", { diff: false });", null);
|
||||||
|
webView.evaluateJavascript("map.redraw()", null); //Force redraw to make the style change visible
|
||||||
|
} catch (Exception e) {
|
||||||
|
app.logcat("Failed to set style: " + style.name);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Since we just annihilated all overlays from the face of the earth, lets repatch them
|
||||||
|
if (controller.overlays != null) {
|
||||||
|
for(MapOverlay overlay : controller.overlays.values())
|
||||||
|
overlayUpdate(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void overlayUpdate(MapOverlay overlay) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
webView.evaluateJavascript(
|
||||||
|
"JSON.stringify(map.getStyle())",
|
||||||
|
style -> {
|
||||||
|
try {
|
||||||
|
JsonNode current = mapper.readTree(style);
|
||||||
|
|
||||||
|
JsonNode merged;
|
||||||
|
if (controller.hasOverlay(overlay)) {
|
||||||
|
merged = JsonPatcher.patch(current, overlay.makePatch());
|
||||||
|
} else {
|
||||||
|
merged = JsonPatcher.unpatch(origin, current, overlay.makePatch());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String mapped = mapper.writeValueAsString(merged);
|
||||||
|
webView.evaluateJavascript("map.setStyle(" + mapped + ", { diff: false });", null);
|
||||||
|
webView.evaluateJavascript("map.redraw()", null); //Force redraw to make the style change visible
|
||||||
|
} catch (Exception e) {
|
||||||
|
app.logcat("Failed to patch overlay: " + overlay.toString());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView() {
|
||||||
|
return webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Feature> featuresAtPoint(LatLng point) {
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Bridge { }
|
class Bridge { }
|
||||||
|
|||||||
+66
-31
@@ -5,12 +5,13 @@ import android.view.View;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
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 com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
||||||
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
|
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
|
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
import org.maplibre.android.MapLibre;
|
import org.maplibre.android.MapLibre;
|
||||||
@@ -27,11 +28,13 @@ import eu.konggdev.strikemaps.app.AppController;
|
|||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
|
|
||||||
public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
||||||
AppController app;
|
@NonNull AppController app;
|
||||||
MapComponent controller;
|
@NonNull MapComponent controller;
|
||||||
MapLibreMap map;
|
MapLibreMap map;
|
||||||
final MapView mapView;
|
final MapView mapView;
|
||||||
|
|
||||||
|
private JsonNode origin;
|
||||||
|
|
||||||
public MapLibreNativeRenderer(AppController app, MapComponent controller) {
|
public MapLibreNativeRenderer(AppController app, MapComponent controller) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
@@ -42,37 +45,69 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void styleUpdate(MapStyle style) {
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||||
MapStyle style = controller.style;
|
|
||||||
|
//Thanks to this, styleUpdate(null) can be used as an origin reload
|
||||||
|
if (style != null) {
|
||||||
|
try {
|
||||||
|
ObjectNode root = style.metadata.deepCopy();
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//Set all to root
|
||||||
|
root.set("sources", sources);
|
||||||
|
root.set("layers", layers);
|
||||||
|
this.origin = root;
|
||||||
|
} catch (Exception e) {
|
||||||
|
app.logcat("Failed to parse style: " + style.name);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* Take metadata from MapStyle
|
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(origin)));
|
||||||
everything outside sources, layers */
|
|
||||||
ObjectNode root = style.metadata.deepCopy();
|
|
||||||
|
|
||||||
//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) {
|
} catch (Exception e) {
|
||||||
app.logcat("Failed to reload Map");
|
app.logcat("Failed to set style: " + style.name);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Since we just annihilated all overlays from the face of the earth, lets repatch them
|
||||||
|
if (controller.overlays != null) {
|
||||||
|
for(MapOverlay overlay : controller.overlays.values())
|
||||||
|
overlayUpdate(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void overlayUpdate(MapOverlay overlay) {
|
||||||
|
if(map == null) return;
|
||||||
|
|
||||||
|
Style style = map.getStyle();
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
if(style == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonNode current = mapper.readTree(style.getJson());
|
||||||
|
|
||||||
|
JsonNode merged;
|
||||||
|
if (controller.hasOverlay(overlay)) {
|
||||||
|
merged = JsonPatcher.patch(current, overlay.makePatch());
|
||||||
|
} else {
|
||||||
|
merged = JsonPatcher.unpatch(origin, current, overlay.makePatch());
|
||||||
|
}
|
||||||
|
|
||||||
|
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(merged)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
app.logcat("Failed to patch overlay: " + overlay.toString());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +126,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
public void onMapReady(@NonNull MapLibreMap maplibreMap) {
|
public void onMapReady(@NonNull MapLibreMap maplibreMap) {
|
||||||
this.map = maplibreMap;
|
this.map = maplibreMap;
|
||||||
|
|
||||||
controller.setStyle(MapStyle.fromMapLibreJsonFile(UserPrefsHelper.startupMapStyle(app.getPrefs()), app));
|
controller.setStyle(MapStyle.fromJsonFile(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 :)
|
//I have my own implementation of attribution that credits MapLibre among others, it's not as bad as it looks :)
|
||||||
map.getUiSettings().setLogoEnabled(false);
|
map.getUiSettings().setLogoEnabled(false);
|
||||||
|
|||||||
+9
-1
@@ -3,7 +3,9 @@ package eu.konggdev.strikemaps.map.renderer.implementation;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
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.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
@@ -16,6 +18,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class VtmRenderer implements MapRenderer {
|
public class VtmRenderer implements MapRenderer {
|
||||||
|
|
||||||
AppController app;
|
AppController app;
|
||||||
MapComponent controller;
|
MapComponent controller;
|
||||||
|
|
||||||
@@ -30,7 +33,12 @@ public class VtmRenderer implements MapRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void overlayUpdate(MapOverlay overlay) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void styleUpdate(MapStyle style) {
|
||||||
//TODO
|
//TODO
|
||||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||||
OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build();
|
OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build();
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ public class MapStyle {
|
|||||||
|
|
||||||
public JsonNode metadata; // everything except layers + sources
|
public JsonNode metadata; // everything except layers + sources
|
||||||
public Map<String, MapSource> sources;
|
public Map<String, MapSource> sources;
|
||||||
public ArrayNode layerDefinitions; // the "layers" array
|
public ArrayNode layerDefinitions; // "layers" array
|
||||||
|
|
||||||
//FIXME
|
//FIXME
|
||||||
public static MapStyle fromMapLibreJsonFile(String filename, AppController app) {
|
public static MapStyle fromJsonFile(String filename, AppController app) {
|
||||||
String styleContents;
|
String styleContents;
|
||||||
if (filename.startsWith("/storage")) styleContents = FileHelper.loadStringFromUserFile(filename);
|
if (filename.startsWith("/storage")) styleContents = FileHelper.loadStringFromUserFile(filename);
|
||||||
else styleContents = FileHelper.loadStringFromAssetFile(filename, app);
|
else styleContents = FileHelper.loadStringFromAssetFile(filename, app);
|
||||||
|
|||||||
+1
-1
@@ -56,6 +56,6 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
|
|||||||
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
|
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
|
||||||
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
|
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
|
||||||
for (String style : stylePaths)
|
for (String style : stylePaths)
|
||||||
stylesLayout.addView(GenericItem.fromStyle(MapStyle.fromMapLibreJsonFile(style, app), map).makeView(ui));
|
stylesLayout.addView(GenericItem.fromStyle(MapStyle.fromJsonFile(style, app), map).makeView(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
<string name="app_name">Strike Maps</string>
|
<string name="app_name">Strike Maps</string>
|
||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
<string name="attribution_title"> Attribution </string>
|
<string name="attribution_title"> Attribution </string>
|
||||||
<string name="shipped_attribution">Earth map data Included;\n - © OpenStreetMap Contributors\nMap Rendering libraries Included;\n - MapLibre developed by MapLibre Organization\n - Vtm developed by Mapsforge\nStrike Maps made with <3 by konggdev</string>
|
<string name="shipped_attribution">Map Rendering libraries Included;\n - MapLibre developed by MapLibre Organization\n - Vtm developed by Mapsforge\nStrike Maps made with <3 by konggdev</string>
|
||||||
<string-array name="map_renderers">
|
<string-array name="map_renderers">
|
||||||
<item>MapLibre Native</item>
|
|
||||||
<item>MapLibre GL JS</item>
|
<item>MapLibre GL JS</item>
|
||||||
|
<item>MapLibre Native</item>
|
||||||
<item>VTM</item>
|
<item>VTM</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user