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