Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions build_keys.conf.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Local OSM client ID configuration for builds
# Local build key configuration
# Copy this file to build_keys.conf and fill in your values
# This file is gitignored to keep your keys secret
#
# Get your client IDs from:
# Production: https://www.openstreetmap.org/oauth2/applications
# Production: https://www.openstreetmap.org/oauth2/applications
# Sandbox: https://master.apis.dev.openstreetmap.org/oauth2/applications

OSM_PROD_CLIENTID=your_production_client_id_here
OSM_SANDBOX_CLIENTID=your_sandbox_client_id_here
OSM_SANDBOX_CLIENTID=your_sandbox_client_id_here

# Optional: Stadia Maps API key for vector tiles
# Get a free key from https://stadiamaps.com
STADIA_API_KEY=
11 changes: 10 additions & 1 deletion do_builds.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ if [ ! -f "build_keys.conf" ]; then
exit 1
fi

echo "Loading OSM client IDs from build_keys.conf..."
echo "Loading keys from build_keys.conf..."
OSM_PROD_CLIENTID=$(read_from_file "OSM_PROD_CLIENTID")
OSM_SANDBOX_CLIENTID=$(read_from_file "OSM_SANDBOX_CLIENTID")
STADIA_API_KEY=$(read_from_file "STADIA_API_KEY")

# Check required keys
if [ -z "$OSM_PROD_CLIENTID" ]; then
Expand All @@ -80,6 +81,14 @@ fi
# Build the dart-define arguments
DART_DEFINE_ARGS="--dart-define=OSM_PROD_CLIENTID=$OSM_PROD_CLIENTID --dart-define=OSM_SANDBOX_CLIENTID=$OSM_SANDBOX_CLIENTID"

# Optional keys
if [ -n "$STADIA_API_KEY" ]; then
DART_DEFINE_ARGS="$DART_DEFINE_ARGS --dart-define=STADIA_API_KEY=$STADIA_API_KEY"
echo " Stadia Maps API key: configured"
else
echo " Stadia Maps API key: not set (vector tiles will require manual key entry)"
fi

# Run tests before building
echo "Running tests..."
flutter test || exit 1
Expand Down
12 changes: 12 additions & 0 deletions lib/app_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'models/operator_profile.dart';
import 'models/osm_node.dart';
import 'models/pending_upload.dart';
import 'models/suspected_location.dart';
import 'models/service_endpoint.dart';
import 'models/tile_provider.dart';
import 'models/search_result.dart';
import 'services/offline_area_service.dart';
Expand All @@ -31,6 +32,7 @@ import 'state/operator_profile_state.dart';
import 'state/profile_state.dart';
import 'state/search_state.dart';
import 'state/session_state.dart';
import 'state/service_registry.dart';
import 'state/settings_state.dart';
import 'state/suspected_location_state.dart';
import 'state/upload_queue_state.dart';
Expand Down Expand Up @@ -167,6 +169,14 @@ class AppState extends ChangeNotifier {
bool get hasUnreadMessages => _messagesState.hasUnreadMessages;
bool get isCheckingMessages => _messagesState.isChecking;

// API endpoint settings
List<ServiceEndpoint> get routingEndpoints => _settingsState.routingEndpoints;
List<ServiceEndpoint> get enabledRoutingEndpoints => _settingsState.enabledRoutingEndpoints;
List<ServiceEndpoint> get overpassEndpoints => _settingsState.overpassEndpoints;
List<ServiceEndpoint> get enabledOverpassEndpoints => _settingsState.enabledOverpassEndpoints;
ServiceRegistry<ServiceEndpoint> get routingRegistry => _settingsState.routingRegistry;
ServiceRegistry<ServiceEndpoint> get overpassRegistry => _settingsState.overpassRegistry;

// Tile provider state
List<TileProvider> get tileProviders => _settingsState.tileProviders;
TileType? get selectedTileType => _settingsState.selectedTileType;
Expand Down Expand Up @@ -763,6 +773,8 @@ class AppState extends ChangeNotifier {
await _settingsState.setDistanceUnit(unit);
}

// Endpoint registry methods are accessed via routingRegistry/overpassRegistry directly

// ---------- Queue Methods ----------
void clearQueue() {
_uploadQueueState.clearQueue();
Expand Down
25 changes: 24 additions & 1 deletion lib/dev_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const double kNavigationMinRouteDistance = 100.0; // meters - minimum distance b
const double kNavigationDistanceWarningThreshold = 300000.0; // meters - distance threshold for timeout warning (30km)

// Node display configuration
const int kDefaultMaxNodes = 500; // Default maximum number of nodes to render on the map at once
const int kDefaultMaxNodes = 2000; // Default maximum number of nodes to render on the map at once (clustering handles visual density)

// NSI (Name Suggestion Index) configuration
const int kNSIMinimumHitCount = 500; // Minimum hit count for NSI suggestions to be considered useful
Expand All @@ -163,8 +163,31 @@ const int kMaxReasonableTileCount = 20000;
const int kAbsoluteMaxTileCount = 50000;
const int kAbsoluteMaxZoom = 23;

// Direction cone zoom gating
const int kDirectionConeMinZoomLevel = 14;

// Direction cone arc smoothness
const int kDirectionConeArcPoints = 36; // points per 90 degrees
const int kDirectionConeMinArcPoints = 12; // minimum for narrow FOVs

// Node icon configuration
const double kNodeIconDiameter = 18.0;

// Node marker zoom scaling
const double kNodeIconReferenceZoom = 15.0;
const double kNodeIconScalePerZoom = 2.0; // px per zoom level
const double kNodeIconMinDiameter = 8.0;
const double kNodeIconMaxDiameter = 28.0;

/// Returns marker diameter scaled by current zoom level
double getScaledNodeDiameter(double zoom) {
final scaled = kNodeIconDiameter + (zoom - kNodeIconReferenceZoom) * kNodeIconScalePerZoom;
return scaled.clamp(kNodeIconMinDiameter, kNodeIconMaxDiameter);
}

// Clustering
const int kNodeClusterMaxZoomLevel = 13; // clustering active at zoom <= 13; disabled at zoom >= 14
const double kClusterIconDiameter = 32.0;
const double _kNodeRingThicknessBase = 2.5;
const double kNodeDotOpacity = 0.3; // Opacity for the grey dot interior
const Color kNodeRingColorReal = Color(0xFF3036F0); // Real nodes from OSM - blue
Expand Down
8 changes: 5 additions & 3 deletions lib/keys.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// OpenStreetMap OAuth client IDs for this app.
// These must be provided via --dart-define at build time.
// Build-time API keys, provided via --dart-define.

/// Whether OSM OAuth secrets were provided at build time.
/// When false, the app should force simulate mode.
Expand All @@ -17,4 +16,7 @@ String get kOsmProdClientId {
String get kOsmSandboxClientId {
const fromBuild = String.fromEnvironment('OSM_SANDBOX_CLIENTID');
return fromBuild;
}
}

// Stadia Maps API key (optional — vector tiles won't load without it).
const kStadiaApiKey = String.fromEnvironment('STADIA_API_KEY');
25 changes: 23 additions & 2 deletions lib/localizations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,23 @@
"advancedSettings": "Erweiterte Einstellungen",
"advancedSettingsSubtitle": "Leistungs-, Warnungs- und Kachelanbieter-Einstellungen",
"proximityAlerts": "Näherungswarnungen",
"networkStatusIndicator": "Netzwerkstatus-Anzeige"
"networkStatusIndicator": "Netzwerkstatus-Anzeige",
"apiEndpoints": "API-Endpunkte",
"apiEndpointsDescription": "Prioritätsreihenfolge der API-Endpunkte konfigurieren, benutzerdefinierte Endpunkte hinzufügen oder auf Standard zurücksetzen.",
"apiEndpointRouting": "Routing",
"apiEndpointNodeSource": "Knotenquelle",
"invalidUrl": "Geben Sie eine gültige HTTPS-URL ein",
"addEndpoint": "Endpunkt hinzufügen",
"editEndpoint": "Endpunkt bearbeiten",
"resetToDefaults": "Auf Standard zurücksetzen",
"endpointName": "Endpunktname",
"endpointUrl": "Endpunkt-URL",
"endpointMaxRetries": "Max. Wiederholungen",
"endpointTimeout": "Timeout (Sekunden)",
"endpointEnabled": "Aktiviert",
"noEnabledEndpoints": "Mindestens ein Endpunkt muss aktiviert bleiben",
"confirmResetEndpoints": "Alle Endpunkte auf Standardkonfiguration zurücksetzen?",
"builtInEndpoint": "Eingebaut"
},
"proximityAlerts": {
"getNotified": "Benachrichtigung erhalten beim Annähern an Überwachungsgeräte",
Expand Down Expand Up @@ -284,7 +300,12 @@
"fetchPreview": "Vorschau Laden",
"previewTileLoaded": "Vorschau-Kachel erfolgreich geladen",
"previewTileFailed": "Vorschau laden fehlgeschlagen: {}",
"save": "Speichern"
"save": "Speichern",
"rasterTiles": "Raster",
"vectorTiles": "Vektor",
"styleUrl": "Stil-URL",
"styleUrlHint": "https://beispiel.com/style.json",
"styleUrlRequired": "Stil-URL ist erforderlich"
},
"profiles": {
"nodeProfiles": "Knoten-Profile",
Expand Down
25 changes: 23 additions & 2 deletions lib/localizations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,23 @@
"advancedSettings": "Advanced Settings",
"advancedSettingsSubtitle": "Performance, alerts, and tile provider settings",
"proximityAlerts": "Proximity Alerts",
"networkStatusIndicator": "Network Status Indicator"
"networkStatusIndicator": "Network Status Indicator",
"apiEndpoints": "API Endpoints",
"apiEndpointsDescription": "Configure API endpoint priority order, add custom endpoints, or reset to defaults.",
"apiEndpointRouting": "Routing",
"apiEndpointNodeSource": "Node Source",
"invalidUrl": "Enter a valid HTTPS URL",
"addEndpoint": "Add Endpoint",
"editEndpoint": "Edit Endpoint",
"resetToDefaults": "Reset to Defaults",
"endpointName": "Endpoint Name",
"endpointUrl": "Endpoint URL",
"endpointMaxRetries": "Max Retries",
"endpointTimeout": "Timeout (seconds)",
"endpointEnabled": "Enabled",
"noEnabledEndpoints": "At least one endpoint must remain enabled",
"confirmResetEndpoints": "Reset all endpoints to their default configuration?",
"builtInEndpoint": "Built-in"
},
"proximityAlerts": {
"getNotified": "Get notified when approaching surveillance devices",
Expand Down Expand Up @@ -321,7 +337,12 @@
"fetchPreview": "Fetch Preview",
"previewTileLoaded": "Preview tile loaded successfully",
"previewTileFailed": "Failed to fetch preview: {}",
"save": "Save"
"save": "Save",
"rasterTiles": "Raster",
"vectorTiles": "Vector",
"styleUrl": "Style URL",
"styleUrlHint": "https://example.com/style.json",
"styleUrlRequired": "Style URL is required"
},
"profiles": {
"nodeProfiles": "Node Profiles",
Expand Down
25 changes: 23 additions & 2 deletions lib/localizations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,23 @@
"advancedSettings": "Configuración Avanzada",
"advancedSettingsSubtitle": "Configuración de rendimiento, alertas y proveedores de teselas",
"proximityAlerts": "Alertas de Proximidad",
"networkStatusIndicator": "Indicador de Estado de Red"
"networkStatusIndicator": "Indicador de Estado de Red",
"apiEndpoints": "Endpoints de API",
"apiEndpointsDescription": "Configurar el orden de prioridad de los endpoints de API, agregar endpoints personalizados o restablecer valores predeterminados.",
"apiEndpointRouting": "Enrutamiento",
"apiEndpointNodeSource": "Fuente de nodos",
"invalidUrl": "Ingrese una URL HTTPS válida",
"addEndpoint": "Agregar Endpoint",
"editEndpoint": "Editar Endpoint",
"resetToDefaults": "Restablecer Valores Predeterminados",
"endpointName": "Nombre del Endpoint",
"endpointUrl": "URL del Endpoint",
"endpointMaxRetries": "Máx. Reintentos",
"endpointTimeout": "Tiempo de espera (segundos)",
"endpointEnabled": "Habilitado",
"noEnabledEndpoints": "Al menos un endpoint debe permanecer habilitado",
"confirmResetEndpoints": "¿Restablecer todos los endpoints a su configuración predeterminada?",
"builtInEndpoint": "Incorporado"
},
"proximityAlerts": {
"getNotified": "Recibe notificaciones al acercarte a dispositivos de vigilancia",
Expand Down Expand Up @@ -321,7 +337,12 @@
"fetchPreview": "Obtener Vista Previa",
"previewTileLoaded": "Tile de vista previa cargado exitosamente",
"previewTileFailed": "Falló al obtener vista previa: {}",
"save": "Guardar"
"save": "Guardar",
"rasterTiles": "Ráster",
"vectorTiles": "Vectorial",
"styleUrl": "URL de estilo",
"styleUrlHint": "https://ejemplo.com/style.json",
"styleUrlRequired": "La URL de estilo es obligatoria"
},
"profiles": {
"nodeProfiles": "Perfiles de Nodos",
Expand Down
25 changes: 23 additions & 2 deletions lib/localizations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,23 @@
"advancedSettings": "Paramètres Avancés",
"advancedSettingsSubtitle": "Paramètres de performance, alertes et fournisseurs de tuiles",
"proximityAlerts": "Alertes de Proximité",
"networkStatusIndicator": "Indicateur de Statut Réseau"
"networkStatusIndicator": "Indicateur de Statut Réseau",
"apiEndpoints": "Points d'accès API",
"apiEndpointsDescription": "Configurer l'ordre de priorité des points d'accès API, ajouter des points d'accès personnalisés ou réinitialiser aux valeurs par défaut.",
"apiEndpointRouting": "Routage",
"apiEndpointNodeSource": "Source des nœuds",
"invalidUrl": "Entrez une URL HTTPS valide",
"addEndpoint": "Ajouter un Point d'accès",
"editEndpoint": "Modifier le Point d'accès",
"resetToDefaults": "Réinitialiser aux Valeurs par Défaut",
"endpointName": "Nom du Point d'accès",
"endpointUrl": "URL du Point d'accès",
"endpointMaxRetries": "Max. Tentatives",
"endpointTimeout": "Délai d'attente (secondes)",
"endpointEnabled": "Activé",
"noEnabledEndpoints": "Au moins un point d'accès doit rester activé",
"confirmResetEndpoints": "Réinitialiser tous les points d'accès à leur configuration par défaut ?",
"builtInEndpoint": "Intégré"
},
"proximityAlerts": {
"getNotified": "Recevoir des notifications en s'approchant de dispositifs de surveillance",
Expand Down Expand Up @@ -321,7 +337,12 @@
"fetchPreview": "Récupérer Aperçu",
"previewTileLoaded": "Tuile d'aperçu chargée avec succès",
"previewTileFailed": "Échec de récupération de l'aperçu: {}",
"save": "Sauvegarder"
"save": "Sauvegarder",
"rasterTiles": "Raster",
"vectorTiles": "Vectoriel",
"styleUrl": "URL de style",
"styleUrlHint": "https://exemple.com/style.json",
"styleUrlRequired": "L'URL de style est requise"
},
"profiles": {
"nodeProfiles": "Profils de Nœuds",
Expand Down
25 changes: 23 additions & 2 deletions lib/localizations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,23 @@
"advancedSettings": "Impostazioni Avanzate",
"advancedSettingsSubtitle": "Impostazioni di prestazioni, avvisi e fornitori di tessere",
"proximityAlerts": "Avvisi di Prossimità",
"networkStatusIndicator": "Indicatore di Stato di Rete"
"networkStatusIndicator": "Indicatore di Stato di Rete",
"apiEndpoints": "Endpoint API",
"apiEndpointsDescription": "Configura l'ordine di priorità degli endpoint API, aggiungi endpoint personalizzati o ripristina i valori predefiniti.",
"apiEndpointRouting": "Routing",
"apiEndpointNodeSource": "Fonte dei nodi",
"invalidUrl": "Inserisci un URL HTTPS valido",
"addEndpoint": "Aggiungi Endpoint",
"editEndpoint": "Modifica Endpoint",
"resetToDefaults": "Ripristina Predefiniti",
"endpointName": "Nome Endpoint",
"endpointUrl": "URL Endpoint",
"endpointMaxRetries": "Max Tentativi",
"endpointTimeout": "Timeout (secondi)",
"endpointEnabled": "Abilitato",
"noEnabledEndpoints": "Almeno un endpoint deve rimanere abilitato",
"confirmResetEndpoints": "Ripristinare tutti gli endpoint alla configurazione predefinita?",
"builtInEndpoint": "Integrato"
},
"proximityAlerts": {
"getNotified": "Ricevi notifiche quando ti avvicini a dispositivi di sorveglianza",
Expand Down Expand Up @@ -321,7 +337,12 @@
"fetchPreview": "Ottieni Anteprima",
"previewTileLoaded": "Tile di anteprima caricato con successo",
"previewTileFailed": "Impossibile ottenere l'anteprima: {}",
"save": "Salva"
"save": "Salva",
"rasterTiles": "Raster",
"vectorTiles": "Vettoriale",
"styleUrl": "URL dello stile",
"styleUrlHint": "https://esempio.com/style.json",
"styleUrlRequired": "L'URL dello stile è obbligatorio"
},
"profiles": {
"nodeProfiles": "Profili Nodo",
Expand Down
25 changes: 23 additions & 2 deletions lib/localizations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,23 @@
"advancedSettings": "Geavanceerde Instellingen",
"advancedSettingsSubtitle": "Prestaties, waarschuwingen en tile provider instellingen",
"proximityAlerts": "Nabijheids Waarschuwingen",
"networkStatusIndicator": "Netwerk Status Indicator"
"networkStatusIndicator": "Netwerk Status Indicator",
"apiEndpoints": "API-eindpunten",
"apiEndpointsDescription": "Configureer de prioriteitsvolgorde van API-eindpunten, voeg aangepaste eindpunten toe of reset naar standaard.",
"apiEndpointRouting": "Routing",
"apiEndpointNodeSource": "Knooppuntbron",
"invalidUrl": "Voer een geldige HTTPS-URL in",
"addEndpoint": "Eindpunt Toevoegen",
"editEndpoint": "Eindpunt Bewerken",
"resetToDefaults": "Standaardwaarden Herstellen",
"endpointName": "Eindpuntnaam",
"endpointUrl": "Eindpunt-URL",
"endpointMaxRetries": "Max. Pogingen",
"endpointTimeout": "Timeout (seconden)",
"endpointEnabled": "Ingeschakeld",
"noEnabledEndpoints": "Minstens één eindpunt moet ingeschakeld blijven",
"confirmResetEndpoints": "Alle eindpunten terugzetten naar standaardconfiguratie?",
"builtInEndpoint": "Ingebouwd"
},
"proximityAlerts": {
"getNotified": "Krijg meldingen wanneer u surveillance apparaten nadert",
Expand Down Expand Up @@ -321,7 +337,12 @@
"fetchPreview": "Haal Voorbeeld Op",
"previewTileLoaded": "Voorbeeld tile succesvol geladen",
"previewTileFailed": "Kon voorbeeld niet ophalen: {}",
"save": "Opslaan"
"save": "Opslaan",
"rasterTiles": "Raster",
"vectorTiles": "Vector",
"styleUrl": "Stijl-URL",
"styleUrlHint": "https://voorbeeld.com/style.json",
"styleUrlRequired": "Stijl-URL is vereist"
},
"profiles": {
"nodeProfiles": "Node Profielen",
Expand Down
Loading
Loading