From 55cf1caf97cd8752798eddd7134eab64441ed002 Mon Sep 17 00:00:00 2001
From: Sara <sarasdj@stud.ntnu.no>
Date: Mon, 11 Mar 2024 11:49:53 +0100
Subject: [PATCH] update: working division of relation

---
 app/lib/pages/default_page.dart               |  10 +-
 app/lib/pages/widgets/cloropleth_map.dart     | 139 ++++++++++++------
 .../__pycache__/get_markers.cpython-311.pyc   | Bin 3330 -> 3203 bytes
 .../__pycache__/get_relation.cpython-311.pyc  | Bin 3040 -> 4594 bytes
 server/map/get_relation.py                    |  89 ++++++-----
 5 files changed, 152 insertions(+), 86 deletions(-)

diff --git a/app/lib/pages/default_page.dart b/app/lib/pages/default_page.dart
index 0879ac6a..4f0fe3b1 100644
--- a/app/lib/pages/default_page.dart
+++ b/app/lib/pages/default_page.dart
@@ -1,14 +1,11 @@
 import 'dart:async';
 import 'package:flutter/material.dart';
-import 'package:path_provider/path_provider.dart';
 import 'widgets/map_widget.dart';
 import 'marker_handler/marker_data.dart';
 import 'consts.dart';
 import 'marker_handler/get_markers.dart';
 import 'marker_handler/get_relation.dart';
 import 'dart:typed_data';
-import 'dart:io';
-import 'dart:convert';
 
 class DefaultPage extends StatefulWidget {
   const DefaultPage({Key? key}) : super(key: key);
@@ -28,6 +25,7 @@ class _DefaultPageState extends State<DefaultPage> {
   @override
   void initState() {
     super.initState();
+    // Try to fetch measurement data from server
     markerListFuture = fetchMarkerData().then((fetchResult) {
       List<Measurement> measurements = fetchResult.measurements;
       serverConnection = fetchResult.connected;
@@ -39,8 +37,8 @@ class _DefaultPageState extends State<DefaultPage> {
     });
 
 
-    //relationFuture = fetchRelation();
-    relationFuture = Future.value(Uint8List(0));
+    relationFuture = fetchRelation();
+    //relationFuture = Future.value(Uint8List(0));
 
     // Schedule fetchMarkerData to run periodically based on fetchInterval from consts
     const Duration interval = Duration(minutes: fetchInterval); // NB fetchInterval value to be determined
@@ -87,7 +85,7 @@ class _DefaultPageState extends State<DefaultPage> {
             } else if (snapshot.hasError) {
               return Center(child: Text('Error: ${snapshot.error}'));
             } else {
-              if (!serverConnection) {
+              if (!serverConnection) { // NB: implement alert
                 print("Failed to connect to server");
               }
               // Display default page once all data is loaded from server
diff --git a/app/lib/pages/widgets/cloropleth_map.dart b/app/lib/pages/widgets/cloropleth_map.dart
index 65804c00..6cd8d968 100644
--- a/app/lib/pages/widgets/cloropleth_map.dart
+++ b/app/lib/pages/widgets/cloropleth_map.dart
@@ -3,8 +3,9 @@ import '../marker_handler/marker_data.dart';
 import 'package:flutter/material.dart';
 import 'package:syncfusion_flutter_maps/maps.dart';
 import 'dart:math';
+import 'dart:convert';
 
-/// A class containing thickness for each subdivision of the map.
+/// A class containing thickness data for each subdivision of the map.
 class IceThicknessModel {
   IceThicknessModel(this.subDivID, this.thickness);
 
@@ -12,9 +13,9 @@ class IceThicknessModel {
   final int 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.
+/// ChoroplethMap is a stateful widget that contains a choropleth map.
+/// The map is created using the Syncfusion Flutter Maps library and
+/// coordinates fetched from the server.
 class ChoroplethMap extends StatefulWidget {
   const ChoroplethMap({Key? key,
     required this.relation,
@@ -39,55 +40,101 @@ class _ChoroplethMapState extends State<ChoroplethMap> {
 
     final Random random = Random();
     for (int i = 0; i <= 60; i++) {
-      int randomNumber = random.nextInt(21); // 0 -> 20
+      int randomNumber = random.nextInt(21); // 0 -> 20, NB: temp test data
       iceThicknessList.add(IceThicknessModel(i.toString(), randomNumber));
     }
   }
 
   @override
   Widget build(BuildContext context) {
-    return SfMaps(
-        layers: [
-          MapShapeLayer(
-            source: MapShapeSource.memory(
-              widget.relation,
-              shapeDataField: 'properties.SubDivID',
-              dataCount: iceThicknessList.length,
-              primaryValueMapper: (int index) => iceThicknessList[index].subDivID,
-              shapeColorValueMapper: (int index) => iceThicknessList[index].thickness,
-              shapeColorMappers: const [
-                MapColorMapper(
-                  from: null,  // Default color
-                  color: Colors.grey,
-                  text: 'No value',
-                ),
-                MapColorMapper(
-                    from: 0,
-                    to: 3,
-                    color: Color.fromRGBO(223,169,254, 1),
-                    text: '0-3'),
-                MapColorMapper(
-                    from: 4,
-                    to: 8,
-                    color: Color.fromRGBO(190,78,253, 1),
-                    text: '4-8'),
-                MapColorMapper(
-                    from: 9,
-                    to: 15,
-                    color: Color.fromRGBO(167,17,252, 1),
-                    text: '9-15'),
-                MapColorMapper(
-                    from: 16,
-                    to: 20,
-                    color: Color.fromRGBO(170,20,250, 1),
-                    text: '16-20'),
-              ],
+    return Stack(
+      children: [
+        SfMaps(
+          layers: [
+            MapShapeLayer(
+              source: MapShapeSource.memory( // Map polygon
+                widget.relation, // JSON coordinates from server
+                shapeDataField: 'properties.SubDivID',
+                dataCount: iceThicknessList.length,
+                primaryValueMapper: (int index) => iceThicknessList[index].subDivID,
+                shapeColorValueMapper: (int index) => iceThicknessList[index].thickness,
+              ),
+              color: Colors.lightBlueAccent, // Map color
+              zoomPanBehavior: _zoomPanBehavior,
+              strokeColor: Colors.orange,
             ),
-            //color: Colors.lightBlueAccent,
-            zoomPanBehavior: _zoomPanBehavior,
-            strokeColor: Colors.orange,
-          ),
-        ],
+          ],
+        ),
+        CustomPaint( // Render polygons clipped on map
+          size: Size.infinite,
+          painter: OverlayPainter(mapData: widget.relation),
+        ),
+      ],
     );
   }
 }
+
+
+/// OverlayPainter is a class extending CustomPainter to
+class OverlayPainter extends CustomPainter {
+  final Uint8List mapData;
+
+  const OverlayPainter({
+    required this.mapData, // Pass map shape
+  });
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    // NB: test polygon
+    Path testPol = Path();
+    testPol.moveTo(60.8015, 10.5908);
+    testPol.lineTo(60.5146, 10.8270);
+    testPol.lineTo(60.7115, 11.3242);
+    testPol.close();
+
+    // Decode map data
+    var mapShape = json.decode(utf8.decode(mapData));
+
+    // Extract polygons and render them
+    for (var feature in mapShape['features']) {
+      var geometry = feature['geometry'];
+      var coordinates = geometry['coordinates'];
+
+      for (var ring in coordinates) {
+        var points = <Offset>[];
+        for (var point in ring) {
+          points.add(Offset(point[0], point[1]));
+        }
+        var path = Path()..addPolygon(points, true);
+
+        // Render each map shape
+        canvas.drawPath(path, Paint()..color = Colors.lightBlueAccent);
+      }
+    }
+
+    // Now clip the rendering to the map shape
+    var mapBoundsPath = Path();
+    for (var feature in mapShape['features']) {
+      var geometry = feature['geometry'];
+      var coordinates = geometry['coordinates'];
+
+      for (var ring in coordinates) {
+        var points = <Offset>[];
+        for (var point in ring) {
+          points.add(Offset(point[0], point[1]));
+        }
+        mapBoundsPath.addPolygon(points, true);
+      }
+    }
+    canvas.clipPath(mapBoundsPath);
+
+    // Render test shape over map, not clipped to map
+    canvas.drawPath(testPol, Paint()..color = Colors.pink.shade900);
+  }
+
+
+  @override
+  bool shouldRepaint(covariant CustomPainter oldDelegate) {
+    return false;
+  }
+}
diff --git a/server/map/__pycache__/get_markers.cpython-311.pyc b/server/map/__pycache__/get_markers.cpython-311.pyc
index c71428b3dba827673ec34ba15710366ad6c1fc07..32f67260dd7d434a9a4797a16a3b99c5f9901391 100644
GIT binary patch
delta 910
zcmZvaO=uHQ5Xa~3+waYn`KaBb29pR9R47I8(6pd53LeCdi&lD&zO+bV3Y%?3*tJDH
zc<5z$hh7rMsUj62<e&$yIe4o#$=OS93&o3|INLTPE6(y}=Ewf$|K4F<WnY`IchRU0
z80{~IH5T<^lhriRE-+xE4vWBqH?T+;c>ra47zpny)1aJ&fi>WxADDMR!zq_Kq~mqd
z6ddZcuWgA>!Id0w0hT_wvLiFW91er4Akz^ei6N3gB!@@|ks2axMDt8<DX!`$j<gBZ
zjH5b|Gd9YMk#v(-g@6BYj}+F_Jp!<g=OhU1*yty-$Y>TR8lqT;;vq_m$X14~0O)as
zpA*cB$rg3gRk8{!)xvi>N(DtbpKWbSD|IBVTjX0poN9K|^RoAuuqFh8X3CC^3AOO!
zhbk*+$44rb4yy>TjH~nCf;p?P*oTQdblyL94`y7waj67$uqmf;0j#`hgdER02ERu$
zDfD@S>*N|QjfQ$s`3qVile|sK)MQ!yl|H6=C&9ZkLvnmhERm_+6LCf8{gG8dl08K&
zl6YhwHf!~jfxNt7HP<aZr`5#>-p)U24+}T2k?}Nd;~TfZ7j6+L=L?Zc<gMmUffnbp
z`W3mTC4DIJhCawdu40SnAaTBv|CX3ONKF6C_m+$tc|K7!p>OIp3GJ8C)e7xbB<#zA
zbr~<Pf}7sM5oWNiWBR35o~^TudTrjSA_hCs41_)<gnS2M`}p(qyM)a6@c(Qr;J{xk
C4Vd--

delta 1127
zcmZ{jO-vI(6vyA}?02^xK#P=uS?GltQGyVoAw-ZiA!<dbC>SxwECel(Y)cPh*AnzV
z3>UHo4r0OyG!QS1(Zn-eg&XO~lL@y~;>DA*Kq*UbHuK*6_WkESyR$p5!t=Ghb+1<j
z+S<PEY0rF1zM6b3rDY&MP#T7SFd7<T2!72DF$DEPjNNnO$s(5<Nl%bb)}f>3p&8S@
z|C`2Tkf?>p8jQ{vEMaE70F=<`k=UIKb`1e+(7yEMa-N$2lQ*~?c=Fj441wVAvDy?V
zvt_2lxrld>;3CmQl8fXrbrR2tU`mE)@Hx<14awk*6H|#&mgbP&aJp*cP=mG3`11%B
zv(!W&-tq)WJ{S326mU^RnX<z176A3AELTb!cUUJ?E7*|6(KwJua7A#Qt%Ft;zhtEo
z2E28HxssQ&^qZ8L!JRU~FBqqmsdB00vsHR!)?ac0AR)I30jV}UQ`OmuvgWyTz66F$
z{F~?2=yDqeOFH*Wj}^e7Yl_RZ#!%kEJLW9&LHfs<%1dQRJj~WGj-s$Nk7Kx&M68ea
zaoDpOD6M!hlRB%BjiUNOf*oV7&bF&v$sTp`QfgvSyQ7U{W^|3HooY{4D*m+pL3dN~
z?!%_oMYT7Xir*jTx~;a;(qOEx$%_b86+Q`#DCjM+87)0l<VR=p%(SjMXTtg^ra5Q3
zRck<c)P9qWc-k|=^o{4hW^N%aVlzreS&-~HP%3Od(mk}Rgto(dJ4)X|?+?*ueV3Z~
zs8ne~)MDgf$lFGTG;9ad)$==nmhC{xAGPpHj-Y&fT!k%FPKbExLNI~xH_Xv-4C~Ew
zQN76t&Lez)eHuaUrs1#HF*!|U(psmUph%A*WUkX-V}#Hyc;*f+TTUU=`aWoblm8bz
KhXp-O=l=ti|GRwv

diff --git a/server/map/__pycache__/get_relation.cpython-311.pyc b/server/map/__pycache__/get_relation.cpython-311.pyc
index c51b70ad6c08be43a224a3676932bf1db92926e9..1a49b7006d65c76c71c4ebaa80c673617677d1bd 100644
GIT binary patch
literal 4594
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

literal 3040
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
A&;S4c

diff --git a/server/map/get_relation.py b/server/map/get_relation.py
index b277b8d5..6c7db641 100644
--- a/server/map/get_relation.py
+++ b/server/map/get_relation.py
@@ -1,7 +1,11 @@
 import geopandas as gpd
-from shapely.geometry import Polygon
+from shapely.geometry import Polygon, Point, LineString
+import matplotlib.pyplot as plt
+import numpy as np
+
 
 def get_relation(self, body_of_water: str):
+    print("In get_relation")
     # Load GeoJSON data using geopandas
     geo_data = gpd.read_file("server/map/mjosa.geojson")
 
@@ -15,8 +19,13 @@ def get_relation(self, body_of_water: str):
         print("Failed to convert to polygons")
         return
 
-    # Divide relation into tiles
-    tiles = divide_relation(polygons)
+    print("Performing div call")
+    tiles = divide_relation(polygons, 0.01)  # NB: any size under 0.02 will require patience
+    print("Executed div call")
+
+    # NB: temp test plot of map
+    tiles.plot(color='blue', edgecolor='orange', linewidth=0.5)
+    plt.show()
 
     # Convert GeoDataFrame to GeoJSON
     tiles_json = tiles.to_json()
@@ -26,40 +35,52 @@ def get_relation(self, body_of_water: str):
     self.send_header("Content-type", "application/json")
     self.end_headers()
 
-    # Write coordinates to response object
+    # Write GeoJSON to response object
     self.wfile.write(tiles_json.encode('utf-8'))
 
 
-def divide_relation(polygons):
-    # Define tile size
-    tile_size = 0.1
-    subdiv_id = 0
+def divide_relation(polygons, cell_size):
+    # Calculate the combined bounding box of all polygons
+    combined_bounds = polygons[0].bounds
+    for polygon in polygons[1:]:
+        combined_bounds = (min(combined_bounds[0], polygon.bounds[0]),
+                           min(combined_bounds[1], polygon.bounds[1]),
+                           max(combined_bounds[2], polygon.bounds[2]),
+                           max(combined_bounds[3], polygon.bounds[3]))
+
+    # Create an empty list to store polygons representing tiles
     tiles = []
 
+    # Iterate over each polygon
     for polygon in polygons:
-        x_min, y_min, x_max, y_max = polygon.bounds
-        rows = int((y_max - y_min) / tile_size)
-        cols = int((x_max - x_min) / tile_size)
-
-        if rows == 0 or cols == 0:  # Skip polygons that are smaller than the division size
-            continue
-
-        for row in range(rows):
-            for col in range(cols):
-                tile_bbox = Polygon([  # Calculate coordinate for current place in column and row
-                    (x_min + col * tile_size, y_min + row * tile_size),
-                    (x_min + (col + 1) * tile_size, y_min + row * tile_size),
-                    (x_min + (col + 1) * tile_size, y_min + (row + 1) * tile_size),
-                    (x_min + col * tile_size, y_min + (row + 1) * tile_size)
-                ])
-                tiles.append({"SubDivID": subdiv_id, "geometry": tile_bbox})
-                subdiv_id += 1
-
-    if len(tiles) <= 1:  # Return empty object if division fails
-        print("Failed to divide polygons into tiles")
-        return []
-
-    # Convert tiles list to a GeoDataFrame
-    tiles_df = gpd.GeoDataFrame(tiles, geometry='geometry')
-
-    return tiles_df
+        # Iterate over each cell
+        for i in np.arange(combined_bounds[1], combined_bounds[3], cell_size):
+            for j in np.arange(combined_bounds[0], combined_bounds[2], cell_size):
+                # Define the bounds of the cell
+                cell_bounds = (j, i, j + cell_size, i + cell_size)
+
+                # Create a polygon representing the cell
+                cell_polygon = Polygon([(cell_bounds[0], cell_bounds[1]),
+                                        (cell_bounds[2], cell_bounds[1]),
+                                        (cell_bounds[2], cell_bounds[3]),
+                                        (cell_bounds[0], cell_bounds[3])])
+
+                # Check if the cell polygon intersects with the current polygon
+                if polygon.intersects(cell_polygon):
+                    tiles.append(cell_polygon)
+
+    # Create a GeoDataFrame from the list of tiles
+    tiles_gdf = gpd.GeoDataFrame(geometry=tiles)
+
+    return tiles_gdf
+
+
+def divide_relation_2(polygons, cell_size):
+    polygon = Point(0, 0).buffer(2).difference(Point(0, 0).buffer(1))
+    line1 = LineString([(0, 0), (3, 3)])
+    line2 = LineString([(0, 0), (3, -3)])
+
+    line1_pol = line1.buffer(1e-3)
+    line2_pol = line2.buffer(1e-3)
+
+    new_polygon = polygon.difference(line1_pol).difference(line2_pol)
-- 
GitLab