americana / osm liberty merge
This commit is contained in:
parent
c48687b77c
commit
d0d9783b78
11 changed files with 180 additions and 33 deletions
|
@ -238,7 +238,7 @@
|
||||||
"mapStyleGoogleNormal": "Google Maps",
|
"mapStyleGoogleNormal": "Google Maps",
|
||||||
"mapStyleGoogleHybrid": "Google Maps (Hybrid)",
|
"mapStyleGoogleHybrid": "Google Maps (Hybrid)",
|
||||||
"mapStyleGoogleTerrain": "Google Maps (Terrain)",
|
"mapStyleGoogleTerrain": "Google Maps (Terrain)",
|
||||||
"mapStyleOsmAmericana": "OSM Americana",
|
"mapStyleOsmLiberty": "OSM Liberty",
|
||||||
"mapStyleOpenTopoMap": "OpenTopoMap",
|
"mapStyleOpenTopoMap": "OpenTopoMap",
|
||||||
"mapStyleOsmHot": "Humanitarian OSM",
|
"mapStyleOsmHot": "Humanitarian OSM",
|
||||||
"mapStyleStamenWatercolor": "Stamen Watercolor",
|
"mapStyleStamenWatercolor": "Stamen Watercolor",
|
||||||
|
@ -1034,7 +1034,7 @@
|
||||||
"mapZoomInTooltip": "Zoom in",
|
"mapZoomInTooltip": "Zoom in",
|
||||||
"mapZoomOutTooltip": "Zoom out",
|
"mapZoomOutTooltip": "Zoom out",
|
||||||
"mapPointNorthUpTooltip": "Point north up",
|
"mapPointNorthUpTooltip": "Point north up",
|
||||||
"mapAttributionOsmAmericana": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0)",
|
"mapAttributionOsmLiberty": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Hosted by [OSM Americana](https://tile.ourmap.us)",
|
||||||
"mapAttributionOpenTopoMap": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • [SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Tiles by [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)",
|
"mapAttributionOpenTopoMap": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • [SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Tiles by [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)",
|
||||||
"mapAttributionOsmHot": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [HOT](https://www.hotosm.org/) • Hosted by [OSM France](https://openstreetmap.fr/)",
|
"mapAttributionOsmHot": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [HOT](https://www.hotosm.org/) • Hosted by [OSM France](https://openstreetmap.fr/)",
|
||||||
"mapAttributionStamen": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)",
|
"mapAttributionStamen": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)",
|
||||||
|
|
|
@ -289,6 +289,11 @@ class Dependencies {
|
||||||
license: bsd3,
|
license: bsd3,
|
||||||
sourceUrl: 'https://github.com/greensopinion/flutter-vector-map-tiles',
|
sourceUrl: 'https://github.com/greensopinion/flutter-vector-map-tiles',
|
||||||
),
|
),
|
||||||
|
Dependency(
|
||||||
|
name: 'Vector Tile Renderer',
|
||||||
|
license: bsd3,
|
||||||
|
sourceUrl: 'https://github.com/greensopinion/dart-vector-tile-renderer',
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
static const List<Dependency> dartPackages = [
|
static const List<Dependency> dartPackages = [
|
||||||
|
@ -333,6 +338,11 @@ class Dependencies {
|
||||||
license: mit,
|
license: mit,
|
||||||
sourceUrl: 'https://github.com/fluttercommunity/get_it',
|
sourceUrl: 'https://github.com/fluttercommunity/get_it',
|
||||||
),
|
),
|
||||||
|
Dependency(
|
||||||
|
name: 'HTTP',
|
||||||
|
license: bsd3,
|
||||||
|
sourceUrl: 'https://github.com/dart-lang/http',
|
||||||
|
),
|
||||||
Dependency(
|
Dependency(
|
||||||
name: 'Intl',
|
name: 'Intl',
|
||||||
license: bsd3,
|
license: bsd3,
|
||||||
|
|
|
@ -68,7 +68,7 @@ extension ExtraEntryMapStyleView on EntryMapStyle {
|
||||||
EntryMapStyle.googleNormal => l10n.mapStyleGoogleNormal,
|
EntryMapStyle.googleNormal => l10n.mapStyleGoogleNormal,
|
||||||
EntryMapStyle.googleHybrid => l10n.mapStyleGoogleHybrid,
|
EntryMapStyle.googleHybrid => l10n.mapStyleGoogleHybrid,
|
||||||
EntryMapStyle.googleTerrain => l10n.mapStyleGoogleTerrain,
|
EntryMapStyle.googleTerrain => l10n.mapStyleGoogleTerrain,
|
||||||
EntryMapStyle.osmAmericana => l10n.mapStyleOsmAmericana,
|
EntryMapStyle.osmLiberty => l10n.mapStyleOsmLiberty,
|
||||||
EntryMapStyle.openTopoMap => l10n.mapStyleOpenTopoMap,
|
EntryMapStyle.openTopoMap => l10n.mapStyleOpenTopoMap,
|
||||||
EntryMapStyle.osmHot => l10n.mapStyleOsmHot,
|
EntryMapStyle.osmHot => l10n.mapStyleOsmHot,
|
||||||
EntryMapStyle.stamenWatercolor => l10n.mapStyleStamenWatercolor,
|
EntryMapStyle.stamenWatercolor => l10n.mapStyleStamenWatercolor,
|
||||||
|
|
|
@ -16,8 +16,8 @@ class Attribution extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
switch (style) {
|
switch (style) {
|
||||||
case EntryMapStyle.osmAmericana:
|
case EntryMapStyle.osmLiberty:
|
||||||
return _buildAttributionMarkdown(context, context.l10n.mapAttributionOsmAmericana);
|
return _buildAttributionMarkdown(context, context.l10n.mapAttributionOsmLiberty);
|
||||||
case EntryMapStyle.openTopoMap:
|
case EntryMapStyle.openTopoMap:
|
||||||
return _buildAttributionMarkdown(context, context.l10n.mapAttributionOpenTopoMap);
|
return _buildAttributionMarkdown(context, context.l10n.mapAttributionOpenTopoMap);
|
||||||
case EntryMapStyle.osmHot:
|
case EntryMapStyle.osmHot:
|
||||||
|
|
|
@ -185,7 +185,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
onMarkerTap: _onMarkerTap,
|
onMarkerTap: _onMarkerTap,
|
||||||
onMarkerLongPress: onMarkerLongPress,
|
onMarkerLongPress: onMarkerLongPress,
|
||||||
);
|
);
|
||||||
case EntryMapStyle.osmAmericana:
|
case EntryMapStyle.osmLiberty:
|
||||||
case EntryMapStyle.openTopoMap:
|
case EntryMapStyle.openTopoMap:
|
||||||
case EntryMapStyle.osmHot:
|
case EntryMapStyle.osmHot:
|
||||||
case EntryMapStyle.stamenWatercolor:
|
case EntryMapStyle.stamenWatercolor:
|
||||||
|
|
|
@ -198,8 +198,8 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
||||||
|
|
||||||
Widget _buildMapLayer() {
|
Widget _buildMapLayer() {
|
||||||
switch (widget.style) {
|
switch (widget.style) {
|
||||||
case EntryMapStyle.osmAmericana:
|
case EntryMapStyle.osmLiberty:
|
||||||
return const OsmAmericanaLayer();
|
return const OsmLibertyLayer();
|
||||||
case EntryMapStyle.openTopoMap:
|
case EntryMapStyle.openTopoMap:
|
||||||
return const OpenTopoMapLayer();
|
return const OpenTopoMapLayer();
|
||||||
case EntryMapStyle.osmHot:
|
case EntryMapStyle.osmHot:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:aves/model/device.dart';
|
import 'package:aves/model/device.dart';
|
||||||
|
import 'package:aves/widgets/common/map/leaflet/vector_style_reader_extra.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:vector_map_tiles/vector_map_tiles.dart';
|
import 'package:vector_map_tiles/vector_map_tiles.dart';
|
||||||
|
@ -45,39 +46,61 @@ class StamenWatercolorLayer extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OsmAmericanaLayer extends StatefulWidget {
|
class OsmLibertyLayer extends StatefulWidget {
|
||||||
const OsmAmericanaLayer({super.key});
|
const OsmLibertyLayer({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OsmAmericanaLayer> createState() => _OsmAmericanaLayerState();
|
State<OsmLibertyLayer> createState() => _OsmLibertyLayerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OsmAmericanaLayerState extends State<OsmAmericanaLayer> {
|
class _OsmLibertyLayerState extends State<OsmLibertyLayer> {
|
||||||
late final Future<Style> _styleFuture;
|
late final Future<Style> _americanaStyleFuture;
|
||||||
|
late final Future<Style> _osmLibertyStyleFuture;
|
||||||
|
|
||||||
|
static const _openMapTileProviderSource = 'openmaptiles';
|
||||||
|
|
||||||
|
// `Americana` provides tiles, but it uses layer syntax that is not supported by the vector tile renderer
|
||||||
|
static const _americanaStyle = 'https://americanamap.org/style.json';
|
||||||
|
|
||||||
|
// `OSM Liberty` is well supported by the vector tile renderer, but it requires an API key for the tiles
|
||||||
|
static const _osmLiberty = 'https://maputnik.github.io/osm-liberty/style.json';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_styleFuture = StyleReader(
|
|
||||||
uri: 'https://americanamap.org/style.json',
|
_americanaStyleFuture = StyleReader(
|
||||||
|
uri: _americanaStyle,
|
||||||
|
).readExtra(skippedSources: {});
|
||||||
|
|
||||||
|
_osmLibertyStyleFuture = StyleReader(
|
||||||
|
uri: _osmLiberty,
|
||||||
logger: const vtr.Logger.console(),
|
logger: const vtr.Logger.console(),
|
||||||
).read();
|
).readExtra(skippedSources: {_openMapTileProviderSource});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder<Style>(
|
return FutureBuilder<Style>(
|
||||||
future: _styleFuture,
|
future: _americanaStyleFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, americanaStyleSnapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
return FutureBuilder<Style>(
|
||||||
|
future: _osmLibertyStyleFuture,
|
||||||
|
builder: (context, osmLibertyStyleSnapshot) {
|
||||||
|
if (americanaStyleSnapshot.hasError) return Text(americanaStyleSnapshot.error.toString());
|
||||||
|
if (osmLibertyStyleSnapshot.hasError) return Text(osmLibertyStyleSnapshot.error.toString());
|
||||||
|
|
||||||
final style = snapshot.data;
|
final americanaStyle = americanaStyleSnapshot.data;
|
||||||
if (style == null) return const SizedBox();
|
final osmLibertyStyle = osmLibertyStyleSnapshot.data;
|
||||||
|
if (americanaStyle == null || osmLibertyStyle == null) return const SizedBox();
|
||||||
|
|
||||||
return VectorTileLayer(
|
return VectorTileLayer(
|
||||||
tileProviders: style.providers,
|
tileProviders: americanaStyle.providers,
|
||||||
theme: style.theme,
|
theme: osmLibertyStyle.theme,
|
||||||
sprites: style.sprites,
|
sprites: osmLibertyStyle.sprites,
|
||||||
|
layerMode: VectorTileLayerMode.raster,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
113
lib/widgets/common/map/leaflet/vector_style_reader_extra.dart
Normal file
113
lib/widgets/common/map/leaflet/vector_style_reader_extra.dart
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:vector_map_tiles/vector_map_tiles.dart';
|
||||||
|
import 'package:vector_tile_renderer/vector_tile_renderer.dart';
|
||||||
|
|
||||||
|
extension StyleReaderExtra on StyleReader {
|
||||||
|
Future<Style> readExtra({required Set<String> skippedSources}) async {
|
||||||
|
final styleText = await _httpGet(uri);
|
||||||
|
final style = await compute(jsonDecode, styleText);
|
||||||
|
if (style is! Map<String, dynamic>) {
|
||||||
|
throw _invalidStyle(uri);
|
||||||
|
}
|
||||||
|
final sources = style['sources'] as Map<String, dynamic>;
|
||||||
|
final providerByName = await _readProviderByName(Map.fromEntries(sources.entries.where((kv) => !skippedSources.contains(kv.key))));
|
||||||
|
final name = style['name'] as String?;
|
||||||
|
|
||||||
|
final center = style['center'];
|
||||||
|
LatLng? centerPoint;
|
||||||
|
if (center is List && center.length == 2) {
|
||||||
|
centerPoint = LatLng((center[1] as num).toDouble(), (center[0] as num).toDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
double? zoom = (style['zoom'] as num?)?.toDouble();
|
||||||
|
if (zoom != null && zoom < 2) {
|
||||||
|
zoom = null;
|
||||||
|
centerPoint = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final spriteUri = style['sprite'];
|
||||||
|
SpriteStyle? sprites;
|
||||||
|
if (spriteUri is String && spriteUri.trim().isNotEmpty) {
|
||||||
|
final spriteUris = [
|
||||||
|
_SpriteUri(json: '$spriteUri@2x.json?secure', image: '$spriteUri@2x.png?secure'),
|
||||||
|
_SpriteUri(json: '$spriteUri.json?secure', image: '$spriteUri.png?secure'),
|
||||||
|
];
|
||||||
|
for (final spriteUri in spriteUris) {
|
||||||
|
dynamic spritesJson;
|
||||||
|
try {
|
||||||
|
final spritesJsonText = await _httpGet(spriteUri.json);
|
||||||
|
spritesJson = await compute(jsonDecode, spritesJsonText);
|
||||||
|
} catch (e) {
|
||||||
|
logger.log(() => 'error reading sprite uri: ${spriteUri.json}');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sprites = SpriteStyle(atlasProvider: () => _loadBinary(spriteUri.image), index: SpriteIndexReader(logger: logger).read(spritesJson));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Style(theme: ThemeReader(logger: logger).read(style), providers: TileProviders(providerByName), sprites: sprites, name: name, center: centerPoint, zoom: zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, VectorTileProvider>> _readProviderByName(Map<String, dynamic> sources) async {
|
||||||
|
final providers = <String, VectorTileProvider>{};
|
||||||
|
final sourceEntries = sources.entries.toList();
|
||||||
|
for (final entry in sourceEntries) {
|
||||||
|
final type = TileProviderType.values.where((e) => e.name == entry.value['type']).firstOrNull;
|
||||||
|
if (type == null) continue;
|
||||||
|
dynamic source;
|
||||||
|
var entryUrl = entry.value['url'] as String?;
|
||||||
|
if (entryUrl != null) {
|
||||||
|
final sourceUrl = entryUrl;
|
||||||
|
source = await compute(jsonDecode, await _httpGet(sourceUrl));
|
||||||
|
if (source is! Map) {
|
||||||
|
throw _invalidStyle(sourceUrl);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
source = entry.value;
|
||||||
|
}
|
||||||
|
final entryTiles = source['tiles'];
|
||||||
|
final maxzoom = source['maxzoom'] as int? ?? 14;
|
||||||
|
final minzoom = source['minzoom'] as int? ?? 1;
|
||||||
|
if (entryTiles is List && entryTiles.isNotEmpty) {
|
||||||
|
final tileUri = entryTiles[0] as String;
|
||||||
|
final tileUrl = tileUri;
|
||||||
|
providers[entry.key] = NetworkVectorTileProvider(type: type, urlTemplate: tileUrl, maximumZoom: maxzoom, minimumZoom: minzoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (providers.isEmpty) {
|
||||||
|
throw 'Unexpected response';
|
||||||
|
}
|
||||||
|
return providers;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _invalidStyle(String url) => 'Uri does not appear to be a valid style: $url';
|
||||||
|
|
||||||
|
Future<String> _httpGet(String url) async {
|
||||||
|
final response = await get(Uri.parse(url));
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return response.body;
|
||||||
|
} else {
|
||||||
|
throw 'HTTP ${response.statusCode}: ${response.body}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> _loadBinary(String url) async {
|
||||||
|
final response = await get(Uri.parse(url));
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return response.bodyBytes;
|
||||||
|
} else {
|
||||||
|
throw 'HTTP ${response.statusCode}: ${response.body}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpriteUri {
|
||||||
|
final String json;
|
||||||
|
final String image;
|
||||||
|
|
||||||
|
const _SpriteUri({required this.json, required this.image});
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ enum EntryMapStyle {
|
||||||
googleHybrid,
|
googleHybrid,
|
||||||
googleTerrain,
|
googleTerrain,
|
||||||
// Vector (OpenMapTiles)
|
// Vector (OpenMapTiles)
|
||||||
osmAmericana,
|
osmLiberty,
|
||||||
// Raster (Leaflet)
|
// Raster (Leaflet)
|
||||||
openTopoMap,
|
openTopoMap,
|
||||||
osmHot,
|
osmHot,
|
||||||
|
|
11
pubspec.lock
11
pubspec.lock
|
@ -701,7 +701,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.4"
|
version: "0.15.4"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||||
|
@ -1741,11 +1741,12 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
vector_tile_renderer:
|
vector_tile_renderer:
|
||||||
dependency: "direct overridden"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "../dart-vector-tile-renderer"
|
name: vector_tile_renderer
|
||||||
relative: true
|
sha256: b58fa02b472ced87de00de2b4d5837576239bddb21b562481f25bde39bd117ae
|
||||||
source: path
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
version: "5.2.0"
|
version: "5.2.0"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|
|
@ -78,6 +78,7 @@ dependencies:
|
||||||
flutter_markdown:
|
flutter_markdown:
|
||||||
flutter_staggered_animations:
|
flutter_staggered_animations:
|
||||||
get_it:
|
get_it:
|
||||||
|
http:
|
||||||
intl:
|
intl:
|
||||||
latlong2:
|
latlong2:
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
|
@ -113,6 +114,7 @@ dependencies:
|
||||||
url_launcher:
|
url_launcher:
|
||||||
vector_map_tiles:
|
vector_map_tiles:
|
||||||
vector_math:
|
vector_math:
|
||||||
|
vector_tile_renderer:
|
||||||
volume_controller:
|
volume_controller:
|
||||||
xml:
|
xml:
|
||||||
|
|
||||||
|
@ -120,8 +122,6 @@ dependency_overrides:
|
||||||
# media_kit_video v1.2.4 depends on a specific old version of screen_brightness
|
# media_kit_video v1.2.4 depends on a specific old version of screen_brightness
|
||||||
media_kit_video: ^1.0.0
|
media_kit_video: ^1.0.0
|
||||||
screen_brightness: ^1.0.0
|
screen_brightness: ^1.0.0
|
||||||
vector_tile_renderer:
|
|
||||||
path: ../dart-vector-tile-renderer
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue