Skip to content
Snippets Groups Projects
Commit 9659058d authored by Sara Savanovic Djordjevic's avatar Sara Savanovic Djordjevic
Browse files

update: db structure and api req

parent 97f6e99a
No related branches found
No related tags found
1 merge request!3Choropleth map implementation
......@@ -12,7 +12,7 @@ Future<List<Measurement>> fetchMarkerData() async {
..badCertificateCallback = // NB: temporary disable SSL certificate validation
(X509Certificate cert, String host, int port) => true;
// Request markers from API
// Request markers from server
var request = await client.getUrl(Uri.parse(serverURI + mapEndpoint));
var response = await request.close(); // Close response body at end of function
......@@ -23,8 +23,11 @@ Future<List<Measurement>> fetchMarkerData() async {
if (responseBody.isNotEmpty) {
var jsonData = json.decode(responseBody);
if (jsonData != null && jsonData is List) { // Check if jsonData is not null and is a List
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');
}
......@@ -35,7 +38,6 @@ Future<List<Measurement>> fetchMarkerData() async {
throw Exception('Failed to fetch marker data: Status code ${response.statusCode}');
}
} catch (e) {
print('Error fetching marker data: $e');
throw Exception('Failed to fetch marker data: ${e.toString()}');
}
}
......
import 'dart:core';
import 'package:latlong2/latlong.dart';
class Measurement {
int measurementID;
DateTime timeMeasured;
Sensor sensor;
List<Data> dataList;
List<Corner> cornerList;
String bodyOfWater;
LatLng center;
List <SubDiv> subDiv;
Measurement({
required this.measurementID,
required this.timeMeasured,
required this.sensor,
required this.dataList,
required this.cornerList,
required this.bodyOfWater,
required this.center,
required this.subDiv,
});
factory Measurement.Measurement(Map<String, dynamic> json) {
factory Measurement.fromJson(Map<String, dynamic> json) {
return Measurement(
measurementID: json['MeasurementID'],
timeMeasured: DateTime.parse(json['TimeMeasured']),
sensor: Sensor.fromJson(json['Sensor']),
dataList: (json['Data'] as List<dynamic>)
.map((data) => Data.fromJson(data))
.toList(),
cornerList: (json['Corners'] as List<dynamic>)
.map((data) => Corner.fromJson(data))
.toList(),
bodyOfWater: json['BodyOfWater'],
/*
dataList: (json['Data'] != null && json['Data'] is List)
? (json['Data'] as List<dynamic>).map((data) => Data.fromJson(data)).toList()
: [],
cornerList: (json['Corner'] != null && json['Corner'] is List)
? (json['Corner'] as List<dynamic>).map((data) => Corner.fromJson(data)).toList()
: [],
bodyOfWater: json['WaterBodyName'] ?? '',
*/
bodyOfWater: json['BodyOfWater'] ?? 'nil',
center: LatLng(json['CenterLat'], json['CenterLon']),
subDiv: (json['Subdivisions'] as List<dynamic>).map((data) => SubDiv.fromJson(data)).toList(),
);
}
}
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'],
);
}
}
......@@ -56,57 +73,8 @@ class Sensor {
factory Sensor.fromJson(Map<String, dynamic> json) {
return Sensor(
sensorID: json['SensorID'],
sensorType: json['SensorType'],
sensorType: json['SensorType'] ?? 'nil',
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 {
int 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['Latitude'],
longitude: json['Longitude'],
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
// InteractivePolygon returns a Polygon object wrapped in a GestureDetector
// in order to make the Polygon clickable
class InteractivePolygon extends StatelessWidget {
final List<Offset> points;
final VoidCallback onTap;
final Color color;
final double strokeWidth;
InteractivePolygon({
required this.points,
required this.onTap,
this.color = Colors.blue,
this.strokeWidth = 1.0,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: CustomPaint(
painter: PolygonPainter(
points: points,
color: color,
strokeWidth: strokeWidth,
),
),
);
}
}
// PolygonPainter takes the points, color, and stroke width from a
// object of type InteractivePolygon, and renders it
class PolygonPainter extends CustomPainter {
final List<Offset> points;
final Color color;
final double strokeWidth;
PolygonPainter({
required this.points,
required this.color,
required this.strokeWidth,
});
// paint() iterates through all the vertices of the polygon and connects
// each sequential pair with a line
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth;
final path = Path();
path.moveTo(points[0].dx, points[0].dy);
for (var i = 1; i < points.length; i++) {
path.lineTo(points[i].dx, points[i].dy); // Render line between vertices
}
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(PolygonPainter oldDelegate) {
return oldDelegate.points != points ||
oldDelegate.color != color ||
oldDelegate.strokeWidth != strokeWidth;
}
}
\ No newline at end of file
......@@ -69,12 +69,8 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: const ['a', 'b', 'c'],
),
PolygonLayer(
/*PolygonLayer(
polygons: widget.markerList.map((Measurement measurement) {
// Map corners to a list of LatLng objects
List<LatLng> points = measurement.cornerList.map((Corner corner) {
return LatLng(corner.latitude, corner.longitude);
}).toList();
return Polygon(
points: points, // Use list of corner coordinates to render polygon
......@@ -82,35 +78,14 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
isFilled: true,
);
}).toList(),
),
),*/
MarkerLayer(
markers: widget.markerList.map((Measurement measurement) {
List<LatLng> corners = measurement.cornerList.map((Corner corner) {
return LatLng(corner.latitude, corner.longitude);
}).toList();
// point calculates the middle point between corners
LatLng point(List<LatLng> coordinates) {
double averageLatitude = 0.0;
double averageLongitude = 0.0;
for (LatLng point in coordinates) {
averageLatitude += point.latitude;
averageLongitude += point.longitude;
}
// Calculate average latitude and longitude
averageLatitude /= coordinates.length;
averageLongitude /= coordinates.length;
return LatLng(averageLatitude, averageLongitude); // Return the middle point
}
return Marker(
width: 50,
height: 50,
point: point(corners),
point: measurement.center, // Set markers at center of measurement
builder: (ctx) => GestureDetector(
onTap: () {
setState(() {
......@@ -126,7 +101,6 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
);
}).toList(),
),
],
),
),
......@@ -268,7 +242,7 @@ class _MapContainerWidgetState extends State<MapContainerWidget> {
),
const SizedBox(height: contPadding),
Text(
'Measuring point: (${selectedMarker?.dataList[0].latitude}, ${selectedMarker?.dataList[0].latitude})',
'Measuring point: (${selectedMarker?.measurementID}, ${selectedMarker?.measurementID})',
style: regTextStyle,
),
const SizedBox(height: contPadding),
......
No preview for this file type
......@@ -2,16 +2,12 @@
# Server variables
HOST = "0.0.0.0"
PORT = 8443
# Database paths
DB_NAME = 'IceMapDB'
#COLLECTION = 'IceData'
COLLECTION = 'TestCollection' # NB: temporary collection
MONGO_URI = "mongodb+srv://icemapcluster.i02epob.mongodb.net/?authSource=%24external&authMechanism=MONGODB-X509&retryWrites=true&w=majority"
PORT = 8443
# Certificate paths
CERT_DIR = "server/certificates/"
MONGO_CERT_PATH = CERT_DIR + "MongoCert.pem"
SSL_KEY_PATH = CERT_DIR + "testKey.key"
SSL_CERT_PATH = CERT_DIR + "testCert.crt"
\ No newline at end of file
SSL_CERT_PATH = CERT_DIR + "testCert.crt"
# Measurement specs
AREA_SIZE = 20
\ No newline at end of file
No preview for this file type
from math import pi, cos
EARTH = 6378.137 # Radius of the earth in kilometer
METER = (1 / ((2 * pi / 360) * EARTH)) / 1000 # 1 meter in degree
OFFSET = 20 # Offset in meters
def calculate_corners(lat, lng):
# From https://stackoverflow.com/questions/7477003/calculating-new-longitude-latitude-from-old-n-meters
# Formulas:
lat_pos = lat + (OFFSET * METER)
lng_pos = lng + (OFFSET * METER) / cos(lat * (pi / 180))
lat_neg = lat - (OFFSET * METER)
lng_neg = lng - (OFFSET * METER) / cos(lat * (pi / 180))
return [
(lat_neg, lng_pos),
(lat_pos, lng_pos),
(lat_pos, lng_neg),
(lat_neg, lng_neg)
]
# def div_measurement():
......@@ -6,7 +6,7 @@ import json
def get_all_markers(self, cursor, valid: bool, waterBodyName):
try:
sql_query = '''
SELECT m.MeasurementID, m.SensorID, m.TimeMeasured,
SELECT m.MeasurementID, m.SensorID, m.TimeMeasured, m.CenterLat, m.CenterLon,
s.SensorType, s.Active,
b.Name,
d.SubDivisionID, d.GroupID, d.MinimumThickness,
......@@ -35,13 +35,13 @@ def get_all_markers(self, cursor, valid: bool, waterBodyName):
# Create subdivision new object
sub_division = {
'SubdivID': row[6],
'GroupID': row[7],
'MinThickness': row[8],
'AvgThickness': row[9],
'CenLatitude': row[10],
'CenLongitude': row[11],
'Accuracy': row[12]
'SubdivID': row[8],
'GroupID': row[9],
'MinThickness': row[10],
'AvgThickness': row[11],
'CenLatitude': row[12],
'CenLongitude': row[13],
'Accuracy': row[14]
}
# Check if measurement ID already exists in measurement_data
......@@ -55,10 +55,12 @@ def get_all_markers(self, cursor, valid: bool, waterBodyName):
measurement_data[measurement_id] = {
'MeasurementID': measurement_id,
'TimeMeasured': row[2],
'CenterLat': row[3],
'CenterLon': row[4],
'Sensor': { # Each measurement only has one related sensor
'SensorID': row[1],
'SensorType': row[3],
'Active': bool(row[4])
'SensorType': row[5],
'Active': bool(row[6])
},
'Subdivisions': [sub_division], # Array of sub_division objects
}
......
No preview for this file type
......@@ -16,6 +16,8 @@ CREATE TABLE Measurement (
SensorID INT NOT NULL,
TimeMeasured DATETIME NOT NULL,
WaterBodyName TEXT NOT NULL,
CenterLat FLOAT NOT NULL,
CenterLon FLOAT NOT NULL,
WholeAverageThickness FLOAT NOT NULL,
FOREIGN KEY (SensorID) REFERENCES Sensor(SensorID),
FOREIGN KEY (WaterBodyName) REFERENCES BodyOfWater(Name)
......
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