einsatzprotokoll/source/GeocodingService.mc
EiSiMo 216d40c2c5 Custom icons, improved GPS, history layout, address fixes
- Replace placeholder SVG icons with custom PNG icons (80x80 padded)
- AffineTransform for pixel-perfect icon centering in menu ring
- Colored circles behind icons with icon drawn at 60% size
- GPS: always wait full timeout (30s), keep best quality fix
- History: show address, PLZ+city, coordinates on separate lines
- Geocoding: fall back to "name" field for street-level results,
  include PLZ+city in cached address data
- Address distance threshold raised to 70m

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 19:05:17 +02:00

87 lines
3 KiB
MonkeyC

import Toybox.Communications;
import Toybox.Lang;
// Reverse-geocodes a lat/lon pair via Photon API. Calls back with
// a Dictionary { "addr" => String, "zip" => String } or null.
class GeocodingService {
private var _callback as Method(result as Dictionary or Null) as Void;
private var _lat as Float;
private var _lon as Float;
function initialize(lat as Float, lon as Float,
callback as Method(result as Dictionary or Null) as Void) {
_callback = callback;
_lat = lat;
_lon = lon;
}
function start() as Void {
var url = Config.PHOTON_URL;
var params = { "lon" => _lon, "lat" => _lat, "limit" => 1 };
var options = {
:method => Communications.HTTP_REQUEST_METHOD_GET,
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
:headers => { "Accept" => "application/json" }
};
Communications.makeWebRequest(url, params, options, method(:_onResponse));
}
function _onResponse(responseCode as Number, data as Dictionary or String or Null) as Void {
if (responseCode != 200 || data == null || !(data instanceof Dictionary)) {
_callback.invoke(null);
return;
}
var features = data["features"];
if (features == null || !(features instanceof Array) || features.size() == 0) {
_callback.invoke(null);
return;
}
var feature = features[0] as Dictionary;
var props = feature["properties"] as Dictionary;
var geometry = feature["geometry"] as Dictionary;
// Haversine check: only use address if result is within threshold.
if (geometry != null) {
var coords = geometry["coordinates"] as Array;
if (coords != null && coords.size() >= 2) {
var rLon = (coords[0] as Double).toFloat();
var rLat = (coords[1] as Double).toFloat();
var dist = Haversine.distance(_lat, _lon, rLat, rLon);
if (dist > Config.ADDRESS_MAX_DISTANCE_M) {
_callback.invoke(null);
return;
}
}
}
var street = props["street"];
if (street == null) { street = props["name"]; }
if (street == null) {
_callback.invoke(null);
return;
}
var addr = street as String;
var number = props["housenumber"];
if (number != null) {
addr = addr + " " + number;
}
var result = { "addr" => addr } as Dictionary;
var zip = props["postcode"];
var city = props["city"];
if (zip != null || city != null) {
var zipCity = "";
if (zip != null) { zipCity = zip as String; }
if (city != null) {
if (!zipCity.equals("")) { zipCity = zipCity + " "; }
zipCity = zipCity + city;
}
result["zip"] = zipCity;
}
_callback.invoke(result);
}
}