Skip to content
Snippets Groups Projects
Commit 52d3d018 authored by Hoa Ben The Nguyen's avatar Hoa Ben The Nguyen
Browse files

Merge branch 'main' of gitlab.stud.idi.ntnu.no:sarasdj/prog2900

parents 5c1548f8 56980714
No related branches found
No related tags found
No related merge requests found
Showing
with 161780 additions and 178 deletions
# PROG2900 # PROG2900
## Database setup ## Dependencies
This project requires the use of MongoDB. To use the application, a MongoDB Atlas database must first be set up and configured. The following is a step by step guide of how to set up the database: ### Python
1. Create a user/Log into MongoDB Atlas (https://www.mongodb.com/atlas) To run the server...
2. ?Create an organization?
3. On the left-hand side in the menu under "Organization", press the "Project" tab.
![Alt text](images/image-1.png) ### Dart & Flutter
4. In the right-hand corner, press "New Project" and name it as you wish.
![Alt text](images/image-2.png) ### Database
This project requires SQLite3. Download the precompiled binary for your operating system.
Precompiled binaries can be found on https://www.sqlite.org/download.html. Extract the downloaded
binary in a folder and note its path. Add the path to your system environment variables. Now you can
manage the SQLite database.
5. After successful creation, you should be taken to a page called "Overview". In the middle of the page right below "Create a deployment", press the button that says "+ Create" ## Endpoints
![Alt text](images/image.png) ## Bugs
6. When creating a database deployment, select the M10 template. The provider and region can be selected as is most fit to the organization. ## Developers
![Alt text](images/image-4.png)
7. Name your cluster appropriately. Optionally, give your cluster tags. Press "Create".
![Alt text](images/image-5.png)
8. After deploying the database you will be taken to the Security Quickstart page. Choose the option "certificate", and chose an appropriate "Common Name". Toggle the "Download certificate when user is added" on, and select a certificate expiration. Then press the "Add User" button.
![Alt text](images/image-7.png)
9. After pressing "Add User", the access certificate will automatically be downloaded to your PC. The certificate will be used to access your database, and should be kept in a safe location to prevent unauthorized access.
![Alt text](images/image-8.png)
10. Still in the Security Quickstart page, scroll down to the "Where would you like to connect from?" section. Press "Add My Current IP Address", and then press "Finish and close" at the bottom of the page.
![Alt text](images/image-9.png)
![Alt text](images/image-10.png)
11. You have now created a database deployment, but you will still have to configure the connection to the database.
### Database connection
1. To create a connection with the database, locate your newly created database deployment and press "Connect"
![alt text](images/image-11.png)
2. Select "Drivers"
![alt text](images/image-12.png)
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -11,7 +11,7 @@ class MyApp extends StatelessWidget { ...@@ -11,7 +11,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const MaterialApp( return const MaterialApp(
home: const DefaultPage(), home: DefaultPage(),
); );
} }
} }
...@@ -23,12 +23,13 @@ final titleStyle = GoogleFonts.dmSans( ...@@ -23,12 +23,13 @@ final titleStyle = GoogleFonts.dmSans(
color: textColor, color: textColor,
fontWeight: FontWeight.bold, // Add this line to make the text bold fontWeight: FontWeight.bold, // Add this line to make the text bold
); );
final regTextStyle = GoogleFonts.dmSans(fontSize: 20, color: textColor); final regTextStyle = GoogleFonts.dmSans(fontSize: 19, color: textColor);
final chartTextStyle = GoogleFonts.dmSans(fontSize: 14, color: textColor); final chartTextStyle = GoogleFonts.dmSans(fontSize: 14, color: textColor);
final subHeadingStyle = GoogleFonts.dmSans(fontSize: 22, color: textColor, fontWeight: FontWeight.bold);
// Colors // Colors
const mediumBlue = Color(0xFF015E8F); const darkBlue = Color(0xFF015E8F);
const darkBlue = Color(0xFF00B4D8); const teal = Color(0xFF00B4D8);
const darkestBlue = Color(0xFF03045E); const darkestBlue = Color(0xFF03045E);
const lightBlue = Color(0xFFCAF0F8); const lightBlue = Color(0xFFCAF0F8);
const superLightBlue = Color(0xFFCAF0F8); const superLightBlue = Color(0xFFCAF0F8);
......
...@@ -4,6 +4,8 @@ import 'widgets/map_widget.dart'; ...@@ -4,6 +4,8 @@ import 'widgets/map_widget.dart';
import 'marker_handler/marker_data.dart'; import 'marker_handler/marker_data.dart';
import 'consts.dart'; import 'consts.dart';
import 'marker_handler/get_markers.dart'; import 'marker_handler/get_markers.dart';
import 'marker_handler/get_relation.dart';
import 'dart:typed_data';
class DefaultPage extends StatefulWidget { class DefaultPage extends StatefulWidget {
const DefaultPage({super.key}); const DefaultPage({super.key});
...@@ -14,30 +16,34 @@ class DefaultPage extends StatefulWidget { ...@@ -14,30 +16,34 @@ class DefaultPage extends StatefulWidget {
class _DefaultPageState extends State<DefaultPage> { class _DefaultPageState extends State<DefaultPage> {
late Timer _timer; late Timer _timer;
bool showBar = false;
List<Measurement> markerList = []; List<Measurement> markerList = [];
Uint8List relation = Uint8List(0);
// Call fetchMarkerTemplate and await its result before setting the state // Call fetchMarkerTemplate and await its result before setting the state
Future<void> loadMarkerList() async { Future<void> loadMarkerList() async {
try { try {
List<Measurement> fetchedMarkers = await fetchMarkerData(); List<Measurement> fetchedMarkers = await fetchMarkerData();
Uint8List fetchedRelation = await fetchRelation();
setState(() { setState(() { // Initialise markers and relations
markerList = fetchedMarkers; markerList = fetchedMarkers;
relation = fetchedRelation;
}); });
} catch (e) { } catch (e) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text("Error"), title: const Text("Error"),
content: Text(e.toString()), content: Text(e.toString()),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text("OK"), child: const Text("OK"),
), ),
], ],
); );
...@@ -83,10 +89,18 @@ class _DefaultPageState extends State<DefaultPage> { ...@@ -83,10 +89,18 @@ class _DefaultPageState extends State<DefaultPage> {
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: AppBar( appBar: AppBar(
title: const Text('IceMap'), title: const Text('IceMap'),
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
showBar = !showBar;
},
),
],
), ),
body: ListView( body: ListView(
children: [ // Add main widget children: [ // Add main widget
MapContainerWidget(markerList: markerList), MapContainerWidget(markerList: markerList, relation: relation),
], ],
), ),
), ),
......
...@@ -7,25 +7,37 @@ import 'marker_data.dart'; ...@@ -7,25 +7,37 @@ import 'marker_data.dart';
// fetchMarkerTemplate requests all marker data from the server // fetchMarkerTemplate requests all marker data from the server
Future<List<Measurement>> fetchMarkerData() async { Future<List<Measurement>> fetchMarkerData() async {
try { try {
// Custom HTTP client
HttpClient client = HttpClient() HttpClient client = HttpClient()
..badCertificateCallback = (X509Certificate cert, String host, int port) => true; ..badCertificateCallback = // NB: temporary disable SSL certificate validation
(X509Certificate cert, String host, int port) => true;
// Request markers from server
var request = await client.getUrl(Uri.parse(serverURI + mapEndpoint)); var request = await client.getUrl(Uri.parse(serverURI + mapEndpoint));
var response = await request.close(); var response = await request.close(); // Close response body at end of function
// Parse body to JSON if request is ok
if (response.statusCode == 200) { if (response.statusCode == 200) {
var responseBody = await response.transform(utf8.decoder).join(); var responseBody = await response.transform(utf8.decoder).join();
List<dynamic> jsonData = json.decode(responseBody); if (responseBody.isNotEmpty) {
var jsonData = json.decode(responseBody);
return jsonData.map((data) => Measurement.Measurement(data)).toList();
// Attempt to parse response to Measurement object only if the body
// contains correctly formatted data
if (jsonData != null && jsonData is List) {
print(jsonData.map((data) => Measurement.fromJson(data)).toList());
return jsonData.map((data) => Measurement.fromJson(data)).toList();
} else {
throw Exception('Failed to parse marker data: Unexpected response format');
}
} else {
throw Exception('Failed to parse marker data: Empty response body');
}
} else { } else {
print('Request failed with status: ${response.statusCode}'); throw Exception('Failed to fetch marker data: Status code ${response.statusCode}');
throw Exception('Failed to parse marker data');
} }
} catch (e) { } catch (e) {
print('Error: $e'); throw Exception('Failed to fetch marker data: ${e.toString()}');
throw Exception('failed to connect to the server. Please check your network connection');
} }
} }
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import '../consts.dart';
import 'dart:typed_data';
/// Fetch relation data from server
Future<Uint8List> fetchRelation() async {
try {
// Custom HTTP client
HttpClient client = HttpClient()
..badCertificateCallback = // NB: temporary disable SSL certificate validation
(X509Certificate cert, String host, int port) => true;
// Execute request to to get_relation endpoint
var request = await client.getUrl(Uri.parse('${serverURI}get_relation'));
var response = await request.close(); // Close response body at end of function
// Parse body to JSON if request is ok
if (response.statusCode == 200) {
var responseBody = await response.transform(utf8.decoder).join();
if (responseBody.isNotEmpty) {
return Uint8List.fromList(utf8.encode(responseBody));
} else {
throw Exception('Response body is empty');
}
} else {
throw Exception('Failed to fetch relation data: Status code ${response.statusCode}');
}
} catch (e) {
throw Exception('Failed to fetch relation data: ${e.toString()}');
}
}
import 'dart:core';
import 'package:latlong2/latlong.dart';
class Measurement { class Measurement {
int measurementID; int measurementID;
int timeMeasured; DateTime timeMeasured;
Sensor sensor; Sensor sensor;
List<Data> dataList;
List<Corner> cornerList;
String bodyOfWater; String bodyOfWater;
LatLng center;
List <SubDiv> subDiv;
List <LatLng> corners;
Measurement({ Measurement({
required this.measurementID, required this.measurementID,
required this.timeMeasured, required this.timeMeasured,
required this.sensor, required this.sensor,
required this.dataList,
required this.cornerList,
required this.bodyOfWater, required this.bodyOfWater,
required this.center,
required this.subDiv,
required this.corners
}); });
factory Measurement.Measurement(Map<String, dynamic> json) { factory Measurement.fromJson(Map<String, dynamic> json) {
return Measurement( return Measurement(
measurementID: json['MeasurementID'], measurementID: json['MeasurementID'],
timeMeasured: json['TimeMeasured'], timeMeasured: DateTime.parse(json['TimeMeasured']),
sensor: Sensor.fromJson(json['Sensor']), sensor: Sensor.fromJson(json['Sensor']),
dataList: (json['Data'] as List<dynamic>) bodyOfWater: json['BodyOfWater'] ?? 'nil',
.map((data) => Data.fromJson(data)) center: LatLng(json['CenterLat'], json['CenterLon']),
.toList(), subDiv: (json['Subdivisions'] as List<dynamic>).map((data) => SubDiv.fromJson(data)).toList(),
cornerList: (json['Corner'] as List<dynamic>) corners: (json['Corners'] as List<dynamic>).map((corner) => LatLng(corner[0], corner[1])).toList(),
.map((data) => Corner.fromJson(data)) );
.toList(), }
bodyOfWater: json['WaterBodyName'], }
class SubDiv {
int subDivID;
int groupID;
double minThickness;
double avgThickness;
LatLng center;
double accuracy;
SubDiv({
required this.subDivID,
required this.groupID,
required this.minThickness,
required this.avgThickness,
required this.center,
required this.accuracy,
});
factory SubDiv.fromJson(Map<String, dynamic> json) {
return SubDiv(
subDivID: json['SubdivID'],
groupID: json['GroupID'],
minThickness: json['MinThickness'],
avgThickness: json['AvgThickness'],
center: LatLng(json['CenLatitude'], json['CenLongitude']),
accuracy: json['Accuracy'],
); );
} }
} }
...@@ -45,57 +76,8 @@ class Sensor { ...@@ -45,57 +76,8 @@ class Sensor {
factory Sensor.fromJson(Map<String, dynamic> json) { factory Sensor.fromJson(Map<String, dynamic> json) {
return Sensor( return Sensor(
sensorID: json['SensorID'], sensorID: json['SensorID'],
sensorType: json['SensorType'], sensorType: json['SensorType'] ?? 'nil',
active: json['Active'], active: json['Active'],
); );
} }
} }
class Data {
double latitude;
double longitude;
double iceTop;
double iceBottom;
double calculatedThickness;
double accuracy;
Data({
required this.latitude,
required this.longitude,
required this.iceTop,
required this.iceBottom,
required this.calculatedThickness,
required this.accuracy,
});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
latitude: json['Latitude'],
longitude: json['Longitude'],
iceTop: json['IceTop'],
iceBottom: json['IceBottom'],
calculatedThickness: json['CalculatedThickness'],
accuracy: json['Accuracy'],
);
}
}
class Corner {
double cornerID;
double latitude;
double longitude;
Corner({
required this.cornerID,
required this.latitude,
required this.longitude,
});
factory Corner.fromJson(Map<String, dynamic> json) {
return Corner(
cornerID: json['CornerID'],
latitude: json['CornerLatitude'],
longitude: json['CornerLongitude'],
);
}
}
\ No newline at end of file
import 'dart:typed_data';
import 'package:app/pages/consts.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_maps/maps.dart';
import '../consts.dart';
import 'dart:typed_data';
/// A class containing thickness for each subdivision of the map.
class IceThicknessModel {
IceThicknessModel(this.subDivID, this.thickness);
final int subDivID;
final double thickness;
}
/// A stateful widget which contains a choropleth map.
/// The map data is fetched from the server, and the map is rendered
/// using the Syncfusion Flutter Maps library.
class ChoroplethMap extends StatefulWidget {
const ChoroplethMap({Key? key, required this.relation}) : super(key: key);
final Uint8List relation;
@override
_ChoroplethMapState createState() => _ChoroplethMapState();
}
class _ChoroplethMapState extends State<ChoroplethMap> {
late MapShapeSource mapShapeSource;
List<IceThicknessModel> iceThicknessList = <IceThicknessModel>[];
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return SfMaps(
layers: [
MapShapeLayer(
source: MapShapeSource.memory(
widget.relation,
shapeDataField: 'name',
),
color: Colors.orange,
),
],
);
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../marker_handler/marker_data.dart'; import '../marker_handler/marker_data.dart';
import '../consts.dart'; import '../consts.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:fl_chart/fl_chart.dart';
import 'quick_view_chart.dart'; import 'quick_view_chart.dart';
import 'stat_charts.dart'; import 'stat_charts.dart';
import 'sat_layer.dart';
import 'package:flutter_map/flutter_map.dart';
import 'cloropleth_map.dart';
import 'dart:typed_data';
/// MapContainerWidget is the main widget that contains the map with all
/// its layers, polygons and markers.
class MapContainerWidget extends StatefulWidget { class MapContainerWidget extends StatefulWidget {
final List<Measurement> markerList; final List<Measurement> markerList;
final Uint8List relation;
const MapContainerWidget({Key? key, required this.markerList}) : super(key: key); const MapContainerWidget({Key? key,
required this.markerList,
required this.relation,
}) : super(key: key);
@override @override
_MapContainerWidgetState createState() => _MapContainerWidgetState(); _MapContainerWidgetState createState() => _MapContainerWidgetState();
...@@ -18,11 +25,22 @@ class MapContainerWidget extends StatefulWidget { ...@@ -18,11 +25,22 @@ class MapContainerWidget extends StatefulWidget {
class _MapContainerWidgetState extends State<MapContainerWidget> { class _MapContainerWidgetState extends State<MapContainerWidget> {
Measurement? selectedMarker; Measurement? selectedMarker; // Containing data for selected marker
bool isMinimized = true; // Quick view box state tacker bool isMinimized = true; // Quick view box state tacker
bool satLayer = false; // Satellite layer visibility tracker
bool isTapped = false; // Button tap state tracker
final MapController _mapController = MapController(); // Map controller to re-center map view
// recenterMap moves the map back to its initial view
void recenterMap() {
_mapController.move(mapCenter, 9.0);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Initialise selectedMarker to first element in markerList
selectedMarker ??= widget.markerList[0];
const double contPadding = 30; // Container padding space const double contPadding = 30; // Container padding space
...@@ -34,42 +52,74 @@ class _MapContainerWidgetState extends State<MapContainerWidget> { ...@@ -34,42 +52,74 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
return Column( return Column(
children: [ children: [
const SizedBox(height: contPadding), const SizedBox(height: contPadding),
/*if (true) NB: add search bar
const SearchBar(),
const SizedBox(height: contPadding),*/
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
child: Stack( // Stack quick view on top of map child: Stack( // Stack of quick view, map layer, satellite layer, and buttons
children: [ children: [
SizedBox( /*SizedBox(
width: screenWidth * boxWidth, width: screenWidth * boxWidth,
height: screenWidth * boxHeight, height: screenWidth * boxHeight,
child: FlutterMap( child: Stack(
options: MapOptions(
center: mapCenter, // From consts
zoom: 9.0,
),
children: [ children: [
TileLayer( // Map from OpenStreetMap SatLayer(markerList: widget.markerList), // Satellite layer
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", Visibility(
subdomains: const ['a', 'b', 'c'], visible: satLayer, // Only show layer if satellite button is toggled on
child: FlutterMap(
options: MapOptions(
center: mapCenter,
zoom: 9.0,
),
mapController: _mapController,
children: [
TileLayer(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: const ['a', 'b', 'c'],
),
MarkerLayer(
markers: widget.markerList.map((Measurement measurement) {
return Marker(
width: 50,
height: 50,
point: measurement.center, // Set markers at center of measurement
builder: (ctx) => GestureDetector(
onTap: () {
setState(() {
selectedMarker = measurement;
});
},
child: Icon(
Icons.severe_cold,
color: measurement == selectedMarker ? Colors.green : Colors.blue,
size: measurement == selectedMarker ? 40.0 : 30.0,
),
),
);
}).toList(),
),
],
),
), ),
PolygonLayer( // Map each element in markerList to Measurement object
polygons: widget.markerList.map((Measurement measurement) {
return Polygon(
points: measurement.cornerList.map((Corner corner) {
// Map corners to LatLng objects
return LatLng(corner.latitude, corner.longitude);
}).toList(),
/*onTap: () {
setState(() {
selectedMarker = measurement;
});
},*/
color: Colors.blue,
isFilled: true,
);
}).toList(),
)
], ],
), ),
),*/
SizedBox( // Colored box behind map
width: screenWidth * boxWidth,
height: screenWidth * boxHeight,
child: Container(
color: const Color(0x883366ff)
),
),
SizedBox( // Lake map
width: screenWidth * boxWidth,
height: screenWidth * boxHeight,
child: Padding(
padding: const EdgeInsets.all(15.0), // Padding around map
child: ChoroplethMap(relation: widget.relation),
),
), ),
Positioned( // Quick view box layered over map Positioned( // Quick view box layered over map
bottom: 10, bottom: 10,
...@@ -82,7 +132,7 @@ class _MapContainerWidgetState extends State<MapContainerWidget> { ...@@ -82,7 +132,7 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
color: Colors.blue.withOpacity(0.7), color: Colors.blue.withOpacity(0.7),
child: Stack( child: Stack(
children: [ children: [
Visibility( // Content only visible when box is maximized Visibility( // Graph only visible when box is maximized and a marker is selected
visible: !isMinimized && selectedMarker != null, visible: !isMinimized && selectedMarker != null,
child: Center( child: Center(
child: Padding( child: Padding(
...@@ -90,7 +140,7 @@ class _MapContainerWidgetState extends State<MapContainerWidget> { ...@@ -90,7 +140,7 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
child: SizedBox( child: SizedBox(
width: (screenWidth * boxWidth) / 2.3, width: (screenWidth * boxWidth) / 2.3,
height: (screenWidth * boxWidth) / 2.3, height: (screenWidth * boxWidth) / 2.3,
child: const QuickViewChart(), child: const QuickViewChart(), // Quick view graph
), ),
), ),
), ),
...@@ -112,13 +162,62 @@ class _MapContainerWidgetState extends State<MapContainerWidget> { ...@@ -112,13 +162,62 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
), ),
), ),
), ),
Positioned( // Satellite button
top: 10,
right: 10,
child: GestureDetector(
onTap: () {
setState(() {
satLayer = !satLayer; // Toggle satellite layer state on press
});
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: satLayer ? const BoxDecoration( // Add decoration only when pressed
shape: BoxShape.circle,
color: Colors.blue,
) : null,
child: const Icon(Icons.satellite_alt_outlined),
),
),
),
Positioned( // Back to center button
top: 45,
right: 10,
child: GestureDetector(
onTapDown: (_) {
setState(() {
recenterMap(); // Reset map view
isTapped = true;
});
},
onTapUp: (_) {
setState(() {
isTapped = false;
});
},
onTapCancel: () {
setState(() {
isTapped = false;
});
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: isTapped ? const BoxDecoration( // Add decoration only when pressed
shape: BoxShape.circle,
color: Colors.blue,
) : null,
child: const Icon(Icons.settings_backup_restore),
),
),
),
], ],
), ),
), ),
const SizedBox(height: contPadding), // Padding between containers const SizedBox(height: contPadding), // Padding between containers
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
child: Container( child: SizedBox(
width: screenWidth * boxWidth, width: screenWidth * boxWidth,
height: screenWidth * boxHeight * 1.5, // NB: make dynamic height: screenWidth * boxHeight * 1.5, // NB: make dynamic
child: Align( child: Align(
...@@ -132,12 +231,22 @@ class _MapContainerWidgetState extends State<MapContainerWidget> { ...@@ -132,12 +231,22 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
'Ice stats', 'Ice stats',
style: titleStyle, style: titleStyle,
), ),
const Divider(),
Text(
'Time of measurement',
style: subHeadingStyle,
),
Text( Text(
'Time of measurement: ${selectedMarker?.timeMeasured}', 'Date ${(selectedMarker?.timeMeasured.day ?? '-')}/${(selectedMarker?.timeMeasured.month ?? '-')}/${(selectedMarker?.timeMeasured.year ?? '-')}',
style: regTextStyle, style: regTextStyle,
), ),
Text( Text(
'Location: (placeholder, placeholder)', 'Time: ${selectedMarker?.timeMeasured.hour}:00',
style: regTextStyle,
),
const SizedBox(height: contPadding),
Text(
'Measuring point: (${selectedMarker?.measurementID}, ${selectedMarker?.measurementID})',
style: regTextStyle, style: regTextStyle,
), ),
const SizedBox(height: contPadding), const SizedBox(height: contPadding),
......
import 'package:flutter/material.dart';
import '../marker_handler/marker_data.dart';
import '../consts.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
class OSMmap extends StatelessWidget {
final List<Measurement> markerList;
const OSMmap({
Key? key,
required this.markerList,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// Init list of polygons
List<Polygon> polygons = [];
// Map each element from markerList to a measurement object
return FlutterMap(
options: MapOptions(
center: mapCenter,
zoom: 9.0,
),
children: [
TileLayer(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: const ['a', 'b', 'c'],
),
PolygonLayer(
polygons: polygons, // Return map with list of polygons included
),
],
);
}
}
import 'package:flutter/material.dart';
import '../marker_handler/marker_data.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
class SatLayer extends StatelessWidget {
final List<Measurement> markerList;
const SatLayer({
Key? key,
required this.markerList,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return FlutterMap(
options: MapOptions(
center: LatLng(60.7666, 10.8471),
zoom: 9.0,
),
children: [
TileLayer( // Map from OpenStreetMap
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: const ['a', 'b', 'c'],
),
],
);
}
}
...@@ -119,10 +119,10 @@ packages: ...@@ -119,10 +119,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.6" version: "0.13.5"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
...@@ -147,6 +147,30 @@ packages: ...@@ -147,6 +147,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.2" version: "0.8.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "2.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
...@@ -167,26 +191,26 @@ packages: ...@@ -167,26 +191,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.16+1"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.8.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.11.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:
...@@ -207,10 +231,10 @@ packages: ...@@ -207,10 +231,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.3" version: "1.9.0"
path_drawing: path_drawing:
dependency: transitive dependency: transitive
description: description:
...@@ -360,6 +384,22 @@ packages: ...@@ -360,6 +384,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: "3979f0b1c5a97422cadae52d476c21fa3e0fb671ef51de6cae1d646d8b99fe1f"
url: "https://pub.dev"
source: hosted
version: "20.4.54"
syncfusion_flutter_maps:
dependency: "direct main"
description:
name: syncfusion_flutter_maps
sha256: "1c95924e2dee5bbad922c2d2f1d5fb1b13ce4548c34a6d30fe6bf8821dfcfcd6"
url: "https://pub.dev"
source: hosted
version: "20.4.54"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
...@@ -408,14 +448,14 @@ packages: ...@@ -408,14 +448,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
web: vm_service:
dependency: transitive dependency: transitive
description: description:
name: web name: vm_service
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "13.0.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:
......
name: app name: app
description: "A new Flutter project." description: "IceMap Application"
publish_to: 'none' publish_to: 'none'
version: 0.1.0 version: 0.1.0
...@@ -15,8 +15,7 @@ dependencies: ...@@ -15,8 +15,7 @@ dependencies:
provider: ^5.0.0 provider: ^5.0.0
fl_chart: ^0.20.0-nullsafety1 fl_chart: ^0.20.0-nullsafety1
google_fonts: any google_fonts: any
syncfusion_flutter_maps: ^20.4.41
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
...@@ -27,3 +26,5 @@ flutter: ...@@ -27,3 +26,5 @@ flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/icons/ - assets/icons/
- assets/mjosa.geojson
- assets/australia.json
images/image-1.png

10.6 KiB

images/image-10.png

1.2 KiB

images/image-11.png

48.2 KiB

images/image-12.png

34.9 KiB

images/image-2.png

1.71 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment