This commit is contained in:
randogoth 2025-02-27 13:43:31 +02:00
commit a5851e71fb
9 changed files with 664 additions and 0 deletions

96
lib/rugreefer.dart Normal file
View file

@ -0,0 +1,96 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:dart_geohash/dart_geohash.dart';
import 'package:latlong2/latlong.dart';
import 'package:turf/turf.dart';
class GeoMetadataLookup {
final String apiUrl;
final Directory cacheDir;
final double distanceThreshold;
final Distance distance = const Distance();
GeoMetadataLookup({
required this.apiUrl,
required this.cacheDir,
this.distanceThreshold = 10.0, // Default 10 meters
}) {
if (!cacheDir.existsSync()) {
cacheDir.createSync(recursive: true);
}
}
Future<List<Map<String, dynamic>>> lookup(double latitude, double longitude) async {
final String geohash = GeoHasher().encode(latitude, longitude, precision: 4);
final File cacheFile = File('${cacheDir.path}/$geohash.geojson.gz');
if (!cacheFile.existsSync()) {
await _downloadGeoJson(geohash, cacheFile);
}
final String geoJsonStr = utf8.decode(GZipCodec().decode(cacheFile.readAsBytesSync()));
final Map<String, dynamic> geoJson = jsonDecode(geoJsonStr);
return _findMetadata(latitude, longitude, geoJson);
}
Future<void> _downloadGeoJson(String geohash, File cacheFile) async {
final response = await http.get(Uri.parse('$apiUrl/$geohash.geojson.gz'));
if (response.statusCode == 200) {
cacheFile.writeAsBytesSync(GZipCodec().encode(response.bodyBytes));
} else {
throw Exception('Failed to download GeoJSON for geohash: $geohash');
}
}
List<Map<String, dynamic>> _findMetadata(double latitude, double longitude, Map<String, dynamic> geoJson) {
final Point point = Point(coordinates: Position(longitude, latitude));
List<Map<String, dynamic>> results = [];
for (var feature in geoJson['features']) {
final geometry = feature['geometry'];
final properties = feature['properties'] ?? {};
if (geometry['type'] == 'Polygon') {
final polygon = Polygon(
coordinates: (geometry['coordinates'] as List)
.map((ring) => (ring as List)
.map((p) => Position(p[0] as double, p[1] as double))
.toList())
.toList(),
);
}
else if (geometry['type'] == 'LineString') {
final line = LineString(coordinates: (geometry['coordinates'] as List).map((p) => Position(p[0], p[1])).toList());
if (_isPointNearLine(point, line)) {
results.add({'type': 'line', ...properties});
}
}
else if (geometry['type'] == 'MultiLineString') {
for (var lineCoords in geometry['coordinates']) {
final line = LineString(coordinates: lineCoords.map((p) => Position(p[0], p[1])).toList());
if (_isPointNearLine(point, line)) {
results.add({'type': 'multi_line', ...properties});
break;
}
}
}
}
return results;
}
bool _isPointNearLine(Point point, LineString line) {
for (var i = 0; i < line.coordinates.length - 1; i++) {
final p1 = line.coordinates[i];
final p2 = line.coordinates[i + 1];
final double dist = distance.distance(
LatLng(point.coordinates.lat.toDouble(), point.coordinates.lng.toDouble()),
LatLng(p1.lat.toDouble(), p1.lng.toDouble()),
);
if (dist <= distanceThreshold) return true;
}
return false;
}
}