From 9659058de9da606a12d006585d73a2bfeecdf7a6 Mon Sep 17 00:00:00 2001
From: Sara <sarasdj@stud.ntnu.no>
Date: Mon, 26 Feb 2024 12:51:45 +0100
Subject: [PATCH] update: db structure and api req

---
 app/lib/pages/marker_handler/get_markers.dart |  10 +-
 app/lib/pages/marker_handler/marker_data.dart | 110 +++++++-----------
 .../pages/widgets/interactive_polygon.dart    |  71 +++++++++++
 app/lib/pages/widgets/map_widget.dart         |  34 +-----
 server/__pycache__/consts.cpython-311.pyc     | Bin 602 -> 359 bytes
 server/consts.py                              |  14 +--
 .../__pycache__/get_markers.cpython-311.pyc   | Bin 3265 -> 3345 bytes
 server/map/divide_measurement.py              |  24 ++++
 server/map/get_markers.py                     |  22 ++--
 server/sql_db/icedb                           | Bin 45056 -> 49152 bytes
 server/sql_db/schema.sql                      |   2 +
 11 files changed, 163 insertions(+), 124 deletions(-)
 create mode 100644 app/lib/pages/widgets/interactive_polygon.dart
 create mode 100644 server/map/divide_measurement.py

diff --git a/app/lib/pages/marker_handler/get_markers.dart b/app/lib/pages/marker_handler/get_markers.dart
index 702d4145..156de62c 100644
--- a/app/lib/pages/marker_handler/get_markers.dart
+++ b/app/lib/pages/marker_handler/get_markers.dart
@@ -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()}');
   }
 }
diff --git a/app/lib/pages/marker_handler/marker_data.dart b/app/lib/pages/marker_handler/marker_data.dart
index fd466ed8..e84c1416 100644
--- a/app/lib/pages/marker_handler/marker_data.dart
+++ b/app/lib/pages/marker_handler/marker_data.dart
@@ -1,43 +1,60 @@
 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
diff --git a/app/lib/pages/widgets/interactive_polygon.dart b/app/lib/pages/widgets/interactive_polygon.dart
new file mode 100644
index 00000000..52968654
--- /dev/null
+++ b/app/lib/pages/widgets/interactive_polygon.dart
@@ -0,0 +1,71 @@
+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
diff --git a/app/lib/pages/widgets/map_widget.dart b/app/lib/pages/widgets/map_widget.dart
index ebb7521c..ea3ec581 100644
--- a/app/lib/pages/widgets/map_widget.dart
+++ b/app/lib/pages/widgets/map_widget.dart
@@ -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),
diff --git a/server/__pycache__/consts.cpython-311.pyc b/server/__pycache__/consts.cpython-311.pyc
index 5c3188f8a9d95c5a2766585e72bbb8895f196618..3c60371f12da04a05b4df70c07a52110b373b16c 100644
GIT binary patch
delta 240
zcmcb`@|=lpIWI340}!~HT~GbWFp*EfP7BDJ&XB?o#gM`n#hAhr#gxjF!dwLujba9}
zSimfn6xJx#6t-XnP4+5w13fUz{H?&iFj;_6+=9C#wYbDPwNfuTwX%u_#Bok7D$z?W
zDtRdaG{;Yq?G}rNe{jexmH_{tkXsziu0bL3E}lV?XEDlh-{N!(a&?Rk_Kb3!yq(e6
zlLx4S5r~V;fy4)9Mn=XD91I+S4J<d<xf@tMurcr`HE`VE7oVYfkzc)m=Z1j947-a0
Snhm@_DPH~tP7o{t`4IrNZ#+%_

delta 503
zcmX|-!EVz)6h+656FX^~q!ow?goKa?ilBB>!2(1;jT6y`*sc;Iz#G@KUt3qP9gQc2
zdO`gQS&={BYgl9#4I9LUZT4)K7{oly=-x9|?~UdcPlei7S*{?qPJey_iJ#YYuU|0t
z1^LKEK4)`2Z}Wb^F8G2i_(i)2MZc7?gk54v%$3NLlq<<Ek5Q|ViftvY;P*X*s5J0E
zcc;BBtyz!|EuKukBS9RcpTjshi~W;F3BG);s(}Zgdp-3gvxERvg7!X4;}hjyNQr=`
zuiP0qAICH7LFd8#6Zph}kvnN_unyk28wE+&(M{vE+3W7Tf7*W5#6a-XI~)*5It0(4
z`LPqa7cm1@>5T;NC1BM9OoG$Ea|vuGYAWlScWDeNQwZnjoin3Vroy2)wy1Dqjw~wn
zx{h(6_i05lhr_;R4NQZU^SRF3(SS-?e`Gnm!HCxX!rUJnSckMa9uJ*2{SQv=<c<4(
zIsdL5H`woTY$pag)&-8^))JDoGhroeWx~3Iwr*zP5BY9ZS@THN`K-)X^?oL;c5W}6
g<<8@*zN+6^>@DjLv+Am`yLh>5v@&^9xW-!k0WF7_wg3PC

diff --git a/server/consts.py b/server/consts.py
index 295463e8..5653feea 100644
--- a/server/consts.py
+++ b/server/consts.py
@@ -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
diff --git a/server/map/__pycache__/get_markers.cpython-311.pyc b/server/map/__pycache__/get_markers.cpython-311.pyc
index 7dbac2b7ec9e41b54e8502402b73e37c0dd244fc..0936525a134986cdf0765898d85dcce201203bbb 100644
GIT binary patch
delta 655
zcmX>oIZ=vlIWI340}vEv-bq#Do5&Z$#SP>!1M%lS6SGPfw{5&H#Hdrlo+2O#QU(S<
zo*;r(!VVK>U;xSpO=e`06cwJsT*H~pm?AQVt%j|J!v?6NXZvJ*rrFk_%NQ9LRs%5v
zFfycw)pDnZ*Ye~^q)60qmk7dS7#OlRVeA^N6#g}mlUbRKWu<C(XEV%Y!fwjsQ091M
z=@gmC+nI|QWhZO1D6`1<B_>P`VsVko)pJhGD@iT#Ni5M(fOGQmbS5jZifmrRqR%w>
zF`ESYOJ1O*FZm|_V9ON+1;a}g5CL-UOI8rUHo1UZC4}u3uWxE%acNO%F3?a<ms>m`
znYpP@p_E&kaI4_V{JdLi!Kr!0`9(#-Ky#|3T#JhGixe{R6begIiz+ko(l<Y4f5Ir_
z43uO8;^GV@28ITP4@^FjZ*vB)DRK&ZU{IWF$fd~Tw1oYJg7N|&H93Z>fbWK)%7VZp
zVo+-G87^HhQEn5UY9MhzKw*L0MFE2g0tR1YCi8RKaOq#<F}T8GaDm5QawzwsdIeWy
zhNH?{ZoJG#b;aB`n2&L=0@<3(MLIwy6sdy<kb_n-d<K#XMNUBC7l%!5eoARhs$G#A
rkP8Z#Vk;o=ftit!@dg8T0~mhb3T0$8S|Rd*0Yu{>xgioou0Z_&gy*3n

delta 585
zcmbOzbx@LTIWI340}yx`UQfNqJCQGlixbFW2I9{TCT5i|uGn~Ah*6+~9i#>VYS>c*
zCd)EOiVDtQuHj5)Oc9#HQo~lmVFQ%zSvlF8X|}cSGDZf5)j$jZj0`CvwcIJ9wLEze
zDPpzUC4w*+28Jw<bzo4#l_Ic4e6k|5v8+T5?`(#-OxR7CT*w^HESVxT`7(1cyEH`2
zWO*hrZlE;`!3>%*eu?gr^H^LaFJ=|le4j<1iH!|t{!8}BT<kKF4cN1Q>VRC34_~rO
zUeB%)$a0I<H#M=iv?w(<HLt|e<rYs!W^O7}DCHJgaB5z0eo>JyP)C)nW1d24QBi)8
zLVj{`X;D#Xib8otW=^U?VQFenWoBNwLP<vIW_6Ayj6(K6^&p#zqnH>N8W=t>c}<q(
z3Sg7x6#Bp*Ke?Dok;`!j+YJTf1wd-@Jg$PtGTgdif}AEmX&`YyKw*L0MFE2g0tR0t
zCdYHza5-J%alXRie1XS#@>1?c^+K+q3`a$|T<w{U%8R*LF(0*J1+q1ni!^|C7Ab=W
zkPB8ad<K#XMRq{q7l%!5eoARhs$G#2kP8ZjVk;o=ftit!@dg8T0~mhb3T9+9S|Rd*
P0Yu{>xgioojzIkYXzPt!

diff --git a/server/map/divide_measurement.py b/server/map/divide_measurement.py
new file mode 100644
index 00000000..02081900
--- /dev/null
+++ b/server/map/divide_measurement.py
@@ -0,0 +1,24 @@
+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():
diff --git a/server/map/get_markers.py b/server/map/get_markers.py
index 48ce0673..2f5c6733 100644
--- a/server/map/get_markers.py
+++ b/server/map/get_markers.py
@@ -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
                 }
diff --git a/server/sql_db/icedb b/server/sql_db/icedb
index b19e3e7766c83a08ad8f1b6aa7c08743c90ab02d..2ba5aeca796f334edd9df517a92df6a2d401852f 100644
GIT binary patch
delta 657
zcmZp8z|_#dJV9EJkAZ=K2Z*_Wm=TD1C+Zlh@-gV8z2fDPX5ePmW8hEcKgie1Z^bRf
zvzvV#&u^~lTv6<LY<D+KjAV-fs<|j`ZOzzZT#}fSlUiJulUb5lTvD2nnIB)0np+T$
zPngF!$kj2#RUyRD$;VXzqPI?fUEE!nu~B~V0xn%fp2>k+vXftP$p|QDgeR7y7CGgo
zRQe_6rfP0p$DYf?!p+BEKUtp3nzNdpUECF@O&X|8baEaS507tZVsU9vYHn&?2^Y{5
z3qb{qkj&gvsBns=Dc|J1JTj9@_#`=aJ~40rz0R;%jQb9wF#it*PH|;r#@1$jUUo1e
zb@Cq`v&rVXa^ecXsd>fuMV>ASo_-+;e*Qol>f@s`xrA3?@(f<t$vb(~ycC>)R;CvD
zB$g<+`S?2`lqtBlx;cjWgeYhj=ox70z|`gE!3{!FG5ImCCXa#!%uY?6$<lnr910rX
z0MOjr%U9ya%b&);%&*PBug!mizjL#ofGj_!B_jtXqp>(U`{aA^N|FwK$!AShL{vJc
zmNv4f+|>vAQ<)Pe%Q3k-UW(I_nS&9;V+Zm;YBLj`d{6!7pqkX=u)V>=iiv{}V$zm)
zWdR4jSP89}2KyXT52-cG^$gmSz-F+SZNV>o5m4Yk0VpkjFdNV<OZg}7T<^YVLJR-o
bmiTyCPG)gN->m%NL}oaPiGx|36D$S*8-%l9

delta 345
zcmZo@U~YK8G(lRBhk=2C8;D_meWH%BG7p1Z+ACiE9}L{=>lpac`495-@>}tI;tS&z
z<JrwMolBa1-DW`n9rn%Z*mId!s<_z2C(CnLPoBx8FRl@inVagHnpj*~l$xSh%gHY8
zuFTjdJK3CFcXB_M++-hi-pT#!{QMf>i6yB;PWdU7eu=rMnw!PA?=W&TC9<-MD=Ra$
za!zLDHDl#c0D{TZymFK8@NtWHx+r-1g(&#>197O2j}Az7asjUb3z#v1R~Eup%d0k7
zpNDm_9G?ciMsR9gaek4fizY;M5T7xZ2G~9>O;fhXdwFCgf8~<g+{;(u$O{YrX8sli
z{_Ff}`CEX#E90NsvOj*ZWxVXB1U7@sY#ZwMCoYJW<zyCT^v%jIPGp9&m^hflIl*F!
HCI|okyRBEq

diff --git a/server/sql_db/schema.sql b/server/sql_db/schema.sql
index 14138216..ebc8dbae 100644
--- a/server/sql_db/schema.sql
+++ b/server/sql_db/schema.sql
@@ -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)
-- 
GitLab