Map attribution & map layers plugin

This commit is contained in:
Vicente J. Ruiz Jurado 2018-08-04 09:14:51 +02:00
parent 726cd6b65d
commit 845c38bf04
9 changed files with 168 additions and 16 deletions

View file

@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_map/plugin_api.dart';
class AttributionPluginOptions extends LayerOptions {
final String text;
AttributionPluginOptions({this.text = ""});
}
class AttributionPlugin implements MapPlugin {
@override
Widget createLayer(LayerOptions options, MapState mapState) {
if (options is AttributionPluginOptions) {
var style = new TextStyle(
// fontWeight: FontWeight.bold,
fontSize: 12.0,
color: Colors.white,
);
return Padding(
padding: const EdgeInsets.all(5.0),
child: new Text(
options.text,
style: style,
),
);
}
throw ("Unknown options type for Attribution"
"plugin: $options");
}
@override
bool supportsLayer(LayerOptions options) {
return options is AttributionPluginOptions;
}
}

View file

@ -64,7 +64,7 @@ class S implements WidgetsLocalizations {
String get notPermsUbication => "We don't have permission to get your location";
String get notifyAFire => "Notify a fire";
String get privacyPolicy => "Privacy Policy";
String get supportPageDescription => "You can support this initiative, spreading the word, helping us to develop our software, translating this app to your language, etc.";
String get supportPageDescription => "You can support this initiative, spreading the word, helping us to develop our software, translating this app to your language, suggesting other public data sources of fires to use, etc.";
String get supportThisInitiative => "Support this initiative";
String get toDeleteThisNotification => "Slide horizontally to delete this notification";
String get toDeleteThisPlace => "Slide horizontally to delete this place";
@ -158,7 +158,7 @@ class es extends S {
@override
String get tweetAboutAFireTitle => "Twittea";
@override
String get supportPageDescription => "Puedes apoyar esta iniciativa, dando difusión, ayudándonos a desarrollar nuestro software, traduciendo esta aplicación a tu idioma, etc.";
String get supportPageDescription => "Puedes apoyar esta iniciativa, dando difusión, ayudándonos a desarrollar nuestro software, traduciendo esta aplicación a tu idioma, sugeriendo otras fuentes de datos públicas de fuegos para usar, etc.";
@override
String get aMinute => "un minuto";
@override

View file

@ -10,6 +10,7 @@ import 'package:flutter_redux/flutter_redux.dart';
import 'package:latlong/latlong.dart';
import 'package:share/share.dart';
import 'attributionMapPlugin.dart';
import 'colors.dart';
import 'dummyMapPlugin.dart';
import 'fireMarkType.dart';
@ -22,7 +23,7 @@ import 'models/fireMapState.dart';
import 'redux/actions.dart';
import 'slider.dart';
import 'zoomMapPlugin.dart';
import 'layerSelectorMapPlugin.dart';
@immutable
class _ViewModel {
final String serverUrl;
@ -118,18 +119,21 @@ class _genericMapState extends State<genericMap> {
assert(_location != null);
FireMapState mapState = view.mapState;
FireMapStatus status = mapState.status;
print('Build map with status: $status');
FireMapLayer layer = mapState.layer;
print('Build map with status: $status and layer $layer');
double maxZoom = 18.0; // works?
MapOptions mapOptions = new MapOptions(
center: new LatLng(_location.lat, _location.lon),
plugins: globals.isDevelopment
? [new ZoomMapPlugin()]
: [new DummyMapPlugin()],
? [new ZoomMapPlugin(), new AttributionPlugin(), new LayerSelectorMapPlugin()]
: [new DummyMapPlugin(), new AttributionPlugin(), new LayerSelectorMapPlugin()],
// this works ?
interactive: false,
zoom: 13.0,
// THIS does not works as expected
// maxZoom: 6.0,
maxZoom: maxZoom,
onTap: (callback) {
if (status == FireMapStatus.edit) {
_location = _location.copyWith(
@ -155,19 +159,49 @@ class _genericMapState extends State<genericMap> {
: status == FireMapStatus.subscriptionConfirm
? Icons.check
: Icons.notifications_off;
String baseLayer;
List<String> subdomains = [];
String attribution;
switch (layer) {
case FireMapLayer.osmc:
case FireMapLayer.osmcGrey:
attribution = '© OpenStreetMap contributors';
break;
case FireMapLayer.esri:
case FireMapLayer.esriSatellite:
case FireMapLayer.esriTerrain:
attribution = '© ESRI';
}
/* tiles from: https://github.com/dceejay/RedMap/blob/1914ed3b9ce4e8a496049849a93282730b4fff02/worldmap/index.html */
switch (layer) {
case FireMapLayer.osmc:
subdomains = ['a', 'b', 'c'];
baseLayer = 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';
break;
case FireMapLayer.osmcGrey:
subdomains = ['a', 'b', 'c'];
baseLayer = 'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png';
break;
case FireMapLayer.esri:
baseLayer = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}';
break;
case FireMapLayer.esriSatellite:
baseLayer = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}';
break;
case FireMapLayer.esriTerrain:
baseLayer = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}';
}
FlutterMap map = new FlutterMap(
options: mapOptions,
mapController: mapController,
layers: [
new TileLayerOptions(
maxZoom: 6.0,
subdomains: ['a', 'b', 'c'],
urlTemplate:
'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png',
maxZoom: maxZoom,
urlTemplate: baseLayer,
subdomains: subdomains,
additionalOptions: {
// 'opacity': '0.1',
'attribution':
'&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors'
},
),
globals.isDevelopment
@ -181,6 +215,9 @@ class _genericMapState extends State<genericMap> {
mapState.falsePos,
mapState.fireNotification != null),
),
// new AttributionPluginOptions(text: "© OpenStreetMap contributors"),
new LayerSelectorMapPluginOptions(),
new AttributionPluginOptions(text: attribution),
],
);
// mapController.

View file

@ -0,0 +1,57 @@
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_simple_dependency_injection/injector.dart';
import 'package:redux/redux.dart';
import 'models/appState.dart';
import 'redux/actions.dart';
class LayerSelectorMapPluginOptions extends LayerOptions {
final String text;
LayerSelectorMapPluginOptions({this.text = ""});
}
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/plugin_api.dart
class LayerSelectorMapPlugin extends MapPlugin {
Widget LayerSelectorButton(Store<AppState> store) {
return new FloatingActionButton(
backgroundColor: Colors.black26,
mini: true,
child: Icon(Icons.layers, /* size: 40.0, color: Colors.black45, */ ),
onPressed: () {
store.dispatch(new ToggleMapLayerAction());
});
}
@override
Widget createLayer(LayerOptions options, MapState mapState) {
Store<AppState> store = Injector.getInjector().get<Store<AppState>>();
if (options is LayerSelectorMapPluginOptions) {
return LayoutBuilder(
builder: (context, constraints) =>
Stack(fit: StackFit.expand, children: <Widget>[
Positioned(
top: constraints.maxHeight - 60,
left: 10.0,
child: new CenteredRow(
children: <Widget>[
new Column(
children: <Widget>[LayerSelectorButton(store)],
)
],
),
)
]));
}
throw ("Unknown options type for LayerSelectorMapPlugin"
"plugin: $options");
}
@override
bool supportsLayer(LayerOptions options) {
return options is LayerSelectorMapPluginOptions;
}
}

View file

@ -3,10 +3,12 @@ import 'package:fires_flutter/models/fireNotification.dart';
import 'package:meta/meta.dart';
enum FireMapStatus { view, subscriptionConfirm, unsubscribe, edit, viewFireNotification }
enum FireMapLayer { osmcGrey, esriSatellite, osmc, esri, esriTerrain }
@immutable
class FireMapState {
final FireMapStatus status;
final FireMapLayer layer;
final int numFires;
final FireNotification fireNotification;
final List<dynamic> fires;
@ -16,6 +18,7 @@ class FireMapState {
const FireMapState.initial()
: this.status = FireMapStatus.view,
this.layer = FireMapLayer.osmcGrey,
this.yourLocation = null,
this.fireNotification = null,
this.numFires = 0,
@ -25,6 +28,7 @@ class FireMapState {
FireMapState(
{this.status: FireMapStatus.view,
this.layer: FireMapLayer.osmcGrey,
this.yourLocation,
this.numFires,
this.fires,
@ -34,6 +38,7 @@ class FireMapState {
FireMapState copyWith({
FireMapStatus status,
FireMapLayer layer,
YourLocation yourLocation,
FireNotification fireNotication,
int numFires,
@ -48,6 +53,7 @@ class FireMapState {
fires: fires ?? this.fires,
falsePos: falsePos ?? this.falsePos,
industries: industries ?? this.industries,
layer: layer ?? this.layer,
status: status ?? this.status);
}
@ -57,6 +63,7 @@ class FireMapState {
other is FireMapState &&
runtimeType == other.runtimeType &&
status == other.status &&
layer == other.layer &&
numFires == other.numFires &&
fireNotification == other.fireNotification &&
fires == other.fires &&
@ -67,6 +74,7 @@ class FireMapState {
@override
int get hashCode =>
status.hashCode ^
layer.hashCode ^
numFires.hashCode ^
fireNotification.hashCode ^
fires.hashCode ^
@ -76,7 +84,7 @@ class FireMapState {
@override
String toString() {
return 'FireMapState{status: $status, numFires: $numFires, fires: ${fires
return 'FireMapState{status: $status, layer: $layer, numFires: $numFires, fires: ${fires
.length}, falsePos: ${falsePos.length}, industries: ${industries
.length}, yourLocation: $yourLocation, fireNotification: $fireNotification}';
}

View file

@ -53,3 +53,5 @@ class EditCancelYourLocationAction extends FiresMapActions {
YourLocation loc;
EditCancelYourLocationAction(this.loc);
}
class ToggleMapLayerAction extends FiresMapActions {}

View file

@ -20,6 +20,8 @@ final fireMapReducer = combineReducers<FireMapState>([
_editConfirmYourLocationMap),
new TypedReducer<FireMapState, EditCancelYourLocationAction>(
_editCancelYourLocationMap),
new TypedReducer<FireMapState, ToggleMapLayerAction>(
_toggleMapLayer),
new TypedReducer<FireMapState, UpdateYourLocationMapAction>(
_updateYourLocationMap)
]);
@ -89,3 +91,13 @@ FireMapState _editCancelYourLocationMap(
FireMapStatus restoreStatusAfterSave(loc) =>
loc.subscribed ? FireMapStatus.unsubscribe : FireMapStatus.view;
FireMapState _toggleMapLayer(
FireMapState state, ToggleMapLayerAction action) {
FireMapLayer currentLayer = state.layer;
List<FireMapLayer> list = FireMapLayer.values;
int currentPos = list.indexOf(currentLayer);
currentPos++;
FireMapLayer next = list.elementAt(currentPos >= list.length ? 0: currentPos);
return state.copyWith(layer: next);
}

View file

@ -54,7 +54,7 @@
"callEmergencyServicesDescription": "You should call 112 if you have not already done so to notify the emergency services.",
"tweetAboutAFireTitle": "Tweet about",
"tweetAboutAFireDescription": "Additionally if you use twitter you can share additional information with the emergency services, for example, attaching photos to the tweet if you have good visibility of the fire, when you took the photos, exact location, etc. Use #hashtags type #IFMinicipalTerminal for example #IFJumilla (Forest Fire in Jumilla).",
"supportPageDescription": "You can support this initiative, spreading the word, helping us to develop our software, translating this app to your language, etc.",
"supportPageDescription": "You can support this initiative, spreading the word, helping us to develop our software, translating this app to your language, suggesting other public data sources of fires to use, etc.",
"fireNotificationsTitleShort": "Notifications",
"fireNotificationsTitle": "Fire Notifications",
"fireNotificationTitle": "Fire Notification",

View file

@ -54,7 +54,7 @@
"callEmergencyServicesDescription": "Deberías llamar al 112 si no lo has hecho ya para avisar a los servicios de emergencia.",
"tweetAboutAFireTitle": "Twittea",
"tweetAboutAFireDescription": "Adicionalmente si usas twitter puedes compartir información adicional con los servicios de emergencia, por ejemplo, adjuntando fotos al tweet si tienes buena visibilidad del fuego, cuando tomaste las fotos, ubicación exacta, etc. Usa #hashtags tipo #IFTerminoMunicipal por ejemplo #IFJumilla (Incendio Forestal en Jumilla).",
"supportPageDescription": "Puedes apoyar esta iniciativa, dando difusión, ayudándonos a desarrollar nuestro software, traduciendo esta aplicación a tu idioma, etc.",
"supportPageDescription": "Puedes apoyar esta iniciativa, dando difusión, ayudándonos a desarrollar nuestro software, traduciendo esta aplicación a tu idioma, sugeriendo otras fuentes de datos públicas de fuegos para usar, etc.",
"fireNotificationsTitle": "Notificaciones de fuegos",
"fireNotificationTitle": "Notificación de fuego",
"fireNotificationsTitleShort": "Notificaciones",