Compare commits

...

7 Commits

Author SHA1 Message Date
ce4ce2f2ac Abstract style model further from MapLibre 2026-04-16 17:46:32 +02:00
cb892d1bbd Update classic style 2026-04-16 17:46:32 +02:00
b2a07b80d0 Fix raster sources
Fix typo
2026-04-16 17:46:32 +02:00
ea67fbd352 Update README.md 2026-04-16 17:46:32 +02:00
38fb1500b0 Small fixes 2026-04-16 17:46:30 +02:00
881dedf050 Create data namespace 2026-04-16 17:46:26 +02:00
ce13d0c8aa Refractor AlertDialogFactory into UI namespace 2026-04-16 17:46:15 +02:00
25 changed files with 3118 additions and 175 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ DS_Store
.cxx .cxx
local.properties local.properties
/legacy_code/ /legacy_code/
maptiler*

View File

@@ -1,8 +1,8 @@
## Strike Maps Project Android Client ## Strike Maps Project Android Client
Strike Maps is a project hoping to create a multi-platform, <br> Strike Maps is a project hoping to create a multi-platform, <br>
user-facing, complete FOSS mapping stack, focusing on style customization <br> user-facing, complete FOSS mapping stack, focusing on customization <br>
and offline features. and offline features.
<br> <br>
This Android app has been in development since around January 2026 This Android app has been in development since around January 2026

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
"sources": { "sources": {
"satelite": { "satelite": {
"type": "raster", "type": "raster",
"schema" : "raster",
"tiles": [ "tiles": [
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
], ],
@@ -14,8 +15,6 @@
"tileSize": 512 "tileSize": 512
} }
}, },
"sprite": "",
"glyphs": "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
"layers": [ "layers": [
{ {
"id": "satelite", "id": "satelite",

View File

@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.helper; package eu.konggdev.strikemaps.data.helper;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.os.Environment; import android.os.Environment;
@@ -106,4 +106,4 @@ public final class FileHelper {
return fileList.toArray(new String[0]); return fileList.toArray(new String[0]);
} }
} }

View File

@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.helper; package eu.konggdev.strikemaps.data.helper;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@@ -37,4 +37,4 @@ public final class UserPrefsHelper {
public static boolean lastLocationEnabled(SharedPreferences prefs, boolean status) { public static boolean lastLocationEnabled(SharedPreferences prefs, boolean status) {
return prefs.edit().putBoolean(KEY_LAST_LOCATION_ENABLED, status).commit(); return prefs.edit().putBoolean(KEY_LAST_LOCATION_ENABLED, status).commit();
} }
} }

View File

@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.provider; package eu.konggdev.strikemaps.data.provider;
public class HttpDataProvider implements Provider { public class HttpDataProvider implements Provider {
//TODO //TODO

View File

@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.provider; package eu.konggdev.strikemaps.data.provider;
import android.Manifest; import android.Manifest;
import android.content.Context; import android.content.Context;

View File

@@ -0,0 +1,5 @@
package eu.konggdev.strikemaps.data.provider;
public interface Provider {
}

View File

@@ -2,8 +2,8 @@ package eu.konggdev.strikemaps.map;
import java.util.*; import java.util.*;
import eu.konggdev.strikemaps.Component; import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.factory.AlertDialogFactory; import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.helper.UserPrefsHelper; import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer; import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer;
import eu.konggdev.strikemaps.map.style.MapStyle; import eu.konggdev.strikemaps.map.style.MapStyle;
import org.maplibre.android.geometry.LatLng; import org.maplibre.android.geometry.LatLng;

View File

@@ -1,16 +1,11 @@
package eu.konggdev.strikemaps.map.layer; package eu.konggdev.strikemaps.map.layer;
import org.maplibre.android.style.layers.Layer; import com.fasterxml.jackson.databind.JsonNode;
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 class MapLayer {
public GeoJsonSource source; public JsonNode layer;
public Layer layer;
public MapLayer(GeoJsonSource source, Layer layer) { public MapLayer(JsonNode layer) {
this.source = source; this.layer = layer;
this.layer = layer;
} }
} }

View File

@@ -0,0 +1,16 @@
package eu.konggdev.strikemaps.map.layer;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.map.source.MapSource;
public class SourcedMapLayer {
public String key;
public MapSource source;
public JsonNode layer;
public SourcedMapLayer(String key, MapSource source, JsonNode layer) {
this.key = key;
this.source = source;
this.layer = layer;
}
}

View File

@@ -1,8 +1,8 @@
package eu.konggdev.strikemaps.map.overlay; package eu.konggdev.strikemaps.map.overlay;
import eu.konggdev.strikemaps.map.layer.MapLayer; import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
/* More or less a data-driven layer factory */ /* More or less a data-driven layer factory */
public interface MapOverlay { public interface MapOverlay {
public MapLayer makeLayer(); public SourcedMapLayer makeLayer();
} }

View File

@@ -0,0 +1,92 @@
package eu.konggdev.strikemaps.map.overlay.implementation;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
import org.maplibre.geojson.Feature;
import org.maplibre.geojson.FeatureCollection;
import org.maplibre.geojson.Point;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
public class LocationOverlay implements MapOverlay, LocationListener {
LocationDataProvider locationDataProvider;
AppController app;
MapComponent map;
public Location currentLocation = null;
public LocationOverlay(AppController app) {
this.app = app;
this.map = app.getMap();
this.locationDataProvider = new LocationDataProvider(app.getActivity(), this);
}
@Override
public SourcedMapLayer makeLayer() {
MapSource source = new MapSource();
source.type = "geojson";
ObjectMapper mapper = new ObjectMapper();
try {
ObjectNode data = mapper.createObjectNode();
data.put("type", "Feature");
if(currentLocation != null) {
ObjectNode geometry = mapper.createObjectNode();
geometry.put("type", "Point");
ArrayNode coordinates = mapper.createArrayNode();
coordinates.add(currentLocation.getLongitude());
coordinates.add(currentLocation.getLatitude());
geometry.set("coordinates", coordinates);
data.set("geometry", geometry);
data.set("properties", mapper.createObjectNode());
}
source.data = data;
} catch (Exception e) {
e.printStackTrace();
}
ObjectNode layer = mapper.createObjectNode();
layer.put("id", "location");
layer.put("type", "circle");
layer.put("source", "location");
ObjectNode paint = mapper.createObjectNode();
paint.put("circle-radius", 5);
paint.put("circle-color", "#1E88E5");
paint.put("circle-stroke-color", "#FFFFFF");
paint.put("circle-stroke-width", 1.5);
layer.set("paint", paint);
ObjectNode layout = mapper.createObjectNode();
layout.put("circle-pitch-alignment", "map");
layer.set("layout", layout);
ArrayNode layers = mapper.createArrayNode();
layers.add(layer);
return new SourcedMapLayer("location", source, layers);
}
@Override
public void onLocationChanged(@NonNull Location location) {
this.currentLocation = location;
map.onOverlayUpdate();
}
}

View File

@@ -1,11 +1,12 @@
package eu.konggdev.strikemaps.map.overlay.overlay; package eu.konggdev.strikemaps.map.overlay.implementation;
import eu.konggdev.strikemaps.map.layer.MapLayer; import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.overlay.MapOverlay;
public class PointSelectionOverlay implements MapOverlay { public class PointSelectionOverlay implements MapOverlay {
@Override @Override
public MapLayer makeLayer() { public SourcedMapLayer makeLayer() {
return null; return null;
} }
} }

View File

@@ -1,62 +0,0 @@
package eu.konggdev.strikemaps.map.overlay.overlay;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.provider.LocationDataProvider;
import org.maplibre.android.style.layers.CircleLayer;
import org.maplibre.android.style.layers.Property;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.geojson.Feature;
import org.maplibre.geojson.FeatureCollection;
import org.maplibre.geojson.Point;
import static org.maplibre.android.style.layers.PropertyFactory.*;
public class LocationOverlay implements MapOverlay, LocationListener {
LocationDataProvider locationDataProvider;
AppController app;
MapComponent map;
public Location currentLocation = null;
public LocationOverlay(AppController app) {
this.app = app;
this.map = app.getMap();
this.locationDataProvider = new LocationDataProvider(app.getActivity(), this);
}
@Override
public MapLayer makeLayer() {
GeoJsonSource source = new GeoJsonSource(
"location",
FeatureCollection.fromFeatures(new Feature[]{}) // empty
);
if (currentLocation != null)
source.setGeoJson(Feature.fromGeometry(Point.fromLngLat(currentLocation.getLongitude(), currentLocation.getLatitude())));
CircleLayer layer = new CircleLayer("location", "location");
layer.setProperties(
circleRadius(5f),
circleColor(Color.parseColor("#1E88E5")),
circleStrokeColor(Color.WHITE),
circleStrokeWidth(1.5f),
circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP)
);
return new MapLayer(source, layer);
}
@Override
public void onLocationChanged(@NonNull Location location) {
this.currentLocation = location;
map.onOverlayUpdate();
}
}

View File

@@ -15,5 +15,6 @@ public interface MapRenderer {
View getView(); View getView();
//TODO: Get rid of MapLibre Feature class dependence
List<Feature> featuresAtPoint(LatLng point); List<Feature> featuresAtPoint(LatLng point);
} }

View File

@@ -4,12 +4,13 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonInclude;
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.helper.FileHelper; import com.fasterxml.jackson.databind.node.ArrayNode;
import eu.konggdev.strikemaps.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.MapLayer; 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;
@@ -40,25 +41,38 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
mapView.getMapAsync(this); mapView.getMapAsync(this);
} }
void passLayer(MapLayer layer) {
map.getStyle().addSource(layer.source);
map.getStyle().addLayer(layer.layer);
}
@Override @Override
public void reload() { public void reload() {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
MapStyle style = controller.style; MapStyle style = controller.style;
try { try {
/* Take metadata from MapStyle
everything outside sources, layers */
ObjectNode root = style.metadata.deepCopy(); ObjectNode root = style.metadata.deepCopy();
root.set("sources", mapper.valueToTree(style.sources));
root.set("layers", style.layerDefinitions); //Sources
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(root)), intStyle -> { ObjectNode sources = mapper.createObjectNode();
for(MapOverlay overlay : controller.overlays.values()) { style.sources.forEach((k, v) -> sources.set(k, mapper.valueToTree(v)));
passLayer(overlay.makeLayer());
} //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");
e.printStackTrace(); e.printStackTrace();
} }
} }

View File

@@ -1,9 +1,21 @@
package eu.konggdev.strikemaps.map.source; package eu.konggdev.strikemaps.map.source;
import com.fasterxml.jackson.databind.JsonNode;
public class MapSource { public class MapSource {
public String url; public String url;
public JsonNode data;
public String type; public String type;
public String schema; public String schema;
public JsonNode tiles;
public int minzoom;
public int maxzoom;
public String scheme;
public int tileSize;
public String attribution;
public String encoding;
public MapSource() { } public MapSource() { }
} }

View File

@@ -2,15 +2,14 @@ package eu.konggdev.strikemaps.map.style;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
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 com.fasterxml.jackson.databind.node.ArrayNode;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.FileHelper; import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.source.MapSource; import eu.konggdev.strikemaps.map.source.MapSource;
import org.json.JSONObject;
import java.util.*; import java.util.*;
@@ -21,7 +20,7 @@ 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 JsonNode layerDefinitions; // the "layers" array public ArrayNode layerDefinitions; // the "layers" array
//FIXME //FIXME
public static MapStyle fromMapLibreJsonFile(String filename, AppController app) { public static MapStyle fromMapLibreJsonFile(String filename, AppController app) {
@@ -33,7 +32,7 @@ public class MapStyle {
try { try {
JsonNode root = mapper.readTree(styleContents); JsonNode root = mapper.readTree(styleContents);
MapStyle style = new MapStyle(); MapStyle style = new MapStyle();
style.name = root.path("name").asText(); style.name = root.path("name").asText();
style.icon = getIcon(root.path("icon").asText(), app); style.icon = getIcon(root.path("icon").asText(), app);
@@ -42,7 +41,7 @@ public class MapStyle {
new TypeReference<Map<String, MapSource>>() {} new TypeReference<Map<String, MapSource>>() {}
); );
style.layerDefinitions = root.path("layers"); style.layerDefinitions = root.withArray("layers");
ObjectNode metadata = root.deepCopy(); ObjectNode metadata = root.deepCopy();
metadata.remove("layers"); metadata.remove("layers");

View File

@@ -1,5 +0,0 @@
package eu.konggdev.strikemaps.provider;
public interface Provider {
}

View File

@@ -1,21 +1,16 @@
package eu.konggdev.strikemaps.ui.element.item; package eu.konggdev.strikemaps.ui.element.item;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View; import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.R; 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.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle; import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.UIComponent; import eu.konggdev.strikemaps.ui.UIComponent;
import java.io.InputStream;
public class GenericItem implements UIItem { public class GenericItem implements UIItem {
@NonNull public String name; @NonNull public String name;
public Bitmap image; public Bitmap image;

View File

@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.factory; package eu.konggdev.strikemaps.ui.factory;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.graphics.Color; import android.graphics.Color;

View File

@@ -15,8 +15,8 @@ import android.widget.Toast;
import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.UserPrefsHelper; import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.overlay.overlay.LocationOverlay; import eu.konggdev.strikemaps.map.overlay.implementation.LocationOverlay;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup; import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
public class FragmentLayoutControls extends Fragment implements Layout { public class FragmentLayoutControls extends Fragment implements Layout {

View File

@@ -9,14 +9,12 @@ import android.widget.LinearLayout;
import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.FileHelper; import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.factory.AlertDialogFactory;
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.ui.UIComponent; import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.element.item.GenericItem; import eu.konggdev.strikemaps.ui.element.item.GenericItem;
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;