Commit ef7bf511 authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬

Merge branch 'map-data-points' into 'main'

Added data points

See merge request !10
parents fca30031 17e13311
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"app_settings","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\app_settings-4.0.3\\\\","dependencies":[]},{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.17\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"app_settings","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\app_settings-4.0.3\\\\","dependencies":[]},{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.17\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_macos-0.1.0+4\\\\","dependencies":[]},{"name":"path_provider_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_macos-0.0.4+4\\\\","dependencies":[]},{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_linux-0.0.1+2\\\\","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_windows-0.0.4+1\\\\","dependencies":[]}],"web":[{"name":"connectivity_for_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_for_web-0.3.1+2\\\\","dependencies":[]},{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"app_settings","dependencies":[]},{"name":"connectivity","dependencies":["connectivity_macos","connectivity_for_web"]},{"name":"connectivity_for_web","dependencies":[]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-09-28 16:08:39.385573","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"app_settings","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\app_settings-4.0.3\\\\","dependencies":[]},{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.17\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"app_settings","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\app_settings-4.0.3\\\\","dependencies":[]},{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.17\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_macos-0.1.0+4\\\\","dependencies":[]},{"name":"path_provider_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_macos-0.0.4+4\\\\","dependencies":[]},{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_linux-0.0.1+2\\\\","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_windows-0.0.4+1\\\\","dependencies":[]}],"web":[{"name":"connectivity_for_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_for_web-0.3.1+2\\\\","dependencies":[]},{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"app_settings","dependencies":[]},{"name":"connectivity","dependencies":["connectivity_macos","connectivity_for_web"]},{"name":"connectivity_for_web","dependencies":[]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-09-29 01:24:32.010939","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
......@@ -20,7 +20,7 @@ class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'en';
final messages = _notInlinedMessages(_notInlinedMessages);
static _notInlinedMessages(_) => <String, Function> {
static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
"bluetoothAvailable" : MessageLookupByLibrary.simpleMessage("Available Devices"),
"bluetoothFrequent" : MessageLookupByLibrary.simpleMessage("Frequently Used Devices"),
"bluetoothInactive" : MessageLookupByLibrary.simpleMessage("Bluetooth is not active on this device.\nTurn it on ?"),
......
......@@ -9,8 +9,11 @@ import 'package:logair_application/logic/handlers/network_handler.dart';
import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/logic/handlers/position_handler.dart';
import 'package:logair_application/logic/handlers/preference_handler.dart';
import 'package:logair_application/utils/enums/pm_symbol.dart';
import 'package:logair_application/utils/enums/preference_keys.dart';
import 'package:rxdart/rxdart.dart';
import 'package:tuple/tuple.dart';
/// The [MapDisplayController] is a singleton that handles the mapping of information.
class MapDisplayController {
......@@ -33,7 +36,7 @@ class MapDisplayController {
MapController _controller;
/// [List] of [Polyline] representing the evolution of the PM2.5 value.
List<Polyline> _polylines = [];
final List<Polyline> _polylines = [];
/// [Marker] representing the user's current [Position].
Marker _marker;
......@@ -41,73 +44,36 @@ class MapDisplayController {
/// Null [BehaviorSubject] tracking when the [FlutterMap] should be redrawn.
BehaviorSubject shouldRedraw = BehaviorSubject();
/// [BehaviorSubject] that contains retrieved [DataPacket]s.
final BehaviorSubject<List<DataPacket>> _pmRetrievedPackets = BehaviorSubject<List<DataPacket>>();
/// List of markers created by the user.
final Map<PMSymbol, List<CircleMarker>> _pmMarkers = <PMSymbol, List<CircleMarker>>{
PMSymbol.PM1: [],
PMSymbol.PM2_5: [],
PMSymbol.PM4: [],
PMSymbol.PM10: []
};
/// List of markers created by other users.
final Map<PMSymbol, List<CircleMarker>> _pmDistantMarkers = <PMSymbol, List<CircleMarker>>{
PMSymbol.PM1: [],
PMSymbol.PM2_5: [],
PMSymbol.PM4: [],
PMSymbol.PM10: []
};
Future<void> _rebuildMarkers() async {
List<DataPacket> packets = await MainDatabaseHandler().getLastPackets(200);
packets.forEach((DataPacket packet) {
switch (_pmSymbol) {
case PMSymbol.PM1:
this.addToList(this._currentPos, packet.pm1, 200);
break;
case PMSymbol.PM2_5:
this.addToList(this._currentPos, packet.pm2_5, 200);
break;
case PMSymbol.PM4:
this.addToList(this._currentPos, packet.pm4, 200);
break;
case PMSymbol.PM10:
this.addToList(this._currentPos, packet.pm10, 200);
break;
default:
break;
}
});
@override
MapDisplayController.dispose() {
shouldRedraw.close();
}
List<CircleMarker> _pmMarkers = <CircleMarker>[];
/// Internal static [Singleton]() reference.
static final MapDisplayController _singleton = MapDisplayController._internal();
/// Singleton instantiation factory.
factory MapDisplayController() => _singleton;
final BehaviorSubject<Position> _positionObserver = PositionHandler().devicePosition;
/// @Getter for [_polylines].
List<Polyline> get polylines => this._polylines;
/// @Getter for [_marker].
Marker get marker {
if (this._marker == null) {
LatLng position = this._positionObserver.value == null ? LatLng(0, 0) : LatLng(this._positionObserver.value.latitude, this._positionObserver.value.longitude);
this._marker = _buildLocationMarker(position);
}
return this._marker;
}
/// Object used to calculate the real-life distance of two [Position]s
final Distance _distance = Distance();
List<CircleMarker> get pmMarkers => this._pmMarkers;
PMSymbol _pmSymbol = PMSymbol.PM2_5;
void setPMKeyIndex(PMSymbol value) {
print('PM Index $value gives ${value.key}');
this._pmSymbol = value;
this._rebuildMarkers();
}
void addToList(LatLng position, double value, int maxPoints) {
if (this._pmMarkers.length > maxPoints) {
this._pmMarkers.removeRange(0, this._pmMarkers.length - 200);
}
if (value != null) {
this._pmMarkers.add(_buildPMMarker(position, value));
}
}
/// This function is executed on first initialization of the [MapDisplayController]
MapDisplayController._internal() {
/// A new [MapController] is used as the global Controller.
......@@ -115,14 +81,16 @@ class MapDisplayController {
this._controller = MapController();
/// This function is hooked to the [PositionHandler]s progress, ie. when a new [Position] is acquired (once per second), it is broadcasted across the app and can be listened to.
// TODO Fix this to allow external data (bounded map data)
PositionHandler().getCurrentOrLastPosition().listen((posChanged) {
PositionHandler().getCurrentOrLastPosition().listen((posChanged) async {
if (posChanged == null) {
return;
}
// If the map should be redrawn at the end of the tick.
bool _shouldRedraw = false;
/// If a [BluetoothDevice] is connected, then the [DataHandler] will have a [DataPacket] available.
DataPacket latest;
DataPacket latest = await MainDatabaseHandler().getLatestData().first;
/// If the [DataPacket] is older than 5 seconds we invalidate it.
if (latest != null && latest.timestamp <= (DateTime.now().millisecondsSinceEpoch - 5000)) {
......@@ -143,65 +111,120 @@ class MapDisplayController {
/// Move the [Marker] to the current [Position].
this._marker = this._buildLocationMarker(this._currentPos);
if (this._currentPos != this._lastPos) {
this.shouldRedraw.value = null;
_shouldRedraw = true;
}
if ((this._lastPos != null && this._currentPos != null) && this._distance.as(LengthUnit.Meter, this._lastPos, this._currentPos) >= .5) {
// If the position has changed.
if ((this._lastPos != null && this._currentPos != null)){// && this._distance.as(LengthUnit.Meter, this._lastPos, this._currentPos) >= .5) {
if (this._polylines.isEmpty) {
this._polylines.add(
Polyline(
points: [this._lastPos, this._currentPos],
color: Colors.blue
color: Colors.blue,
strokeWidth: 2
)
);
_shouldRedraw = true;
} else {
this._polylines.last.points.add(this._currentPos);
_shouldRedraw = true;
}
if (latest != null) {
switch (_pmSymbol) {
case PMSymbol.PM1:
if (latest.pm1 != null) {
this.addToList(this._currentPos, latest.pm1, 200);
}
break;
case PMSymbol.PM2_5:
if (latest.pm2_5 != null) {
this.addToList(this._currentPos, latest.pm2_5, 200);
}
break;
case PMSymbol.PM4:
if (latest.pm4 != null) {
this.addToList(this._currentPos, latest.pm4, 200);
}
break;
case PMSymbol.PM10:
if (latest.pm10 != null) {
this.addToList(this._currentPos, latest.pm10, 200);
}
break;
default:
break;
}
this.addToList(this._currentPos, Tuple4(latest.pm1, latest.pm2_5, latest.pm4, latest.pm10), 200);
_shouldRedraw = true;
}
}
if (_shouldRedraw) {
this.shouldRedraw.value = null;
}
});
// Iterate over
Timer.periodic(Duration(minutes: 1), (Timer t) {
if (this._controller != null && this._controller.bounds != null) {
NetworkHandler().getProximityPoints(DataHeader().deviceId, this._controller.bounds);
NetworkHandler().getProximityPoints(
DataHeader().deviceId,
this._controller.bounds
).then(
(List<DataPacket> value) => _pmRetrievedPackets.value = value
);
}
});
// Retrieve packets and remove
_pmRetrievedPackets.listen((List<DataPacket> values) {
this._pmDistantMarkers[PMSymbol.PM1].removeWhere((element) => true);
this._pmDistantMarkers[PMSymbol.PM2_5].removeWhere((element) => true);
this._pmDistantMarkers[PMSymbol.PM4].removeWhere((element) => true);
this._pmDistantMarkers[PMSymbol.PM10].removeWhere((element) => true);
values.forEach((DataPacket packet) {
this._addToArray(markers: this._pmDistantMarkers, builder: _buildDistantPMMarker, symbol: PMSymbol.PM1, value: packet.pm1, latitude: packet.latitude, longitude: packet.longitude);
this._addToArray(markers: this._pmDistantMarkers, builder: _buildDistantPMMarker, symbol: PMSymbol.PM2_5, value: packet.pm2_5, latitude: packet.latitude, longitude: packet.longitude);
this._addToArray(markers: this._pmDistantMarkers, builder: _buildDistantPMMarker, symbol: PMSymbol.PM4, value: packet.pm4, latitude: packet.latitude, longitude: packet.longitude);
this._addToArray(markers: this._pmDistantMarkers, builder: _buildDistantPMMarker, symbol: PMSymbol.PM10, value: packet.pm10, latitude: packet.latitude, longitude: packet.longitude);
});
});
}
@override
MapDisplayController.dispose() {
shouldRedraw.close();
_positionObserver.close();
/// @Getter for [_polylines].
List<Polyline> get polylines => this._polylines;
/// @Getter for [_marker].
Marker get marker {
if (this._marker == null) {
LatLng position = PositionHandler().devicePosition.value == null ? LatLng(0, 0) : LatLng(PositionHandler().devicePosition.value.latitude, PositionHandler().devicePosition.value.longitude);
this._marker = _buildLocationMarker(position);
}
return this._marker;
}
/// Internal static [Singleton]() reference.
static final MapDisplayController _singleton = MapDisplayController._internal();
/// Object used to calculate the real-life distance of two [Position]s
final Distance _distance = Distance();
// @Getter for the distant pm markers.
List<CircleMarker> get distantMarkers => this._pmDistantMarkers[this._pmSymbol];
/// @Getter for the pm markers.
List<CircleMarker> get pmMarkers => this._pmMarkers[this._pmSymbol];
/// The current targeted [PMSymbol].
PMSymbol _pmSymbol = PMSymbol.PM2_5;
/// Setter the current targeted [PMSymbol].
void setPMKeyIndex(PMSymbol value) {
this._pmSymbol = value;
this.shouldRedraw.value = true;
}
/// Add specific location markers from [values] and [position] to [_pmMarkers].
void addToList(LatLng position, Tuple4<double, double, double, double> values, int maxPoints) {
if (this._pmMarkers[PMSymbol.PM1].length > maxPoints + 5) {
this._pmMarkers[PMSymbol.PM1].removeRange(0, this._pmMarkers[PMSymbol.PM1].length - maxPoints);
}
if (this._pmMarkers[PMSymbol.PM2_5].length > maxPoints + 5) {
this._pmMarkers[PMSymbol.PM2_5].removeRange(0, this._pmMarkers[PMSymbol.PM2_5].length - maxPoints);
}
if (this._pmMarkers[PMSymbol.PM4].length > maxPoints + 5) {
this._pmMarkers[PMSymbol.PM4].removeRange(0, this._pmMarkers[PMSymbol.PM4].length - maxPoints);
}
if (this._pmMarkers[PMSymbol.PM10].length > maxPoints + 5) {
this._pmMarkers[PMSymbol.PM10].removeRange(0, this._pmMarkers[PMSymbol.PM10].length - maxPoints);
}
this._addToArray(markers: this._pmMarkers, builder: _buildPMMarker, symbol: PMSymbol.PM1, value: values.item1, latitude: position.latitude, longitude: position.longitude);
this._addToArray(markers: this._pmMarkers, builder: _buildPMMarker, symbol: PMSymbol.PM2_5, value: values.item2, latitude: position.latitude, longitude: position.longitude);
this._addToArray(markers: this._pmMarkers, builder: _buildPMMarker, symbol: PMSymbol.PM4, value: values.item3, latitude: position.latitude, longitude: position.longitude);
this._addToArray(markers: this._pmMarkers, builder: _buildPMMarker, symbol: PMSymbol.PM10, value: values.item4, latitude: position.latitude, longitude: position.longitude);
}
/// Add any [DataPacket] like object to a specific [markers].
void _addToArray({@required Map<PMSymbol, List<CircleMarker>> markers, @required Function builder, @required PMSymbol symbol, @required double value, @required double latitude, @required double longitude}) {
markers[symbol].add((value != null && value != -1) ? builder(LatLng(latitude, longitude), value + 0.0) : builder(LatLng(0, 0), 0.0));
}
/// @Getter for [_currentPos]
LatLng currentPosition() => _currentPos ?? LatLng(0, 0);
......@@ -251,14 +274,19 @@ class MapDisplayController {
),
);
CircleMarker _buildPMMarker(LatLng position, double pmValue) {
/// Builds a PM Marker [CircleMarker] for the users data.
CircleMarker _buildPMMarker(LatLng position, double pmValue, {bool distant = false}) {
return CircleMarker(
radius: 8,
point: position,
color: this._pmSymbol.colorizeValue(pmValue).withAlpha(0xff),
borderStrokeWidth: 1,
borderStrokeWidth: (distant) ? 0 : 1,
borderColor: Colors.blue[300]
);
}
/// Builds a PM Marker [CircleMarker] for other users data.
CircleMarker _buildDistantPMMarker(LatLng position, double pmValue) {
return _buildPMMarker(position, pmValue, distant: true);
}
}
\ No newline at end of file
......@@ -28,6 +28,7 @@ class BTLEHandler {
static final BTLEHandler _singleton = BTLEHandler._internal();
/// The referenced [BluetoothCharacteristic].
///
/// Should be singular, unless we plan to broadcast over channels.
BluetoothCharacteristic _characteristic;
......
......@@ -65,7 +65,13 @@ class MainDatabaseHandler {
/// Inserts data into the [Database].
Future<void> insertData(DataPacket packet) async {
final Database database = await this._mainDatabase;
final bool share = await PreferencesHandler().getPreferencesBool(PreferenceKeys.GEN__SHARE.key);
Map<String, dynamic> insert = packet.toMap();
if (!share) {
insert['exported'] = 1;
}
await database.insert(
'data',
insert,
......
......@@ -183,8 +183,8 @@ class NetworkHandler {
}
/// Get all of the points in proximity to the device.
void getProximityPoints(String deviceId, LatLngBounds bounds) async {
if (!(await checkNetworkAllowed())) return;
Future<List<DataPacket>> getProximityPoints(String deviceId, LatLngBounds bounds) async {
if (!(await checkNetworkAllowed())) return [];
this._downloadCounter.value += 1;
......@@ -192,7 +192,7 @@ class NetworkHandler {
int limit = await PreferencesHandler().getPreferencesInt(PreferenceKeys.MAP__PROX__MAX_PER_QUERY.key);
/// The default endpoint.
String url = 'https://api.logair.unige.ch/v1/geo/bounds?device_id=$deviceId&lat1=${bounds.northEast.latitude}&lng1=${bounds.northEast.longitude}&lat2=${bounds.southWest.latitude}&lng2=${bounds.southWest.longitude}&age=${age * 1000}&limit=$limit';
String url = 'https://api.logair.unige.ch/v1/geo/bounds?device_id=1243&lat1=${bounds.southWest.latitude}&lng1=${bounds.southWest.longitude}&lat2=${bounds.northEast.latitude}&lng2=${bounds.northEast.longitude}&age=${age * 1000}&limit=$limit';
/// Attempt to send the data to the endpoint via HTTP POST request.
http.Response response;
......@@ -207,11 +207,24 @@ class NetworkHandler {
printDebug('NetworkHandler: ${e.toString()} COULDN\'T CONNECT');
response = null;
}
this._downloadCounter.value -= 1;
// Rebuild the packets from the retrieved data.
List<DataPacket> packets = [];
if (response != null && response.statusCode == 200) {
// TODO Complete
List<dynamic> responseBody = json.decode(response.body);
responseBody.forEach((packet) {
packets.add(DataPacket.fromData(
0,
packet['lat'], packet['lng'], 0, 0, 0,
0, 0, 0,
castDoubleWrapper(packet['pm_1']), castDoubleWrapper(packet['pm_2_5']), castDoubleWrapper(packet['pm_4']), castDoubleWrapper(packet['pm_10']),
''
));
});
}
return packets;
}
/// Get the time of the last server transmission.
......@@ -240,7 +253,7 @@ class NetworkHandler {
headers: { 'accept': 'application/json', 'content-type': 'application/json' },
);
this._downloadBytes.value += response.contentLength;
this._downloadBytes.value += response.contentLength;
/// On error, the error should be ignored and not break the thread.
} catch (e) {
printDebug('NetworkHandler: ${e.toString()} COULDN\'T CONNECT');
......@@ -304,6 +317,7 @@ class NetworkHandler {
}
}
/// Retrieve the current [ConnectivityResult].
Stream<ConnectivityResult> _connectivityType() async* {
ConnectivityResult previous = ConnectivityResult.none;
while (true) {
......@@ -316,6 +330,7 @@ class NetworkHandler {
}
}
/// Builds a representative [Icon] from a [ConnectivityResult].
StreamBuilder<ConnectivityResult> buildConnectivityTypeWidget({@required Color color}) =>
StreamBuilder<ConnectivityResult>(
stream: this._connectivityType(),
......
......@@ -153,10 +153,11 @@ class PositionHandler {
Stream<int> get timeSinceLastPositionAcquired async* {
while(true) {
yield this._timeSinceLastChange;
await Future.delayed(Duration(seconds: 1));
await Future.delayed(Duration(milliseconds: 250));
}
}
/// Get whether the position has been acquired recently.
Stream<bool> get positionAcquired async* {
while(true) {
yield this._timeSinceLastChange == 0;
......
......@@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:logair_application/logic/handlers/main_database_handler.dart';
import 'package:logair_application/ui/animation/forward_animation.dart';
import 'package:logair_application/ui/components/body/data/locations_of_interest_tiles.dart';
import 'package:logair_application/ui/components/body/loi_adder/loi_insert_form.dart';
import 'package:logair_application/ui/components/common/carousel_card.dart';
import 'package:logair_application/ui/components/body/data/graph/graph.dart';
import 'package:logair_application/ui/components/body/data/tiles/data_tile.dart';
......@@ -46,7 +48,12 @@ class DataWidget extends StatelessWidget {
),
IconButton(
// Open other (Scaffold + Form) with nominatim etc + adds to DB.
onPressed: () => {},
onPressed: () => Navigator.push(
context,
PopForwardRoute(
page: LOIInsertDialog(),
),
),
icon: Icon(
Icons.add,
size: Theme.of(context).iconTheme.size
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class LOIInsertDialog extends StatefulWidget {
LOIInsertDialog({Key key}) : super(key: key);
@override
_LOIInsertDialogState createState() => _LOIInsertDialogState(key: key);
}
/// Represents a state of the [LOIInsertDialog] [Widget].
class _LOIInsertDialogState extends State<LOIInsertDialog> {
_LOIInsertDialogState({Key key});
/// A key for the Scaffold state, so as to avoid reloading the widget unnecessarily.
final GlobalKey<ScaffoldState> _key = GlobalKey();
final _formKey = GlobalKey<FormState>(debugLabel: 'FORM_LOI');
final TextEditingController _loiNameController = TextEditingController();
final TextEditingController _loiLatController = TextEditingController();
final TextEditingController _loiLngController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
void dispose() {
_loiNameController.dispose();
_loiLatController.dispose();
_loiLngController.dispose();
super.dispose();
}
Widget _buildInputTile(String labelText, TextEditingController controller, RegExp validation) {
return ListTile(
title: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
labelText,
textAlign: TextAlign.left,
),
Row(
children: [
Expanded(
flex: 9,
child: TextFormField(
controller: controller,
//inputFormatters: inputFormatters,
style: TextStyle(fontSize: 18),
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 6),
),
autovalidate: false,
validator: (value) {
if (value.isEmpty) {
return 'The field cannot be empty';
} else if (validation.stringMatch(value) != value) {
return 'Invalid value';
}
return null;
}
),
),
],
)
],
)
)
);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _key,
appBar: AppBar(
title: Text(
'Add location of interest',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.subtitle2.copyWith(
color: Colors.white
)
),
),
body: Container(
height: double.maxFinite,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.all(0),
child: Container(
padding: EdgeInsets.all(0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildInputTile('Location Name', _loiNameController, RegExp(r'.*')),
Divider(indent: 20, endIndent: 20, color: Colors.grey[700],),
_buildInputTile('Latitude', _loiLatController, RegExp(r'\d+')),
Divider(indent: 20, endIndent: 20, color: Colors.grey[700],),
_buildInputTile('Longitude', _loiLngController, RegExp(r'\d+')),
],
),
)
),
),
),
)
);
}
}
\ No newline at end of file
......@@ -9,10 +9,10 @@ class MapWidget extends StatefulWidget {