Commit 0017d4c5 authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬

Added data tile

parent e73e727b
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"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.0\\\\","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":"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.0\\\\","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":"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":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"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":[]},{"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-07-21 17:28:31.765660","version":"1.19.0-2.0.pre.49"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"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.0\\\\","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":"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.0\\\\","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":"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":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"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":[]},{"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-07-24 15:47:03.988120","version":"1.20.0-3.0.pre.126"}
\ No newline at end of file
......@@ -9,6 +9,7 @@ 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/utils/pm_symbol.dart';
/// The [MapDisplayController] is a [Singleton] that handles the mapping of information.
class MapDisplayController {
......@@ -33,15 +34,12 @@ class MapDisplayController {
/// [Marker] representing the user's current [Position].
Marker _marker;
HashMap<String, List<CircleMarker>> _pmMarkers = HashMap.from({'PM1': <CircleMarker>[], 'PM2_5': <CircleMarker>[], 'PM4': <CircleMarker>[], 'PM10': <CircleMarker>[]});
/// [List] of [Color]s to be smudged as a gradient and cherry picked based on a value (lerped).
/// @see [_colorizeValue]
List<Color> _colors = [ Color.fromARGB(0xff, 0, 0xcc, 0), Color.fromARGB(0xff, 0, 0xcc, 0), Color.fromARGB(0xff, 0xff, 0xff, 0), Color.fromARGB(0xff, 0xeb, 0x8a, 0x14), Color.fromARGB(0xff, 0xff, 0, 0), Color.fromARGB(0xff, 0xa1, 0x06, 0x49), Color.fromARGB(0xff, 0x7e, 0, 0x23) ];
/// [List] of stops for the lerping to occur.
/// @see [_colorizeValue]
List<double> _stops = [0, 12, 35.4, 55.4, 150.4, 250.4, 500.4];
HashMap<PMSymbol, List<CircleMarker>> _pmMarkers = HashMap.from({
PMSymbol.PM1: <CircleMarker>[],
PMSymbol.PM2_5: <CircleMarker>[],
PMSymbol.PM4: <CircleMarker>[],
PMSymbol.PM10: <CircleMarker>[]}
);
/// [Singleton]() instantiation factory.
factory MapDisplayController() => _singleton;
......@@ -56,13 +54,13 @@ class MapDisplayController {
final Distance _distance = new Distance();
// TODO Faster to rebuild than to store ?
List<CircleMarker> pmMarkers() => this._pmMarkers[_pmKey[_pmKeyIndex]];
List<CircleMarker> pmMarkers() => this._pmMarkers[_pmKey];
List<String> _pmKey = ["PM1", "PM2_5", "PM4", "PM10"];
List<String> _pmKey = PMSymbol.list.map((e) => e.key);
int _pmKeyIndex = 1;
PMSymbol _pmKeyIndex = PMSymbol.PM2_5;
void setPMKeyIndex(int value) { print('PM Index $value gives ${_pmKey[value]}'); this._pmKeyIndex = value; }
void setPMKeyIndex(PMSymbol value) { print('PM Index $value gives ${value.key}'); this._pmKeyIndex = value; }
/// This function is executed on first initialization of the [MapDisplayController]
/// TODO: Throttle to 1 in 5 updates for efficiency ?
......@@ -165,21 +163,6 @@ class MapDisplayController {
}
}
/// Gets the lerped [Color] value at a for a given PM value.
Color _colorizeValue(double pmValue) {
for (int stop = 0; stop < _stops.length - 1; stop++) {
final double left = _stops[stop], right = _stops[stop + 1];
final Color colorLeft = _colors[stop], colorRight = _colors[stop + 1];
if (pmValue <= left)
return colorLeft;
else if (pmValue < right) {
final section = (pmValue - left) / (right - left);
return Color.lerp(colorLeft, colorRight, section);
}
}
return _colors.last;
}
/// Wrapper function to build a [Marker], with default values.
Marker _buildLocationMarker(LatLng position) => Marker(
width: 20.0,
......@@ -195,7 +178,7 @@ class MapDisplayController {
return CircleMarker(
radius: 8,
point: position,
color: _colorizeValue(pmValue).withAlpha(0xff),
color: this._pmKeyIndex.colorizeValue(pmValue).withAlpha(0xff),
borderStrokeWidth: 1,
borderColor: Colors.blue[300]
);
......
......@@ -3,15 +3,13 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/services/battery_service.dart';
import 'package:logair_application/ui/base/custom_paint.dart';
import 'dart:math';
class BatteryVisualizerWidget extends StatelessWidget {
BatteryVisualizerWidget(this.isLeft, {Key key}) : super(key: key);
Stream<int> getBatteryLevel2() async* {
Random r = new Random();
while (true) {
yield 100; //r.nextInt(100);
yield 100;
await Future.delayed(Duration(seconds: 10));
}
}
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/utils/pm_symbol.dart';
//TODO Add weather ?
/// Widget that displays the data specific to a specific place, depending on the stream provided.
/// Data displayed is PM1, PM2.5, PM4, PM10, Temperature, Pressure, Relative Humidity.
///
/// Usage example :
/// ```dart
/// DataTile(StreamHandler().someStream<DataPacket>())
/// ```
class DataTile extends StatefulWidget {
/// [Stream] of [DataPacket] that tracks the newly received packets.
final Stream<DataPacket> stream;
DataTile(this.stream, {Key key}) : super(key: key);
/// Provides [stream] to the [_DataTileState]
@override
State<DataTile> createState() => _DataTileState(this.stream);
}
/// [State] of a [DataTile] displaying [stream] specific data.
class _DataTileState extends State<DataTile> {
/// [Stream] of [DataPacket] that tracks the newly received packets.
final Stream<DataPacket> stream;
/// Value of the PM1 metric.
double _pm1 = -1;
/// Value of the PM2.5 metric.
double _pm2_5 = -1;
/// Value of the PM4 metric.
double _pm4 = -1;
/// Value of the PM10 metric.
double _pm10 = -1;
/// Value of the temperature metric.
double _temperature = -1;
/// Value of the pressure metric.
double _pressure = -1;
/// Value of the relative humidity metric.
double _relativeHumidity = -1;
/// Creates a [_DataTileState] that listens to [stream] and changes the state whenever a new [DataPacket] is available.
_DataTileState(this.stream, {Key key}) {
stream.listen((DataPacket newPacket) {
// Checks that the packet contains actual data.
if (newPacket != null)
setState(() {
_pm1 = newPacket.pm1();
_pm2_5 = newPacket.pm2_5();
_pm4 = newPacket.pm4();
_pm10 = newPacket.pm10();
_temperature = newPacket.temperature();
_pressure = newPacket.pressure();
_relativeHumidity = newPacket.relativeHumidity();
});
});
}
@override
Widget build(BuildContext context) {
// TODO Doc
Widget _buildPMDataCell(PMSymbol cellTitle, double value) => Expanded(
flex: 1,
child: Container(
color: (value != -1 && value != null) ? cellTitle.colorizeValue(value) : Colors.white,
margin: EdgeInsets.all(2),
padding: EdgeInsets.symmetric(vertical: 3),
child: Column(
children: [
Text(
cellTitle.key,
style: TextStyle(
fontSize: 12
),
),
Text(
(value != -1 && value != null) ? value.toString() : 'N/A',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold
),
)
]
)
)
);
// TODO Doc
Widget _buildEnvironmentalDataCellHeaders(String title) => Expanded(
flex: 1,
child: Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.symmetric(vertical: 3),
alignment: Alignment.center,
child: Text(
title,
style: TextStyle(
fontSize: 12,
),
),
)
);
// TODO Doc
Widget _buildEnvironmentalDataCells(double value) => Expanded(
flex: 1,
child: Container(
color: Colors.grey,
alignment: Alignment.center,
margin: EdgeInsets.all(2),
padding: EdgeInsets.symmetric(vertical: 3),
child: Text(
(value != -1 && value != null) ? value.toString() : 'N/A',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold
),
)
)
);
return Container(
margin: EdgeInsets.symmetric(horizontal: 3),
child: Column(
children: [
Text("Area Title"),
Row(
children: [
Expanded(
flex: 1,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildPMDataCell(PMSymbol.PM1, _pm1),
_buildPMDataCell(PMSymbol.PM2_5, _pm2_5),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildPMDataCell(PMSymbol.PM4, _pm4),
_buildPMDataCell(PMSymbol.PM10, _pm10),
],
),
],
),
),
Expanded(
flex: 2,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
_buildEnvironmentalDataCellHeaders('Temperature'),
_buildEnvironmentalDataCellHeaders('Pressure'),
_buildEnvironmentalDataCellHeaders('Relative\nHumidity'),
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
_buildEnvironmentalDataCells(_temperature),
_buildEnvironmentalDataCells(_pressure),
_buildEnvironmentalDataCells(_relativeHumidity),
],
),
]
),
)
],
)
],
),
);
}
}
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/ui/components/graph/graph_colors.dart';
import 'package:logair_application/ui/components/graph/graph_controller.dart';
import 'package:logair_application/ui/components/graph/indicator.dart';
/// WIP
/// TODO Fix display (statefulness ? Yes)
/// TODO Clear FL Spots
/// TODO Add axis titles
/// This graph should display the average quality of the air in 5 second increments.
class Graph extends StatefulWidget {
Graph({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() => _GraphState();
State<Graph> createState() => _GraphState();
}
class _GraphState extends State<Graph> {
_GraphState({Key key}) {
GraphController().getGraphData().listen((Map<String, dynamic> newData) {
if (this.mounted) {
......
import 'package:fl_chart/fl_chart.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/ui/data_widget.dart';
/// This Controller type class serves to remove the data aspect of the [LineChart] from Flutter's build tree,
/// therefore reducing memory usage to paint the graph when cycling back to the [DataWidget]..
/// Data is displayed as 4 lines on a (x: Time, y: PM value) graph, with 2 minutes (approx. 24 averaged points) on x and an auto-scaled y axis (0, max)
class GraphController {
/// [GraphController] factory
factory GraphController() => _singleton;
GraphController._internal() {
/// Every 5 seconds, averaged values are taken from the database and streamed.
/// This listener appends the data to the storage.
DatabaseHandler().getLatest().listen((newData) {
this.start = this.start ?? DateTime.now().millisecondsSinceEpoch * 1.0;
this.now = (DateTime.now().millisecondsSinceEpoch - this.start) / 1000;
this.max = [getMax(spotsPM1), getMax(spotsPM2_5), getMax(spotsPM4), getMax(spotsPM10)].reduce((curr, next) => curr > next? curr: next);
// Get max y value to avoid out of bounding on y-axis
this.max = [getMax(spotsPM1), getMax(spotsPM2_5), getMax(spotsPM4), getMax(spotsPM10)]
.reduce((curr, next) => curr > next ? curr : next);
// If actual data was acquired from the database.
if (newData['start'] != null) {
spotsPM1 = appendDataPoint(this.now, newData['pm_1'], spotsPM1);
spotsPM2_5 = appendDataPoint(this.now, newData['pm_2_5'], spotsPM2_5);
......@@ -19,33 +29,50 @@ class GraphController {
});
}
/// Instantiates the Singleton.
static final GraphController _singleton = new GraphController._internal();
/// List of [FlSpot] for PM1 values.
static List<FlSpot> spotsPM1 = [FlSpot(0, 0)];
/// List of [FlSpot] for PM2.5 values.
static List<FlSpot> spotsPM2_5 = [FlSpot(0, 0)];
/// List of [FlSpot] for PM4 values.
static List<FlSpot> spotsPM4 = [FlSpot(0, 0)];
/// List of [FlSpot] for PM10 values.
static List<FlSpot> spotsPM10 = [FlSpot(0, 0)];
/// Current maximum y-axis value.
double max;
/// Seconds elapsed since start of acquisition.
double now;
/// Timestamp of the first datapoint for this instance.
double start;
bool _displaying = false;
/// Add a data point to the given collection ([currentData]).
///
/// Returns the updated collection with the added [FlSpot] (x: [timestamp], y: [datapoint]) if [datapoint] is not null.
List<FlSpot> appendDataPoint(double timestamp, double datapoint, List<FlSpot> currentData) {
List<FlSpot> data = currentData.where((element) => true).toList();
if (datapoint != null)
if (datapoint != null) {
// Remove the [0, 0] point, to clean the graph's initial area, but only if an additional point can be added.
if (data.length == 1 && (data[0].x == 0 && data[0].y == 0))
data.removeAt(0);
data.add(FlSpot(timestamp, datapoint));
}
if (data.length > 50)
data.removeAt(0);
return data;
}
double getMax(List<FlSpot> data) => data.map((point) => point.y).reduce((curr, next) => curr > next? curr: next);
set displaying(bool isDisplaying) => this._displaying = isDisplaying;
bool get displaying => this._displaying;
/// Get the highest y-value in [data]
double getMax(List<FlSpot> data) => data.map((point) => point.y).reduce((curr, next) => curr > next ? curr : next);
/// References a [Stream] that yields the current graph data every second.
Stream<Map<String, dynamic>> getGraphData() async* {
while (true) {
yield {
......
......@@ -7,6 +7,7 @@ import 'package:logair_application/logic/handlers/data_handler.dart';
import 'package:logair_application/logic/handlers/position_handler.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/ui/carousel_card.dart';
import 'package:logair_application/ui/components/data_tile.dart';
import 'package:logair_application/ui/components/graph/graph.dart';
class DataWidget extends StatelessWidget {
......@@ -67,20 +68,16 @@ class DataWidget extends StatelessWidget {
children: [
// Local data (Chart + Data points)
Graph(),
DataTile(DataHandler().getDataStream()),
// Local Data
// Bar (Locations + Add new -> Pop to new route)
// Locations
Container(
padding: EdgeInsets.symmetric(horizontal: 3),
height: 300,
child: Column(
children: [
StreamBuilder<DataPacket>(
stream: DataHandler().getDataStream(),
initialData: null,
builder: (context, snapshot) {
DataPacket data = snapshot.data;
return _buildPacketDisplay(data);
}
),
Spacer( flex: 1, ),
Spacer(flex: 1),
StreamBuilder<Position>(
stream: PositionHandler().getCurrentOrLastPosition(),
builder: (context, snapshot) {
......@@ -90,6 +87,7 @@ class DataWidget extends StatelessWidget {
else return Text('Latitude N/A | Longitude N/A');
},
),
Spacer(flex: 1),
],
),
)
......
......@@ -6,6 +6,7 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:logair_application/logic/controllers/map_display_controller.dart';
import 'package:logair_application/ui/base_widget.dart';
import 'package:logair_application/ui/carousel_card.dart';
import 'package:logair_application/utils/pm_symbol.dart';
class MapContainer extends BaseWidget {
MapContainer();
......@@ -133,10 +134,10 @@ class MapContainer extends BaseWidget {
height: sizingInformation.unnotchedHeight() * 33/40 * 0.05,
child: Row(
children: [
_buildMapLayerSelection('PM 1', () => MapDisplayController().setPMKeyIndex(0)),
_buildMapLayerSelection('PM 2.5', () => MapDisplayController().setPMKeyIndex(1)),
_buildMapLayerSelection('PM 4', () => MapDisplayController().setPMKeyIndex(2)),
_buildMapLayerSelection('PM 10', () => MapDisplayController().setPMKeyIndex(3)),
_buildMapLayerSelection('PM 1', () => MapDisplayController().setPMKeyIndex(PMSymbol.PM1)),
_buildMapLayerSelection('PM 2.5', () => MapDisplayController().setPMKeyIndex(PMSymbol.PM2_5)),
_buildMapLayerSelection('PM 4', () => MapDisplayController().setPMKeyIndex(PMSymbol.PM4)),
_buildMapLayerSelection('PM 10', () => MapDisplayController().setPMKeyIndex(PMSymbol.PM10)),
],
),
)
......
......@@ -5,7 +5,6 @@ import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/ui/animation/forward_animation.dart';
import 'package:logair_application/ui/base/header.dart';
import 'package:logair_application/ui/routes/general_settings.dart';
import 'package:logair_application/utils/enums/bluetooth_connection_status.dart';
import 'package:logair_application/logic/handlers/bluetooth_le_handler.dart';
import 'package:logair_application/logic/handlers/bluetooth_wake_handler.dart';
import 'package:logair_application/logic/handlers/data_handler.dart';
......
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class PMSymbol {
final String _key;
final double _first;
final double _second;
final double _third;
final double _fourth;
final double _fifth;
final double _sixth;
final double _seventh;
const PMSymbol._internal(this._key, this._first, this._second, this._third, this._fourth, this._fifth, this._sixth, this._seventh);
toString() => 'Enum.$_key ~> [$_first, $_second, $_third, $_fourth, $_fifth, $_sixth, $_seventh]';
operator [](int i) {
switch (i) {
case 0: return this._first;
case 1: return this._second;
case 2: return this._third;
case 3: return this._fourth;
case 4: return this._fifth;
case 5: return this._sixth;
case 6: return this._seventh;
}
}
String get key => _key;
static List<PMSymbol> get list => [PM1, PM2_5, PM4, PM10];
/// [List] of stops for the lerping to occur.
/// @see [_colorizeValue]
static const PM1 = const PMSymbol._internal('PM 1', 0, 0, 0, 0, 0, 0, 0);
static const PM2_5 = const PMSymbol._internal('PM 2.5', 0, 12, 35.4, 55.4, 150.4, 250.4, 500.4);
static const PM4 = const PMSymbol._internal('PM 4', 0, 0, 0, 0, 0, 0, 0);
static const PM10 = const PMSymbol._internal('PM 10', 0, 54, 154, 254, 354, 424, 604);
/// [List] of [Color]s to be smudged as a gradient and cherry picked based on a value (lerped).
/// @see [_colorizeValue]
static List<Color> _colors = [
Color.fromARGB(0xff, 0x00, 0xcc, 0x00),
Color.fromARGB(0xff, 0x00, 0xcc, 0x00),
Color.fromARGB(0xff, 0xff, 0xff, 0x00),
Color.fromARGB(0xff, 0xeb, 0x8a, 0x14),
Color.fromARGB(0xff, 0xff, 0x00, 0x00),
Color.fromARGB(0xff, 0xa1, 0x06, 0x49),
Color.fromARGB(0xff, 0x7e, 0x00, 0x23)
];
/// Gets the lerped [Color] value at a for a given PM value.
Color colorizeValue(double pmValue) {
for (int stop = 0; stop < 6; stop++) {
final double left = this[stop], right = this[stop + 1];
final Color colorLeft = _colors[stop], colorRight = _colors[stop + 1];
if (pmValue <= left)
return colorLeft;
else if (pmValue < right) {
final section = (pmValue - left) / (right - left);
return Color.lerp(colorLeft, colorRight, section);
}
}
return _colors.last;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment