diff --git a/app/lib/pages/default_page.dart b/app/lib/pages/default_page.dart index 8192e7b2cfac3d6c6801c8ae787ab99f83365767..7d4830e90ccb156dd6aaa8da0932b33e73c89686 100644 --- a/app/lib/pages/default_page.dart +++ b/app/lib/pages/default_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'dart:typed_data'; import '../consts.dart'; +import 'loading_page.dart'; import '../widgets/main_layout.dart'; import '../data_classes.dart'; import '../server_requests/fetch_markers.dart'; diff --git a/app/lib/pages/loading_page.dart b/app/lib/pages/loading_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..5e19257eb14c71d75de265c76f1409afa071b6c0 --- /dev/null +++ b/app/lib/pages/loading_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class _LoadingPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( // Loading screen + color: Colors.grey[900], + ), + child: Center( + child: Image.asset( + 'assets/icons/frozen.png', // Loading screen icon + // Icon from: https://www.flaticon.com/free-icons/cold-water" + color: Colors.grey, + width: 170, + height: 170, + ), + ), + ); + } +} \ No newline at end of file diff --git a/app/lib/widgets/choropleth_map.dart b/app/lib/widgets/choropleth_map.dart index a2bc6df760b38369db8a5df7e8eb1d4998304918..770ce4ef5a3c8e2d907d06b133545c18a982f222 100644 --- a/app/lib/widgets/choropleth_map.dart +++ b/app/lib/widgets/choropleth_map.dart @@ -27,39 +27,38 @@ class ChoroplethMap extends StatefulWidget { final Uint8List relation; final List<Measurement> measurements; - final void Function(int selectedIndex) onSelectionChanged; // Callback function + final void Function(int _selectedIndex) onSelectionChanged; // Callback function @override _ChoroplethMapState createState() => _ChoroplethMapState(); } class _ChoroplethMapState extends State<ChoroplethMap> { - int selectedIndex = -1; - Color selectedColor = Colors.grey; // Initialise to gray - late MapShapeSource dataSource; - late MapShapeSource mapShapeSource; + int _selectedIndex = -1; + Color _selectedColor = Colors.grey; // Initialise to gray + late MapShapeSource _dataSource; late final MapZoomPanBehavior _zoomPanBehavior = MapZoomPanBehavior(); - List<SubDiv> subdivisions = <SubDiv>[]; + List<SubDiv> _subdivisions = <SubDiv>[]; @override void initState() { super.initState(); - // Extract all subdivisions from list of measurements + // Extract all _subdivisions from list of measurements for (Measurement measurement in widget.measurements) { for (SubDiv subdivision in measurement.subDivs) { - subdivisions.add(subdivision); + _subdivisions.add(subdivision); } }; // Initialise data source - dataSource = MapShapeSource.memory( + _dataSource = MapShapeSource.memory( widget.relation, shapeDataField: 'sub_div_id', - dataCount: subdivisions.length, + dataCount: _subdivisions.length, primaryValueMapper: (int index) => - subdivisions[index].sub_div_id, + _subdivisions[index].sub_div_id, shapeColorValueMapper: (int index) => - subdivisions[index].avgThickness, // NB will later be minThickness, fix in server first + _subdivisions[index].avgThickness, // NB will later be minThickness, fix in server first shapeColorMappers: const [ MapColorMapper( from: 0, @@ -91,24 +90,25 @@ class _ChoroplethMapState extends State<ChoroplethMap> { children: [ SfMapsTheme( data: SfMapsThemeData( // Coloring of selected shape - selectionColor: selectedColor, + selectionColor: _selectedColor, selectionStrokeWidth: 3.5, selectionStrokeColor: Colors.blue[600], ), child: SfMaps( layers: [ MapShapeLayer( - source: dataSource, + source: _dataSource, zoomPanBehavior: _zoomPanBehavior, strokeColor: Colors.blue.shade50, + strokeWidth: 1, // Shape selection - selectedIndex: selectedIndex, + selectedIndex: _selectedIndex, onSelectionChanged: (int index) { // Shape selection behavior setState(() { - selectedIndex = index; - selectedColor = subdivisions[index].color; + _selectedIndex = index; + _selectedColor = _subdivisions[index].color; }); - widget.onSelectionChanged(selectedIndex); + widget.onSelectionChanged(_selectedIndex); }, ), ], diff --git a/app/lib/widgets/main_layout.dart b/app/lib/widgets/main_layout.dart index 78e693cf495137f0192f9227a088d98d8856facb..150237276cf6a8d82fceb284144e5908d2630c74 100644 --- a/app/lib/widgets/main_layout.dart +++ b/app/lib/widgets/main_layout.dart @@ -118,10 +118,7 @@ class _MapContainerWidgetState extends State<MapContainerWidget> { height: screenWidth * boxHeight, child: Visibility( visible: OSMlayer, - child: Opacity( - opacity: 0.7, - child: OSM(markerList: widget.markerList), - ), + child: OSM(markerList: widget.markerList), ), ), Positioned( // Satellite button diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 2442a708ccce155e0200b25901a69d56b68e97f4..342332141b76348b360cf5fba965bc78079b807d 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: provider: ^5.0.0 fl_chart: ^0.20.0-nullsafety1 # Charts and diagrams google_fonts: any # Fonts - syncfusion_flutter_maps: ^20.4.41 # Choropleth map + syncfusion_flutter_maps: ^20.4.41 # Choropleth map_handler syncfusion_flutter_core: any # Choropleth shape selection path_provider: ^2.0.8 shared_preferences: any # Persistent data storage diff --git a/server/APIs/__pycache__/get_weather.cpython-311.pyc b/server/APIs/__pycache__/get_weather.cpython-311.pyc deleted file mode 100644 index 4efcc88c6050e6cb91118c0e1bb170950b6e7797..0000000000000000000000000000000000000000 Binary files a/server/APIs/__pycache__/get_weather.cpython-311.pyc and /dev/null differ diff --git a/server/APIs/__pycache__/get_weather.cpython-39.pyc b/server/APIs/__pycache__/get_weather.cpython-39.pyc deleted file mode 100644 index 6a4217c61d5d10230214c9f68adf82ae4e7d0ed4..0000000000000000000000000000000000000000 Binary files a/server/APIs/__pycache__/get_weather.cpython-39.pyc and /dev/null differ diff --git a/server/APIs/get_weather.py b/server/APIs/get_weather.py deleted file mode 100644 index 37e76451af99392bc03592a8f6b2a96cfa2c682b..0000000000000000000000000000000000000000 --- a/server/APIs/get_weather.py +++ /dev/null @@ -1,90 +0,0 @@ -from flask import request -import requests -import datetime -import json - - -# get_weather retrieves weather data for a list of coordinate pairs -def get_weather(self): - # Extract coordinates form json data in POST request - try: - content_length = int(self.headers['Content-Length']) - post_data = self.rfile.read(content_length) - request_data = json.loads(post_data.decode('utf-8')) - coordinates = request_data['coords'] - - except KeyError: # Coords key not found in request - self.send_response(400) - self.send_header("Content-type", "application/json") - self.end_headers() - response_message = {"error": "No 'coords' provided in request body"} - self.wfile.write(json.dumps(response_message).encode('utf-8')) - return - - except Exception as e: - self.send_response(500) - self.send_header("Content-type", "application/json") - self.end_headers() - response_message = {"error": "Failed to parse request body"} - self.wfile.write(json.dumps(response_message).encode('utf-8')) - return - - # Form timestamp string with correct format - # Form timestamp string with correct format - current_time = datetime.datetime.utcnow() - current_time = current_time.replace(minute=0, second=0) - current_time_str = current_time.strftime("%Y-%m-%dT%H:%M:%SZ") - - # Set User-Agent header - user_agent = "IceApp sarasdj@stud.ntnu.no" - headers = {'User-Agent': user_agent} - - # Empty list for weather data - weather_data = [] - status_code = 500 - - # Request weather data for each coordinate pair - for coord in coordinates: - lat = round(coord.get('lat'), 4) # YR API only allows for up to 4 decimal points - lng = round(coord.get('lng'), 4) - - # Construct request string and execute get request to Yr API - url = f"https://api.met.no/weatherapi/locationforecast/2.0/compact?lat={lat}&lon={lng}" - response = requests.get(url, headers=headers) - - if response.status_code == 200: - if status_code != 200: - status_code = 200 - - data = response.json() # Extract data from response - timeseries = data['properties']['timeseries'] - - # Get data for current time and append to weather_data - for entry in timeseries: - if entry['time'] == current_time_str: - data = entry['data']['instant']['details'] - temperature = data['air_temperature'] - humidity = data['relative_humidity'] - - weather_object = { - 'Latitude': lat, - 'Longitude': lng, - 'Temperature': temperature, - 'Humidity': humidity - } - # Append weather_object to weather_data list - weather_data.append(weather_object) - break - - else: # Add error message if no weather data is found or the request fails - print( - f"Request to weather API failed with status code {response.status_code}") # NB: append error message to weather_data - status_code = response.status_code - - # Set headers - self.send_response(status_code) - self.send_header("Content-type", "application/json") - self.end_headers() - - # Write weather_data to response object - self.wfile.write(json.dumps(weather_data).encode('utf-8')) diff --git a/server/sql_db/icedb b/server/database/icedb similarity index 100% rename from server/sql_db/icedb rename to server/database/icedb diff --git a/server/sql_db/schema.sql b/server/database/schema.sql similarity index 100% rename from server/sql_db/schema.sql rename to server/database/schema.sql diff --git a/server/main.py b/server/main.py index cdc9cbd873cc6535da044cfb283ce6d3f89e235b..587e72a793803f77a01316821ad9aeca80ca3cc7 100644 --- a/server/main.py +++ b/server/main.py @@ -1,11 +1,10 @@ from flask import Flask from http.server import HTTPServer, BaseHTTPRequestHandler from consts import SSL_CERT_PATH, SSL_KEY_PATH, HOST, PORT -from map.get_measurements import get_all_markers -from map.add_lake import cut_map -from map.process_lake import fetch_divided_map -from APIs.get_weather import get_weather -from map.input_new_data import input_new_Lidar_data +from map_handler.get_measurements import get_all_markers +from map_handler.add_lake import cut_map +from map_handler.process_lake import fetch_divided_map +from map_handler.input_new_data import input_new_Lidar_data import ssl import sqlite3 @@ -46,17 +45,14 @@ class IceHTTP(BaseHTTPRequestHandler): cut_map(self, 'Mjosa') def do_POST(self): - if self.path == '/get_weather_data': - get_weather(self) - - elif self.path == '/new_lidar_data': + if self.path == '/new_lidar_data': input_new_Lidar_data(self,self.cursor, 1, 'Mjosa') # hardcoded body of water must change later # Start a server on port 8443 using self defined HTTP class if __name__ == "__main__": try: # Initialize database connection - conn = sqlite3.connect('server/sql_db/icedb') + conn = sqlite3.connect('server/database/icedb') cursor = conn.cursor() # Load SSL certificate and private key diff --git a/server/map/__pycache__/add_lake.cpython-311.pyc b/server/map_handler/__pycache__/add_lake.cpython-311.pyc similarity index 71% rename from server/map/__pycache__/add_lake.cpython-311.pyc rename to server/map_handler/__pycache__/add_lake.cpython-311.pyc index 9974e9cb3e6c0dc3dc772696f6baa7f8886172d7..aad95f09ac6501960bb1a2d802fb53b39b25c9f2 100644 Binary files a/server/map/__pycache__/add_lake.cpython-311.pyc and b/server/map_handler/__pycache__/add_lake.cpython-311.pyc differ diff --git a/server/map/__pycache__/get_measurements.cpython-311.pyc b/server/map_handler/__pycache__/get_measurements.cpython-311.pyc similarity index 100% rename from server/map/__pycache__/get_measurements.cpython-311.pyc rename to server/map_handler/__pycache__/get_measurements.cpython-311.pyc diff --git a/server/map/__pycache__/get_sat_query.cpython-311.pyc b/server/map_handler/__pycache__/get_sat_query.cpython-311.pyc similarity index 100% rename from server/map/__pycache__/get_sat_query.cpython-311.pyc rename to server/map_handler/__pycache__/get_sat_query.cpython-311.pyc diff --git a/server/map/__pycache__/input_new_data.cpython-311.pyc b/server/map_handler/__pycache__/input_new_data.cpython-311.pyc similarity index 100% rename from server/map/__pycache__/input_new_data.cpython-311.pyc rename to server/map_handler/__pycache__/input_new_data.cpython-311.pyc diff --git a/server/map/__pycache__/input_new_data.cpython-39.pyc b/server/map_handler/__pycache__/input_new_data.cpython-39.pyc similarity index 100% rename from server/map/__pycache__/input_new_data.cpython-39.pyc rename to server/map_handler/__pycache__/input_new_data.cpython-39.pyc diff --git a/server/map/__pycache__/process_lake.cpython-311.pyc b/server/map_handler/__pycache__/process_lake.cpython-311.pyc similarity index 55% rename from server/map/__pycache__/process_lake.cpython-311.pyc rename to server/map_handler/__pycache__/process_lake.cpython-311.pyc index 77488b747682ac7f44bea3f34110bcd114c701c5..8ac558cf013e2b17c0de9ccb902e44d262c42df9 100644 Binary files a/server/map/__pycache__/process_lake.cpython-311.pyc and b/server/map_handler/__pycache__/process_lake.cpython-311.pyc differ diff --git a/server/map/add_lake.py b/server/map_handler/add_lake.py similarity index 97% rename from server/map/add_lake.py rename to server/map_handler/add_lake.py index e8b715481408748f66adff914f4e5f596faf0959..2d6b13120f878a7bd272644e6fef269ccd82fcad 100644 --- a/server/map/add_lake.py +++ b/server/map_handler/add_lake.py @@ -1,9 +1,6 @@ import geopandas as gpd from shapely.geometry import Polygon, LineString, MultiLineString from shapely.ops import linemerge, unary_union, polygonize -import matplotlib.pyplot as plt -import random -import math import json import os @@ -144,7 +141,7 @@ def write_json_to_file(path: str, file_name: str, json_data: dict): def get_divided_map(file_name): - geo_data = gpd.read_file("server/map/" + file_name + ".geojson") + geo_data = gpd.read_file("server/map_handler/" + file_name + ".geojson") polygon_data = geo_data[geo_data['geometry'].geom_type == 'Polygon'] polygons = [Polygon(polygon.exterior) for polygon in polygon_data['geometry']] diff --git a/server/map/get_measurements.py b/server/map_handler/get_measurements.py similarity index 100% rename from server/map/get_measurements.py rename to server/map_handler/get_measurements.py diff --git a/server/map/input_new_data.py b/server/map_handler/input_new_data.py similarity index 100% rename from server/map/input_new_data.py rename to server/map_handler/input_new_data.py diff --git a/server/lake_relations/added_lakes.txt b/server/map_handler/lake_relations/added_lakes.txt similarity index 100% rename from server/lake_relations/added_lakes.txt rename to server/map_handler/lake_relations/added_lakes.txt diff --git a/server/lake_relations/mjosa.geojson b/server/map_handler/lake_relations/mjosa.geojson similarity index 100% rename from server/lake_relations/mjosa.geojson rename to server/map_handler/lake_relations/mjosa.geojson diff --git a/server/lake_relations/mjosa_div.json b/server/map_handler/lake_relations/mjosa_div.json similarity index 100% rename from server/lake_relations/mjosa_div.json rename to server/map_handler/lake_relations/mjosa_div.json diff --git a/server/lake_relations/overpass_query.txt b/server/map_handler/lake_relations/overpass_query.txt similarity index 100% rename from server/lake_relations/overpass_query.txt rename to server/map_handler/lake_relations/overpass_query.txt diff --git a/server/map/process_lake.py b/server/map_handler/process_lake.py similarity index 81% rename from server/map/process_lake.py rename to server/map_handler/process_lake.py index b2f7b85c24eb94025c791d348457914282a4df3d..8e1025919a4a9184698b580304d8c8d9e6e52c06 100644 --- a/server/map/process_lake.py +++ b/server/map_handler/process_lake.py @@ -8,14 +8,14 @@ import json import os -# Writes contents of a map json file to the response +# Writes contents of a map_handler json file to the response def fetch_divided_map(self, file_name): self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() # Extract contents from JSON file - with open("server/lake_relations/" + file_name + "_div.json", "r") as file: + with open("server/map_handler/lake_relations/" + file_name + "_div.json", "r") as file: data = file.read() # Write contents of the JSON file to response @@ -25,7 +25,7 @@ def fetch_divided_map(self, file_name): # Returns a list of [(sub_div_id, sub_div_center)] def get_ids_and_centers(file_name): # NB buggy # Expected format: [(id, [x,y]), (id, [x,y])] - geo_data = gpd.read_file("server/lake_relations/" + file_name + "_div.json") + geo_data = gpd.read_file("server/map_handler/lake_relations/" + file_name + "_div.json") subdivisions = [] for index, row in geo_data.iterrows(): sub_div_id = row['sub_div_id']