From bdcdf65432c311d4a8b5cb7733c25e06420fdf32 Mon Sep 17 00:00:00 2001 From: Sil Klaasboer Date: Wed, 28 May 2025 15:54:31 +0200 Subject: [PATCH] initial commit to split repos --- CMakeLists.txt | 81 +++++++ Main.qml | 17 ++ android/AndroidManifest.xml | 20 ++ generate.py | 14 ++ images/Destination_location.png | Bin 0 -> 618 bytes images/centerToGPS.png | Bin 0 -> 629 bytes images/currentLocationOfWaypoint.svg | 11 + images/live_icons/bewolkt.png | Bin 0 -> 4070 bytes images/live_icons/bliksem.png | Bin 0 -> 3586 bytes images/live_icons/buien.png | Bin 0 -> 4249 bytes images/live_icons/hagel.png | Bin 0 -> 4166 bytes images/live_icons/halfbewolkt.png | Bin 0 -> 4446 bytes images/live_icons/helderenacht.png | Bin 0 -> 4245 bytes images/live_icons/lichtbewolkt.png | Bin 0 -> 4446 bytes images/live_icons/mist.png | Bin 0 -> 2959 bytes images/live_icons/nachtmist.png | Bin 0 -> 3799 bytes images/live_icons/regen.png | Bin 0 -> 4139 bytes images/live_icons/sneeuw.png | Bin 0 -> 4576 bytes images/live_icons/wolkennacht.png | Bin 0 -> 4469 bytes images/live_icons/zonnig.png | Bin 0 -> 6726 bytes images/live_icons/zwaarbewolkt.png | Bin 0 -> 4070 bytes images/start_location.png | Bin 0 -> 479 bytes images/start_location.svg | 1 + images/weather_partly_cloudy_day.png | Bin 0 -> 655 bytes images/weather_rainy.png | Bin 0 -> 554 bytes images/weather_sunny.png | Bin 0 -> 542 bytes images/weather_thunderstorm.png | Bin 0 -> 602 bytes images/weather_tsunami.png | Bin 0 -> 809 bytes main.cpp | 65 ++++++ mvc/controller/networkmanager.cpp | 42 ++++ mvc/controller/networkmanager.h | 22 ++ mvc/controller/stylecontroller.cpp | 30 +++ mvc/controller/stylecontroller.h | 59 +++++ mvc/controller/weathercontroller.cpp | 224 ++++++++++++++++++ mvc/controller/weathercontroller.h | 30 +++ mvc/data/mapdata.cpp | 42 ++++ mvc/data/mapdata.h | 46 ++++ mvc/data/waypoint.cpp | 16 ++ mvc/data/waypoint.h | 23 ++ mvc/data/weatherdata.cpp | 331 +++++++++++++++++++++++++++ mvc/data/weatherdata.h | 306 +++++++++++++++++++++++++ mvc/data/weatherdetailsdata.cpp | 84 +++++++ mvc/data/weatherdetailsdata.h | 81 +++++++ mvc/enums/weatherstatus.h | 20 ++ mvc/model/mapdatamodel.cpp | 34 +++ mvc/model/mapdatamodel.h | 22 ++ qml/CustomInputField.qml | 50 ++++ qml/HomeScreen.qml | 97 ++++++++ qml/MapView.qml | 107 +++++++++ qml/WaypointData.qml | 5 + qml/WeatherDetailsView.qml | 119 ++++++++++ resources.qrc | 28 +++ 52 files changed, 2027 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Main.qml create mode 100644 android/AndroidManifest.xml create mode 100644 generate.py create mode 100644 images/Destination_location.png create mode 100644 images/centerToGPS.png create mode 100644 images/currentLocationOfWaypoint.svg create mode 100644 images/live_icons/bewolkt.png create mode 100644 images/live_icons/bliksem.png create mode 100644 images/live_icons/buien.png create mode 100644 images/live_icons/hagel.png create mode 100644 images/live_icons/halfbewolkt.png create mode 100644 images/live_icons/helderenacht.png create mode 100644 images/live_icons/lichtbewolkt.png create mode 100644 images/live_icons/mist.png create mode 100644 images/live_icons/nachtmist.png create mode 100644 images/live_icons/regen.png create mode 100644 images/live_icons/sneeuw.png create mode 100644 images/live_icons/wolkennacht.png create mode 100644 images/live_icons/zonnig.png create mode 100644 images/live_icons/zwaarbewolkt.png create mode 100644 images/start_location.png create mode 100644 images/start_location.svg create mode 100644 images/weather_partly_cloudy_day.png create mode 100644 images/weather_rainy.png create mode 100644 images/weather_sunny.png create mode 100644 images/weather_thunderstorm.png create mode 100644 images/weather_tsunami.png create mode 100644 main.cpp create mode 100644 mvc/controller/networkmanager.cpp create mode 100644 mvc/controller/networkmanager.h create mode 100644 mvc/controller/stylecontroller.cpp create mode 100644 mvc/controller/stylecontroller.h create mode 100644 mvc/controller/weathercontroller.cpp create mode 100644 mvc/controller/weathercontroller.h create mode 100644 mvc/data/mapdata.cpp create mode 100644 mvc/data/mapdata.h create mode 100644 mvc/data/waypoint.cpp create mode 100644 mvc/data/waypoint.h create mode 100644 mvc/data/weatherdata.cpp create mode 100644 mvc/data/weatherdata.h create mode 100644 mvc/data/weatherdetailsdata.cpp create mode 100644 mvc/data/weatherdetailsdata.h create mode 100644 mvc/enums/weatherstatus.h create mode 100644 mvc/model/mapdatamodel.cpp create mode 100644 mvc/model/mapdatamodel.h create mode 100644 qml/CustomInputField.qml create mode 100644 qml/HomeScreen.qml create mode 100644 qml/MapView.qml create mode 100644 qml/WaypointData.qml create mode 100644 qml/WeatherDetailsView.qml create mode 100644 resources.qrc diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ba2e720 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,81 @@ +cmake_minimum_required(VERSION 3.16) + +project(WeatherRoutes VERSION 0.1 LANGUAGES CXX) + +# Define versioning information +set(VERSION_CODE 1) # Change this to your desired version code +set(VERSION_NAME "0.1") # Your version name + +# Include this in the Android section +set(ANDROID_PACKAGE_NAME "com.example.weatherroutes") +set(ANDROID_VERSION_CODE ${VERSION_CODE}) +set(ANDROID_VERSION_NAME ${VERSION_NAME}) + +set(CMAKE_ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml") + + +set(CMAKE_AUTOMOC ON) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(RESOURCE_FILES resources.qrc) + +find_package(Qt6 REQUIRED COMPONENTS Quick) +find_package(Qt6 REQUIRED COMPONENTS Core) +find_package(Qt6 REQUIRED COMPONENTS Location) +find_package(Qt6 REQUIRED COMPONENTS Positioning) +find_package(Qt6 REQUIRED COMPONENTS Network) + +qt_add_resources(RESOURCES_RCC "${RESOURCE_FILES}") + +qt_add_executable(appWeatherRoutes + main.cpp + ${RESOURCES_RCC} +) + +qt6_add_qml_module(appWeatherRoutes + URI WeatherRoutes + VERSION 1.0 + QML_FILES + Main.qml + SOURCES + QML_FILES + QML_FILES qml/WaypointData.qml + QML_FILES qml/WeatherDetailsView.qml + QML_FILES qml/CustomInputField.qml + QML_FILES qml/MapView.qml + QML_FILES + QML_FILES qml/HomeScreen.qml + SOURCES mvc/data/waypoint.h mvc/data/waypoint.cpp + SOURCES mvc/data/mapdata.cpp mvc/data/mapdata.h + SOURCES mvc/model/mapdatamodel.h mvc/model/mapdatamodel.cpp + SOURCES mvc/data/weatherdetailsdata.h mvc/data/weatherdetailsdata.cpp + SOURCES mvc/enums/weatherstatus.h + SOURCES mvc/data/weatherdata.h mvc/data/weatherdata.cpp + SOURCES mvc/controller/weathercontroller.h mvc/controller/weathercontroller.cpp + SOURCES mvc/controller/networkmanager.h mvc/controller/networkmanager.cpp + SOURCES mvc/controller/stylecontroller.h mvc/controller/stylecontroller.cpp +) + +target_include_directories(appWeatherRoutes + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/mvc +) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +set_target_properties(appWeatherRoutes PROPERTIES +# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appWeatherRoutes + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) +target_link_libraries(appWeatherRoutes PRIVATE Qt6::Quick Qt6::Location Qt6::Positioning) +set(CMAKE_AUTORCC ON) + +include(GNUInstallDirs) +install(TARGETS appWeatherRoutes + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/Main.qml b/Main.qml new file mode 100644 index 0000000..49e5f05 --- /dev/null +++ b/Main.qml @@ -0,0 +1,17 @@ +import QtQuick +import QtQuick.Controls +import QtLocation +import QtPositioning + +ApplicationWindow { + visible: true + + width: Qt.platform.os == "android" ? Screen.width : 360 + height: Qt.platform.os == "android" ? Screen.height : 640 + title: "Weather Routes" + HomeScreen + { + id: homeScreen + anchors.fill: parent + } +} diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000..442e26c --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/generate.py b/generate.py new file mode 100644 index 0000000..918c488 --- /dev/null +++ b/generate.py @@ -0,0 +1,14 @@ +import os + +resource_file = "resources.qrc" +image_folder = "images" + +with open(resource_file, "w") as f: + f.write('\n\n') + for root, _, files in os.walk(image_folder): + for file in files: + if file.endswith('.png'): + f.write(f' {os.path.join(image_folder, file)}\n') + f.write('\n\n') + +print(f"{resource_file} has been generated.") \ No newline at end of file diff --git a/images/Destination_location.png b/images/Destination_location.png new file mode 100644 index 0000000000000000000000000000000000000000..93c7c89ea2e2b24f5fe46b771ed7443870208a20 GIT binary patch literal 618 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAy$bLNaRt)(@Z^{LPyeFMtRr_4d`P zch6qEdk#imm2Y0W0;0D-@+A6(AFJFStvo~*^zkU1i<-3a$miA_X5g(`~3C07q5W|fZ}hTzk2iXHBkNA7huMVS3t(Qm#^MDfBgo` ze*5wj2t9xG4y5blo97?}Py`eQu|ax)Qb1+zfTqD@!Hm}k#X$2RilMq-hCtNs?w@D` z^s|3SkY6wZBNH=%hYLe z=gnWRaM9AW>$Ys&wrlUclV{FdyME{HgGW!Ey?Xuj-KWoA{``xIJa`FcG-Hyty9?ug zCf$2L&J9l&#}En0-h(fLni3e=E}9!en`}OMK`c7w{$^M0GZ$K>eA)Sbccw^^oqAnG zU)sY;FLtku#ZQ~6cdpGmsXYyc-m_XyUC|n5t#yOUY?gezb)w- z_xi545B0Sta^DDjGA&s0+>XE{zMgFD{Jo{WmBmsclCHe>l46LI6l-W)5_2!vxy{$^ zvt{p9%V`f=b}6x!JfC`^ZsYonujiVDuJ4$Z6s=e;>6`M7)7vYg{>7EB=WCMII!8=B xn8|op@j{WwQM;;Fj_*HSxK_WQWP#)#rrEny+8%96=K%VV!PC{xWt~$(69Dbhga!Zr literal 0 HcmV?d00001 diff --git a/images/currentLocationOfWaypoint.svg b/images/currentLocationOfWaypoint.svg new file mode 100644 index 0000000..4780f25 --- /dev/null +++ b/images/currentLocationOfWaypoint.svg @@ -0,0 +1,11 @@ + + + + Layer 1 + + + + + + + \ No newline at end of file diff --git a/images/live_icons/bewolkt.png b/images/live_icons/bewolkt.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc6c1925860c6aaafbadb141da591cb3454bdd5 GIT binary patch literal 4070 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 Y03L@DywJRGivR!s07*qoM6N<$f_E09@Bjb+ literal 0 HcmV?d00001 diff --git a/images/live_icons/bliksem.png b/images/live_icons/bliksem.png new file mode 100644 index 0000000000000000000000000000000000000000..1caf972ce9d0ddc3ec4ff7b523b8376dacbb0680 GIT binary patch literal 3586 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 IM6N<$f?!In+W-In literal 0 HcmV?d00001 diff --git a/images/live_icons/buien.png b/images/live_icons/buien.png new file mode 100644 index 0000000000000000000000000000000000000000..b71398c889b8e8b02f89b91cd8eb36013cc972fe GIT binary patch literal 4249 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 literal 0 HcmV?d00001 diff --git a/images/live_icons/sneeuw.png b/images/live_icons/sneeuw.png new file mode 100644 index 0000000000000000000000000000000000000000..f383019e12a0a090a19fbed54eed7a1cd0246b71 GIT binary patch literal 4576 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 KMNUMnLSTX=uDqH6 literal 0 HcmV?d00001 diff --git a/images/live_icons/wolkennacht.png b/images/live_icons/wolkennacht.png new file mode 100644 index 0000000000000000000000000000000000000000..efb579536141710f751bdfc42080cb7a4406574a GIT binary patch literal 4469 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 Ms_H$hhuOdSA8pi^oB#j- literal 0 HcmV?d00001 diff --git a/images/live_icons/zwaarbewolkt.png b/images/live_icons/zwaarbewolkt.png new file mode 100644 index 0000000000000000000000000000000000000000..725850073b69346ab18d61fdace93daaf46dd29e GIT binary patch literal 4070 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 Y0Qe6Qyn!=FF8}}l07*qoM6N<$g3-UA^Z)<= literal 0 HcmV?d00001 diff --git a/images/start_location.png b/images/start_location.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f5b489010a7881edf2b1e10879027e0d0f9de0 GIT binary patch literal 479 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAO$hJ_aRt)({Sey?O;CpT2(m?9H2(FWEIJ93ZA$oRkI<@Bw0Ehl}+ zKj9Y@6t!%d&bPHv&R5i~#~nSOdqKpN+ejhGSHW$!dadUjzKbP0l+XkKsOt#e literal 0 HcmV?d00001 diff --git a/images/start_location.svg b/images/start_location.svg new file mode 100644 index 0000000..6b79840 --- /dev/null +++ b/images/start_location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/weather_partly_cloudy_day.png b/images/weather_partly_cloudy_day.png new file mode 100644 index 0000000000000000000000000000000000000000..c481203cf238648c4a86b6c3df5f46a5c4555189 GIT binary patch literal 655 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAeF*RgaRt(=R{h_<|NqQ0|5si8 zzv|fIRr_CUJo0+!;SZ~heO$Na)3W`aRvr4h?&#-bd%mpN|7Go=ud9x{TXXQ^io>7Q z?EkcK|A&Y|GYFu zPax;Br;B5VgyhB?j zGV=ehcHW=(pCfwR*0_Ae>U@o-~e zjSiDvc{KXcu}Npe?cQ^4HcQD#xM}KcHsum?|Aw41wHdqjxG%g{^?VUyZgc(url(!E zScAltFJMx5pi{E;uz#L&<_Er=V$K@ubH0}a)Flb;Y|#@*_R>4&p0i$Q`i0hQWnPaRt)<|Nq~=|NqQ0|5si8 zKY#!KRmUE0KJ#qV{#P51yk2_v!@516*6x41{NUSF2S2Pm{9(<(_bU#4SatZrszV=F z9(WHFTn$8rKdn6QZuOz}D-XS2eGo`~SatB@%7gD$0l5c0t~&63B~ZNntZW>dTs*vj!ZNaoN`^+pW{ys7KED0| zLBUb+30XxYt!=aCY}&fx_=%IJPMV zRy%OYe!+|$|Lc|78oxJ6#^@|swBC2)%9hB&*3<`w{Yv_7xXFmdEk0TM_|qQc_b-K( z?fKv1c+9_DAYtpw<-1?}{;^|n!lK=uINdYoVTKA^|grKcpTtDJB9_Ui3Ee}B*CKfdZni)ogS@T(gum4vV8u2R|`uunwg bpDh2AXNpUly;Ev|9%1lw^>bP0l+XkKqZ~G7 literal 0 HcmV?d00001 diff --git a/images/weather_sunny.png b/images/weather_sunny.png new file mode 100644 index 0000000000000000000000000000000000000000..501dce2ecbd255929adda8183978b59c6e4a8985 GIT binary patch literal 542 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaA9S-mbaRt)<|NozP=Krdz|L5=j zzv|fI&1asi+W%_fk=IKPe^|HY)7t%Smmhq)>fnd9hd-=2_-me6q533J+TzT*#PITm*@60H_y< zi_nErFzeoC9iTTHN`m}?85mjFI5@d@cm;)JWEGVRjf~A4o!or<1A?=%bBc;fTH9vN zS+jQ2)*UBKo;rQz?By#rZr-|m@BZT_?>~L{`t#42>7>UT&H+%PJI6B zIH#wVwI`{VFFSL5nvDt1-ffS~{yC5W{Qdt|9ecd_%(GSdUu`_{dg-KzFwfD{1{co2ae7kn<`&9=&tUdf;#r}7z z_P<|q@coKIA66ayuC4xjzyJJYJK=s7Xewipx4R4De;xpXte7Z<2 zrR<1~$)$-Y+(te{?(=n@cpNWYRd?&{p0CC1%Ow&dJyU{1Z132;jLr7k|Htb7QA4LG z3pte&wS6U5-0)If5grpdeJNA)Du&eP%tz`bIZHX;U3}arBsbxl#yqQr&kjJ(F?hQA KxvXi<>89&bMLY}Ni(8;`tRa^mg!{qNTu{k;6h>*WXEuHE~7`TqB-4t`jB_`{n0pH>|F zyynom6^Gxi+W&sd!S^c;eOLt&|FHVtrQhDG#P{GQBK#>otf#w_pB9Ka; z8nEEUl?OkrI{0xVTp3tu^}&xoqgEaO=>jSSDu5x7UJ!zcLu8T213;BXTy$kH?Z|3y zvn{?FfP%H4B*-tAfsvV&jgyOqmrp=UN>*M$QAtBnPv5}M$k@cn+Q!x0$Jaj~Bs?NE zE+H{FB`qT-x3H+Vq_nKOxuvtGw|~ND~jfIXqn)LnI`( z9*i_+N@QSrP_CtUY}(pmMk)6M1K3Z zbxP5L*?zs-II=uXv+3@fsw8@4LRn_s{m(n(tZY|oJT`ry;JuxVMzudKxf^!``!QG~ z$jo1&oyTB#ih;@Mu^oHP-kL0iRV9av)&(~RJQZu;nXT|cSap)Xr4Yu9m;?N*=VVF~ z8u}-Onm0^4vZaNe?d0~95cSBL@%}HLd{txFwBY5ICHk3$llWCyCcJ90*=6#SeV5wd zm iJ=14Y9d|Ikiap!JW2$;>yb&;f7(8A5T-G@yGywpT56*-D literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..bcd7367 --- /dev/null +++ b/main.cpp @@ -0,0 +1,65 @@ +#include "controller/networkmanager.h" +#include "controller/weathercontroller.h" +#include "data/mapdata.h" +#include "data/weatherdetailsdata.h" +#include "controller/weathercontroller.h" +#include "controller/networkmanager.h" +#include "enums/weatherstatus.h" +#include +#include +#include +#include + +int main(int argc, char *argv[]) { +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) + qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); +#endif + + QGuiApplication app(argc, argv); + + QLocationPermission permission; + permission.setAccuracy(QLocationPermission::Precise); + permission.setAvailability(QLocationPermission::WhenInUse); + switch (qApp->checkPermission(permission)) { + case Qt::PermissionStatus::Undetermined: + qApp->requestPermission(permission, [](const QPermission &permission) { + if (permission.status() == Qt::PermissionStatus::Granted) { + qDebug() << "Permission granted !!"; + } + }); + case Qt::PermissionStatus::Granted: + case Qt::PermissionStatus::Denied: + break; + } + + QQmlApplicationEngine engine; + + MapData mapData; + mapData.setDefaultZoomLevel(15); + mapData.setZoomLevel(mapData.defaultZoomLevel()); + mapData.setGpsUpdateInterval(1000); + engine.rootContext()->setContextProperty("mapData", &mapData); + + WeatherController ctrl; + std::shared_ptr weatherCntrl = + std::make_shared(); + ctrl.parseJSONData(); + + WeatherDetailsData weatherDetailsData(weatherCntrl); + engine.rootContext()->setContextProperty("weatherDetailsData", + &weatherDetailsData); + engine.rootContext()->setContextProperty("networkManager", + NetworkManager::Instance()); + + const QUrl url(QStringLiteral("qrc:/WeatherRoutes/Main.qml")); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/mvc/controller/networkmanager.cpp b/mvc/controller/networkmanager.cpp new file mode 100644 index 0000000..16afa88 --- /dev/null +++ b/mvc/controller/networkmanager.cpp @@ -0,0 +1,42 @@ +#include "networkmanager.h" +#include "QStandardPaths" +#include +#include +#include +#include +#include + +NetworkManager *NetworkManager::m_instance; + +NetworkManager::NetworkManager(QObject *parent) : QObject{parent} {} + +NetworkManager *NetworkManager::Instance() { + if (m_instance != nullptr) { + return m_instance; + } + m_instance = new NetworkManager(); + return m_instance; +} + +void NetworkManager::fetchJSONfromAPI(QUrl url, QJsonDocument &doc) +{ + qDebug() << "Fetch URL::" << url.toDisplayString(); + QNetworkAccessManager m_manager; + QNetworkRequest request(url); + QNetworkReply *reply = m_manager.get(request); + QEventLoop loop; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + if (reply->error() == QNetworkReply::NoError) + { + QByteArray response = reply->readAll(); + doc = QJsonDocument::fromJson(response); + } + else + { + qDebug() << "Invalid JSON response:: " << reply->errorString(); + } + + reply->deleteLater(); +} + diff --git a/mvc/controller/networkmanager.h b/mvc/controller/networkmanager.h new file mode 100644 index 0000000..7c7c1f3 --- /dev/null +++ b/mvc/controller/networkmanager.h @@ -0,0 +1,22 @@ +#ifndef NETWORKMANAGER_H +#define NETWORKMANAGER_H + +#include + +class NetworkManager : public QObject { + Q_OBJECT +public: + void fetchJSONfromAPI(QUrl url, QJsonDocument &doc); + static NetworkManager *m_instance; + static NetworkManager *Instance(); + + NetworkManager(const NetworkManager&) = delete; + NetworkManager& operator=(const NetworkManager&) = delete; + +signals: + +private: + explicit NetworkManager(QObject *parent = nullptr); +}; + +#endif // NETWORKMANAGER_H diff --git a/mvc/controller/stylecontroller.cpp b/mvc/controller/stylecontroller.cpp new file mode 100644 index 0000000..15be671 --- /dev/null +++ b/mvc/controller/stylecontroller.cpp @@ -0,0 +1,30 @@ +#include "stylecontroller.h" + +StyleController* StyleController::m_instance; + +StyleController *StyleController::Instance() +{ + if(m_instance != nullptr) + { + return m_instance; + } + m_instance = new StyleController(); + return m_instance; +} + +StyleController::StyleController(QObject *parent) + : QObject{parent} +{} + +int StyleController::colorMode() const +{ + return m_colorMode; +} + +void StyleController::setColorMode(int newColorMode) +{ + if (m_colorMode == newColorMode) + return; + m_colorMode = newColorMode; + emit colorModeChanged(); +} diff --git a/mvc/controller/stylecontroller.h b/mvc/controller/stylecontroller.h new file mode 100644 index 0000000..29c6d44 --- /dev/null +++ b/mvc/controller/stylecontroller.h @@ -0,0 +1,59 @@ +#ifndef STYLECONTROLLER_H +#define STYLECONTROLLER_H + +#include +#include + +class StyleController : public QObject +{ + Q_OBJECT +public: + static StyleController* m_instance; + StyleController(const StyleController&) = delete; + StyleController& operator=(const StyleController&) = delete; + StyleController* Instance(); + + enum COLOR_MODE + { + DARK = 0, + LIGHT, + SYSTEM + } + Q_ENUMS(COLOR_MODE); + + + Q_PROPERTY(int colorMode READ colorMode WRITE setColorMode NOTIFY colorModeChanged FINAL) + + //Dark Mode + Q_PROPERTY(QColor textPrimary READ textPrimary NOTIFY colorChanged FINAL) + QColor textPrimary() const {return QColor(255,255,255);} + + Q_PROPERTY(QColor textSecondary READ textSecondary NOTIFY colorChanged FINAL) + QColor textSecondary() const {return QColor(255,255,255,179);} + + Q_PROPERTY(QColor textDisabled READ textDisabled NOTIFY colorChanged FINAL) + QColor textDisabled() const {return QColor(255,255,255,125);} + + Q_PROPERTY(QColor buttonActive READ buttonActive NOTIFY colorChanged FINAL) + QColor buttonActive() const {return QColor(255,255,255);} + + Q_PROPERTY(QColor buttonDisabled READ buttonDisabled NOTIFY colorChanged FINAL) + QColor buttonDisabled() const {return QColor(255,255,255, 77);} + + Q_PROPERTY(QColor buttonSelected READ buttonSelected NOTIFY colorChanged FINAL) + QColor buttonSelected() const {return QColor(255,255,255, 40);} + + int colorMode() const; + void setColorMode(int newColorMode); + + +signals: + void colorChanged(); + void colorModeChanged(); + +private: + explicit StyleController(QObject *parent = nullptr); + int m_colorMode = COLOR_MODE::DARK; +}; + +#endif // STYLECONTROLLER_H diff --git a/mvc/controller/weathercontroller.cpp b/mvc/controller/weathercontroller.cpp new file mode 100644 index 0000000..1446db7 --- /dev/null +++ b/mvc/controller/weathercontroller.cpp @@ -0,0 +1,224 @@ +#include "weathercontroller.h" +#include "QDir" +#include "QFile" +#include "QGuiApplication" +#include "QStandardPaths" +#include "networkmanager.h" +#include +#include +#include +#include + +const QString WeatherController::FILENAME = "static.json"; + +WeatherController::WeatherController(QObject *parent) : QObject{parent} {} + +bool WeatherController::parseJSONData() { + QJsonDocument doc; + if (this->checkToDownload(doc)) { + QJsonObject jsonObject = doc.object(); + QJsonArray dataArray = jsonObject["data"].toArray(); + for (QJsonArray::const_iterator iter = dataArray.constBegin(); + iter != dataArray.constEnd(); ++iter) { + QJsonObject weatherObject = (*iter).toObject(); + + // Extract values from the JSON object + long tijd = weatherObject["tijd"].toString().toLong(); + QString tijd_nl = weatherObject["tijd_nl"].toString(); + int offset = weatherObject["offset"].toInt(); + float temp = weatherObject["temp"].toString().toFloat(); + int wind_ms = weatherObject["windb"].toInt(); + int wind_bf = weatherObject["winds"].toInt(); + int wind_knp = weatherObject["windknp"].toInt(); + int wind_kmh = weatherObject["windkmh"].toString().toFloat(); + int wind_r = weatherObject["windr"].toInt(); + QString wind_ltr = weatherObject["windrltr"].toString(); + int visibility = weatherObject["vis"].toInt(); + int neersl = weatherObject["neersl"].toString().toFloat() * + 10; // Converted from 0.1 to 1 decimal scale + float luchtd_bar = weatherObject["luchtd"].toString().toFloat(); + float luchtdmmhg = weatherObject["luchtdmmhg"].toString().toFloat(); + float luchtdinHg = weatherObject["luchtdinhg"].toString().toFloat(); + int hw = weatherObject["hw"].toInt(); + int mw = weatherObject["mw"].toInt(); + int lw = weatherObject["lw"].toInt(); + int tw = weatherObject["tw"].toInt(); + int rv = weatherObject["rv"].toInt(); + int gr = weatherObject["gr"].toInt(); + int gr_w = weatherObject["gr_w"].toInt(); + QString cape = weatherObject["cape"].toString(); + int snd = weatherObject["snd"].toInt(); + int snv = weatherObject["snv"].toInt(); + int cond = weatherObject["cond"].toInt(); + int iconCode = weatherObject["ico"].toInt(); + QString sameenv = weatherObject["samenv"].toString(); + QString icoon = weatherObject["icoon"].toString(); + + // Create WeatherData object and add it to the list using initializer list + // constructor + mData.append(WeatherData(tijd, tijd_nl, offset, temp, wind_ms, wind_bf, + wind_knp, wind_kmh, wind_r, wind_ltr, visibility, + neersl, luchtd_bar, luchtdmmhg, luchtdinHg, hw, + mw, lw, tw, rv, gr, gr_w, cape, snd, snv, cond, + iconCode, sameenv, icoon)); + } + } + else + return false; + + return true; +} + +QJsonObject WeatherController::fetchCurrentWeatherData(const QGeoCoordinate &coord) +{ + QJsonDocument doc; + readWeatherData(doc, "liveweer"); + QJsonArray liveweerArray = doc.array(); + QJsonObject firstEntry = liveweerArray.at(0).toObject(); + + //Will fetch live data when the time difference is more than 10 mins from last fetch + if (qAbs(QDateTime::currentSecsSinceEpoch() - firstEntry["timestamp"].toString().toUInt()) > 600) + { + // NetworkManager::Instance()->fetchJSONfromAPI(prepareURL(coord), doc); + // if(!doc.isNull() && doc.isObject()) + // { + // updateWeatherData(doc); + // } + } + return doc.object(); +} + +bool WeatherController::readWeatherData(QJsonDocument &doc, QString objName) +{ + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + QString fileName = cacheDir + "/" + WeatherController::FILENAME; + + QFile file(fileName); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&file); + QJsonParseError error; + doc = QJsonDocument::fromJson(in.readAll().toStdString().c_str(), &error); + file.close(); + if(doc.isNull()) + { + qWarning() << "Error:: " << error.errorString(); + return false; + } + + if (!doc.isObject()) { + qWarning() << "Invalid JSON format"; + return false; + } + if(!objName.isNull()) + { + QJsonArray ary = doc.object()[objName].toArray(); + QJsonDocument temp_doc(ary); + doc = temp_doc; + } + } + return true; +} + +bool WeatherController::saveWeatherData(QJsonDocument &doc) +{ + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + // Ensure the directory exists + QDir dir(cacheDir); + if (!dir.exists()) { + dir.mkpath(cacheDir); + } + + QString fileName = cacheDir + "/" + WeatherController::FILENAME; + + QFile file(fileName); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&file); + out << doc.toJson(); + file.close(); + qDebug() << "Data saved to hidden cache file."; + return true; + } else { + qDebug() << "Failed to open file for writing"; + return false; + } +} + +bool WeatherController::fetchWeatherData(QUrl url, QJsonDocument &doc) +{ + NetworkManager::Instance()->fetchJSONfromAPI(url, doc); + if (doc.isObject() && !doc.isNull()) { + QJsonObject jsonObject = doc.object(); + + QString todayDate = QDate::currentDate().toString("dd-MM-yyyy"); + + jsonObject["downloaddatum"] = todayDate; + + QString timeOfDownload = QDateTime::currentDateTime().toString("hh:mm:ss"); + + jsonObject["downloadtime"] = timeOfDownload; + + QJsonDocument updatedDoc(jsonObject); + saveWeatherData(updatedDoc); + } + return true; +} + +void WeatherController::updateWeatherData(QJsonDocument &inputDoc) +{ + QJsonDocument doc; + readWeatherData(doc); + QJsonObject rootObj = doc.object(); + QJsonObject inputObj = inputDoc.object(); + QJsonArray liveweerArray = inputObj["liveweer"].toArray(); + rootObj["liveweer"] = liveweerArray; + + QJsonDocument updatedDoc(rootObj); + saveWeatherData(updatedDoc); +} + +QUrl WeatherController::prepareURL(const QString &plaats) +{ + return QUrl(QString("http://data.meteoserver.nl/api/uurverwachting.php?locatie=%1&key=785f0630f0").arg(plaats)); +} + +QUrl WeatherController::prepareURL(const QGeoCoordinate &coord) +{ + return QUrl(QString("http://data.meteoserver.nl/api/liveweer_synop.php?lat=%1&long=%2&key=785f0630f0&select=1").arg(coord.latitude()).arg(coord.longitude())); +} + +bool WeatherController::checkToDownload(QJsonDocument &doc) +{ + bool toDownloadData = false; + readWeatherData(doc); + + if (doc.isObject() && !doc.isNull()) { + + // Get the root object + QJsonObject jsonObject = doc.object(); + + // Extract the 'downloaddatum' value + QString downloaddatum = jsonObject.value("downloaddatum").toString(); + if(downloaddatum != QDate::currentDate().toString("dd-MM-yyyy")) + toDownloadData = true; + + QJsonArray plaatsnaamArray = jsonObject.value("plaatsnaam").toArray(); + + if (!plaatsnaamArray.isEmpty()) { + QJsonObject plaatsObject = plaatsnaamArray.at(0).toObject(); + QString plaats = plaatsObject.value("plaats").toString(); + if(plaats != "Raalte") + toDownloadData = true; + } else { + qWarning() << "plaatsnaam array is empty"; + return false; + } + + } else { + qWarning() << "Failed to open file for reading"; + return false; + } + if(toDownloadData) + return fetchWeatherData(prepareURL("Raalte"), doc); + return false; +} + diff --git a/mvc/controller/weathercontroller.h b/mvc/controller/weathercontroller.h new file mode 100644 index 0000000..2faed60 --- /dev/null +++ b/mvc/controller/weathercontroller.h @@ -0,0 +1,30 @@ +#ifndef WEATHERCONTROLLER_H +#define WEATHERCONTROLLER_H + +#include "../data/weatherdata.h" +#include +#include + +class WeatherController : public QObject { + Q_OBJECT +public: + explicit WeatherController(QObject *parent = nullptr); + + static const QString FILENAME; + bool parseJSONData(); + QJsonObject fetchCurrentWeatherData(const QGeoCoordinate &coord); + +signals: + +private: + bool readWeatherData(QJsonDocument &doc, QString objName = NULL); + bool checkToDownload(QJsonDocument &doc); + bool saveWeatherData(QJsonDocument &doc); + bool fetchWeatherData(QUrl url, QJsonDocument &doc); + void updateWeatherData(QJsonDocument &inputDoc); + QUrl prepareURL(const QString &plaats); + QUrl prepareURL(const QGeoCoordinate &coord); + QList mData; +}; + +#endif // WEATHERCONTROLLER_H diff --git a/mvc/data/mapdata.cpp b/mvc/data/mapdata.cpp new file mode 100644 index 0000000..3b28f26 --- /dev/null +++ b/mvc/data/mapdata.cpp @@ -0,0 +1,42 @@ +#include "mapdata.h" + +MapData::MapData(QObject *parent) : QObject{parent} {} + +QList> MapData::waypoints() const { + return m_waypoints; +} + +void MapData::setWaypoints( + const QList> &newWaypoints) { + if (m_waypoints == newWaypoints) + return; + m_waypoints = newWaypoints; + emit waypointsChanged(); +} + +int MapData::zoomLevel() const { return m_zoomLevel; } + +void MapData::setZoomLevel(int newZoomLevel) { + if (m_zoomLevel == newZoomLevel) + return; + m_zoomLevel = newZoomLevel; + emit zoomLevelChanged(); +} + +int MapData::gpsUpdateInterval() const { return m_gpsUpdateInterval; } + +void MapData::setGpsUpdateInterval(int newGpsUpdateInterval) { + if (m_gpsUpdateInterval == newGpsUpdateInterval) + return; + m_gpsUpdateInterval = newGpsUpdateInterval; + emit gpsUpdateIntervalChanged(); +} + +int MapData::defaultZoomLevel() const { return m_defaultZoomLevel; } + +void MapData::setDefaultZoomLevel(int newDefaultZoomLevel) { + if (m_defaultZoomLevel == newDefaultZoomLevel) + return; + m_defaultZoomLevel = newDefaultZoomLevel; + emit defaultZoomLevelChanged(); +} diff --git a/mvc/data/mapdata.h b/mvc/data/mapdata.h new file mode 100644 index 0000000..4ed2629 --- /dev/null +++ b/mvc/data/mapdata.h @@ -0,0 +1,46 @@ +#ifndef MAPDATA_H +#define MAPDATA_H + +#include "waypoint.h" +#include + +class MapData : public QObject { + Q_OBJECT +public: + explicit MapData(QObject *parent = nullptr); + + Q_PROPERTY(QList> waypoints READ waypoints WRITE + setWaypoints NOTIFY waypointsChanged FINAL) + Q_PROPERTY(int zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY + zoomLevelChanged FINAL) + Q_PROPERTY(int gpsUpdateInterval READ gpsUpdateInterval WRITE + setGpsUpdateInterval NOTIFY gpsUpdateIntervalChanged FINAL) + Q_PROPERTY(int defaultZoomLevel READ defaultZoomLevel NOTIFY + defaultZoomLevelChanged FINAL) + + QList> waypoints() const; + void setWaypoints(const QList> &newWaypoints); + + int zoomLevel() const; + void setZoomLevel(int newZoomLevel); + + int gpsUpdateInterval() const; + void setGpsUpdateInterval(int newGpsUpdateInterval); + + int defaultZoomLevel() const; + void setDefaultZoomLevel(int newDefaultZoomLevel); + +signals: + void waypointsChanged(); + void zoomLevelChanged(); + void gpsUpdateIntervalChanged(); + void defaultZoomLevelChanged(); + +private: + QList> m_waypoints; + int m_zoomLevel; + int m_gpsUpdateInterval; + int m_defaultZoomLevel; +}; + +#endif // MAPDATA_H diff --git a/mvc/data/waypoint.cpp b/mvc/data/waypoint.cpp new file mode 100644 index 0000000..924cbd9 --- /dev/null +++ b/mvc/data/waypoint.cpp @@ -0,0 +1,16 @@ +#include "waypoint.h" + +Waypoint::Waypoint(QObject *parent) : QObject{parent} {} + +Waypoint::Waypoint(const double latitude, const double longitude) + : m_latitude(latitude), m_longitude(longitude), m_altitude(0) {} + +Waypoint::Waypoint(const double latitude, const double longitude, + WeatherData &data) + : m_latitude(latitude), m_longitude(longitude), m_altitude(0), + m_data(data) {} + +Waypoint::Waypoint(const double latitude, const double longitude, + const double altitude, WeatherData &data) + : m_latitude(latitude), m_longitude(longitude), m_altitude(altitude), + m_data(data) {} diff --git a/mvc/data/waypoint.h b/mvc/data/waypoint.h new file mode 100644 index 0000000..017b6fe --- /dev/null +++ b/mvc/data/waypoint.h @@ -0,0 +1,23 @@ +#ifndef WAYPOINT_H +#define WAYPOINT_H + +#include "weatherdata.h" +#include + +class Waypoint : public QObject { + Q_OBJECT +public: + explicit Waypoint(QObject *parent = nullptr); + Waypoint(const double latitude, const double longitude); + Waypoint(const double latitude, const double longitude, WeatherData &data); + Waypoint(const double latitude, const double longitude, const double altitude, + WeatherData &data); + +private: + double m_latitude; + double m_longitude; + double m_altitude; + WeatherData m_data; +}; + +#endif // WAYPOINT_H diff --git a/mvc/data/weatherdata.cpp b/mvc/data/weatherdata.cpp new file mode 100644 index 0000000..0529db9 --- /dev/null +++ b/mvc/data/weatherdata.cpp @@ -0,0 +1,331 @@ +#include "weatherdata.h" + +WeatherData::WeatherData(QObject *parent) : QObject{parent} {} + +WeatherData::WeatherData(long tijd, const QString &tijd_nl, int offset, + float temp, int wind_ms, int wind_bf, int wind_knp, + int wind_kmh, int wind_r, const QString &wind_ltr, + int visibility, int neersl, float luchtd_bar, + float luchtdmmhg, float luchtdinHg, int hw, int mw, + int lw, int tw, int rv, int gr, int gr_w, + const QString &cape, int snd, int snv, int cond, + int iconCode, const QString &sameenv, + const QString &icoon) + : m_tijd(tijd), m_tijd_nl(tijd_nl), m_offset(offset), m_temp(temp), + m_wind_ms(wind_ms), m_wind_bf(wind_bf), m_wind_knp(wind_knp), + m_wind_kmh(wind_kmh), m_wind_r(wind_r), m_wind_ltr(wind_ltr), + m_visibility(visibility), m_neersl(neersl), m_luchtd_bar(luchtd_bar), + m_luchtdmmhg(luchtdmmhg), m_luchtdinHg(luchtdinHg), m_hw(hw), m_mw(mw), + m_lw(lw), m_tw(tw), m_rv(rv), m_gr(gr), m_gr_w(gr_w), m_cape(cape), + m_snd(snd), m_snv(snv), m_cond(cond), m_iconCode(iconCode), + m_sameenv(sameenv), m_icoon(icoon) {} + +WeatherData::WeatherData(const WeatherData &other) + : m_tijd(other.m_tijd), m_tijd_nl(other.m_tijd_nl), + m_offset(other.m_offset), m_temp(other.m_temp), + m_wind_ms(other.m_wind_ms), m_wind_bf(other.m_wind_bf), + m_wind_knp(other.m_wind_knp), m_wind_kmh(other.m_wind_kmh), + m_wind_r(other.m_wind_r), m_wind_ltr(other.m_wind_ltr), + m_visibility(other.m_visibility), m_neersl(other.m_neersl), + m_luchtd_bar(other.m_luchtd_bar), m_luchtdmmhg(other.m_luchtdmmhg), + m_luchtdinHg(other.m_luchtdinHg), m_hw(other.m_hw), m_mw(other.m_mw), + m_lw(other.m_lw), m_tw(other.m_tw), m_rv(other.m_rv), m_gr(other.m_gr), + m_gr_w(other.m_gr_w), m_cape(other.m_cape), m_snd(other.m_snd), + m_snv(other.m_snv), m_cond(other.m_cond), m_iconCode(other.m_iconCode), + m_sameenv(other.m_sameenv), m_icoon(other.m_icoon) {} + +WeatherData WeatherData::operator=(const WeatherData &other) { + m_tijd = (other.m_tijd); + m_tijd_nl = (other.m_tijd_nl); + m_offset = (other.m_offset); + m_temp = (other.m_temp); + m_wind_ms = (other.m_wind_ms); + m_wind_bf = (other.m_wind_bf); + m_wind_knp = (other.m_wind_knp); + m_wind_kmh = (other.m_wind_kmh); + m_wind_r = (other.m_wind_r); + m_wind_ltr = (other.m_wind_ltr); + m_visibility = (other.m_visibility); + m_neersl = (other.m_neersl); + m_luchtd_bar = (other.m_luchtd_bar); + m_luchtdmmhg = (other.m_luchtdmmhg); + m_luchtdinHg = (other.m_luchtdinHg); + m_hw = (other.m_hw); + m_mw = (other.m_mw); + m_lw = (other.m_lw); + m_tw = (other.m_tw); + m_rv = (other.m_rv); + m_gr = (other.m_gr); + m_gr_w = (other.m_gr_w); + m_cape = (other.m_cape); + m_snd = (other.m_snd); + m_snv = (other.m_snv); + m_cond = (other.m_cond); + m_iconCode = (other.m_iconCode); + m_sameenv = (other.m_sameenv); + m_icoon = (other.m_icoon); + return *this; +} + +long WeatherData::tijd() const { return m_tijd; } + +void WeatherData::setTijd(long newTijd) { + if (m_tijd == newTijd) + return; + m_tijd = newTijd; + emit tijdChanged(); +} + +QString WeatherData::tijd_nl() const { return m_tijd_nl; } + +void WeatherData::setTijd_nl(const QString &newTijd_nl) { + if (m_tijd_nl == newTijd_nl) + return; + m_tijd_nl = newTijd_nl; + emit tijd_nlChanged(); +} + +int WeatherData::offset() const { return m_offset; } + +void WeatherData::setOffset(int newOffset) { + if (m_offset == newOffset) + return; + m_offset = newOffset; + emit offsetChanged(); +} + +float WeatherData::temp() const { return m_temp; } + +void WeatherData::setTemp(float newTemp) { + if (qFuzzyCompare(m_temp, newTemp)) + return; + m_temp = newTemp; + emit tempChanged(); +} + +int WeatherData::wind_ms() const { return m_wind_ms; } + +void WeatherData::setWind_ms(int newWind_ms) { + if (m_wind_ms == newWind_ms) + return; + m_wind_ms = newWind_ms; + emit wind_msChanged(); +} + +int WeatherData::wind_bf() const { return m_wind_bf; } + +void WeatherData::setWind_bf(int newWind_bf) { + if (m_wind_bf == newWind_bf) + return; + m_wind_bf = newWind_bf; + emit wind_bfChanged(); +} + +int WeatherData::wind_knp() const { return m_wind_knp; } + +void WeatherData::setWind_knp(int newWind_knp) { + if (m_wind_knp == newWind_knp) + return; + m_wind_knp = newWind_knp; + emit wind_knpChanged(); +} + +int WeatherData::wind_kmh() const { return m_wind_kmh; } + +void WeatherData::setWind_kmh(int newWind_kmh) { + if (m_wind_kmh == newWind_kmh) + return; + m_wind_kmh = newWind_kmh; + emit wind_kmhChanged(); +} + +int WeatherData::wind_r() const { return m_wind_r; } + +void WeatherData::setWind_r(int newWind_r) { + if (m_wind_r == newWind_r) + return; + m_wind_r = newWind_r; + emit wind_rChanged(); +} + +QString WeatherData::wind_ltr() const { return m_wind_ltr; } + +void WeatherData::setWind_ltr(const QString &newWind_ltr) { + if (m_wind_ltr == newWind_ltr) + return; + m_wind_ltr = newWind_ltr; + emit wind_ltrChanged(); +} + +int WeatherData::visibility() const { return m_visibility; } + +void WeatherData::setVisibility(int newVisibility) { + if (m_visibility == newVisibility) + return; + m_visibility = newVisibility; + emit visibilityChanged(); +} + +int WeatherData::neersl() const { return m_neersl; } + +void WeatherData::setNeersl(int newNeersl) { + if (m_neersl == newNeersl) + return; + m_neersl = newNeersl; + emit neerslChanged(); +} + +float WeatherData::luchtd_bar() const { return m_luchtd_bar; } + +void WeatherData::setLuchtd_bar(float newLuchtd_bar) { + if (qFuzzyCompare(m_luchtd_bar, newLuchtd_bar)) + return; + m_luchtd_bar = newLuchtd_bar; + emit luchtd_barChanged(); +} + +float WeatherData::luchtdmmhg() const { return m_luchtdmmhg; } + +void WeatherData::setLuchtdmmhg(float newLuchtdmmhg) { + if (qFuzzyCompare(m_luchtdmmhg, newLuchtdmmhg)) + return; + m_luchtdmmhg = newLuchtdmmhg; + emit luchtdmmhgChanged(); +} + +float WeatherData::luchtdinHg() const { return m_luchtdinHg; } + +void WeatherData::setLuchtdinHg(float newLuchtdinHg) { + if (qFuzzyCompare(m_luchtdinHg, newLuchtdinHg)) + return; + m_luchtdinHg = newLuchtdinHg; + emit luchtdinHgChanged(); +} + +int WeatherData::hw() const { return m_hw; } + +void WeatherData::setHw(int newHw) { + if (m_hw == newHw) + return; + m_hw = newHw; + emit hwChanged(); +} + +int WeatherData::mw() const { return m_mw; } + +void WeatherData::setMw(int newMw) { + if (m_mw == newMw) + return; + m_mw = newMw; + emit mwChanged(); +} + +int WeatherData::lw() const { return m_lw; } + +void WeatherData::setLw(int newLw) { + if (m_lw == newLw) + return; + m_lw = newLw; + emit lwChanged(); +} + +int WeatherData::tw() const { return m_tw; } + +void WeatherData::setTw(int newTw) { + if (m_tw == newTw) + return; + m_tw = newTw; + emit twChanged(); +} + +int WeatherData::rv() const { return m_rv; } + +void WeatherData::setRv(int newRv) { + if (m_rv == newRv) + return; + m_rv = newRv; + emit rvChanged(); +} + +int WeatherData::gr() const { return m_gr; } + +void WeatherData::setGr(int newGr) { + if (m_gr == newGr) + return; + m_gr = newGr; + emit grChanged(); +} + +int WeatherData::gr_w() const { return m_gr_w; } + +void WeatherData::setGr_w(int newGr_w) { + if (m_gr_w == newGr_w) + return; + m_gr_w = newGr_w; + emit gr_wChanged(); +} + +QString WeatherData::cape() const { return m_cape; } + +void WeatherData::setCape(const QString &newCape) { + if (m_cape == newCape) + return; + m_cape = newCape; + emit capeChanged(); +} + +int WeatherData::snd() const { return m_snd; } + +void WeatherData::setSnd(int newSnd) { + if (m_snd == newSnd) + return; + m_snd = newSnd; + emit sndChanged(); +} + +int WeatherData::snv() const { return m_snv; } + +void WeatherData::setSnv(int newSnv) { + if (m_snv == newSnv) + return; + m_snv = newSnv; + emit snvChanged(); +} + +int WeatherData::cond() const { return m_cond; } + +void WeatherData::setCond(int newCond) { + if (m_cond == newCond) + return; + m_cond = newCond; + emit condChanged(); +} + +int WeatherData::iconCode() const { return m_iconCode; } + +void WeatherData::setIconCode(int newIconCode) { + if (m_iconCode == newIconCode) + return; + m_iconCode = newIconCode; + emit iconCodeChanged(); +} + +QString WeatherData::sameenv() const { return m_sameenv; } + +void WeatherData::setSameenv(const QString &newSameenv) { + if (m_sameenv == newSameenv) + return; + m_sameenv = newSameenv; + emit sameenvChanged(); +} + +QString WeatherData::icoon() const { return m_icoon; } + +void WeatherData::setIcoon(const QString &newIcoon) { + if (m_icoon == newIcoon) + return; + m_icoon = newIcoon; + emit icoonChanged(); +} + +void WeatherData::print() {} diff --git a/mvc/data/weatherdata.h b/mvc/data/weatherdata.h new file mode 100644 index 0000000..f6a1795 --- /dev/null +++ b/mvc/data/weatherdata.h @@ -0,0 +1,306 @@ +#ifndef WEATHERDATA_H +#define WEATHERDATA_H + +#include + +class WeatherData : public QObject { + Q_OBJECT + +public: + enum ICON_NAME { + regen, + bewolkt, + halfbewolkt, + lichtbewolkt, + nachtbewolkt, + wolkennacht, + zonnig, + helderenacht + }; + Q_ENUM(ICON_NAME) + + enum WEATHER_COND { + Regen, + Bewolkt, + Halfbewolkt, + Lichtbewolkt, + Zonnig, + Helder + }; + Q_ENUM(WEATHER_COND) + + enum WIND_DIR { + Noord, + NNO, + NO, + ONO, + Oost, + OZO, + ZO, + ZZO, + Zuid, + ZZW, + ZW, + WZW, + West, + WNW, + NW, + NNW + }; + Q_ENUM(WIND_DIR) + + explicit WeatherData(QObject *parent = nullptr); + + WeatherData(long tijd, const QString &tijd_nl, int offset, float temp, + int wind_ms, int wind_bf, int wind_knp, int wind_kmh, int wind_r, + const QString &wind_ltr, int visibility, int neersl, + float luchtd_bar, float luchtdmmhg, float luchtdinHg, int hw, + int mw, int lw, int tw, int rv, int gr, int gr_w, + const QString &cape, int snd, int snv, int cond, int iconCode, + const QString &sameenv, const QString &icoon); + + WeatherData(const WeatherData &other); + WeatherData operator=(WeatherData const &other); + + Q_PROPERTY(long tijd READ tijd WRITE setTijd NOTIFY tijdChanged + FINAL) // DateTime in Timestamp + Q_PROPERTY(QString tijd_nl READ tijd_nl WRITE setTijd_nl NOTIFY tijd_nlChanged + FINAL) // DateTime is Human readable format + Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged + FINAL) // Offset is the last reference hour point of the + // model's predicted weather data + Q_PROPERTY(float temp READ temp WRITE setTemp NOTIFY tempChanged + FINAL) // temp in Celsius + Q_PROPERTY(int wind_ms READ wind_ms WRITE setWind_ms NOTIFY wind_msChanged + FINAL) // Wind avg speed in m/s + Q_PROPERTY(int wind_bf READ wind_bf WRITE setWind_bf NOTIFY wind_bfChanged + FINAL) // Wind avg speed in Beaufort + Q_PROPERTY(int wind_knp READ wind_knp WRITE setWind_knp NOTIFY wind_knpChanged + FINAL) // Wind avg speed in knots + Q_PROPERTY(int wind_kmh READ wind_kmh WRITE setWind_kmh NOTIFY wind_kmhChanged + FINAL) // Wind avg speed in k/h + Q_PROPERTY(int wind_r READ wind_r WRITE setWind_r NOTIFY wind_rChanged + FINAL) // Wind direction in degrees + Q_PROPERTY( + QString wind_ltr READ wind_ltr WRITE setWind_ltr NOTIFY wind_ltrChanged + FINAL) // Wind directions in Letters like N,S,E,W + Q_PROPERTY(int visibility READ visibility WRITE setVisibility NOTIFY + visibilityChanged FINAL) // Visibility in meters + Q_PROPERTY(int neersl READ neersl WRITE setNeersl NOTIFY neerslChanged + FINAL) // Precipitation in nml + Q_PROPERTY(float luchtd_bar READ luchtd_bar WRITE setLuchtd_bar NOTIFY + luchtd_barChanged FINAL) // Air pressure in mbar + Q_PROPERTY(float luchtdmmhg READ luchtdmmhg WRITE setLuchtdmmhg NOTIFY + luchtdmmhgChanged FINAL) // Air pressure in mmHg + Q_PROPERTY(float luchtdinHg READ luchtdinHg WRITE setLuchtdinHg NOTIFY + luchtdinHgChanged FINAL) // Air pressure in inHg + Q_PROPERTY(int hw READ hw WRITE setHw NOTIFY hwChanged + FINAL) //% of Higher Cloud desnsity + Q_PROPERTY(int mw READ mw WRITE setMw NOTIFY mwChanged + FINAL) // % of medium cloud density + Q_PROPERTY(int lw READ lw WRITE setLw NOTIFY lwChanged + FINAL) // % of low cloud density + Q_PROPERTY(int tw READ tw WRITE setTw NOTIFY twChanged + FINAL) // % avg of all cloud density + Q_PROPERTY( + int rv READ rv WRITE setRv NOTIFY rvChanged FINAL) // Reletive Humidity + Q_PROPERTY(int gr READ gr WRITE setGr NOTIFY grChanged + FINAL) // Expected Sun radiation J/cm2 + Q_PROPERTY(int gr_w READ gr_w WRITE setGr_w NOTIFY gr_wChanged + FINAL) // Expected Sun radiation watts/m2 + Q_PROPERTY(QString cape READ cape WRITE setCape NOTIFY capeChanged + FINAL) // Not gonna use it + Q_PROPERTY(int snd READ snd WRITE setSnd NOTIFY sndChanged + FINAL) // Available snow deck in mm + Q_PROPERTY(int snv READ snv WRITE setSnv NOTIFY snvChanged + FINAL) // Actual snowfall in mm + Q_PROPERTY(int cond READ cond WRITE setCond NOTIFY condChanged + FINAL) // Cond is condition code is weather type like sunny, + // rainy, etc + Q_PROPERTY(int iconCode READ iconCode WRITE setIconCode NOTIFY iconCodeChanged + FINAL) // Icon code of the weather + Q_PROPERTY(QString sameenv READ sameenv WRITE setSameenv NOTIFY sameenvChanged + FINAL) // Name of the current weather type + Q_PROPERTY(QString icoon READ icoon WRITE setIcoon NOTIFY icoonChanged + FINAL) // Icon name of the current weather + + long tijd() const; + void setTijd(long newTijd); + + QString tijd_nl() const; + void setTijd_nl(const QString &newTijd_nl); + + int offset() const; + void setOffset(int newOffset); + + float temp() const; + void setTemp(float newTemp); + + int wind_ms() const; + void setWind_ms(int newWind_ms); + + int wind_bf() const; + void setWind_bf(int newWind_bf); + + int wind_knp() const; + void setWind_knp(int newWind_knp); + + int wind_kmh() const; + void setWind_kmh(int newWind_kmh); + + int wind_r() const; + void setWind_r(int newWind_r); + + QString wind_ltr() const; + void setWind_ltr(const QString &newWind_ltr); + + int visibility() const; + void setVisibility(int newVisibility); + + int neersl() const; + void setNeersl(int newNeersl); + + float luchtd_bar() const; + void setLuchtd_bar(float newLuchtd_bar); + + float luchtdmmhg() const; + void setLuchtdmmhg(float newLuchtdmmhg); + + float luchtdinHg() const; + void setLuchtdinHg(float newLuchtdinHg); + + int hw() const; + void setHw(int newHw); + + int mw() const; + void setMw(int newMw); + + int lw() const; + void setLw(int newLw); + + int tw() const; + void setTw(int newTw); + + int rv() const; + void setRv(int newRv); + + int gr() const; + void setGr(int newGr); + + int gr_w() const; + void setGr_w(int newGr_w); + + QString cape() const; + void setCape(const QString &newCape); + + int snd() const; + void setSnd(int newSnd); + + int snv() const; + void setSnv(int newSnv); + + int cond() const; + void setCond(int newCond); + + int iconCode() const; + void setIconCode(int newIconCode); + + QString sameenv() const; + void setSameenv(const QString &newSameenv); + + QString icoon() const; + void setIcoon(const QString &newIcoon); + + void print(); + +signals: + void tijdChanged(); + void tijd_nlChanged(); + + void offsetChanged(); + + void tempChanged(); + + void wind_msChanged(); + + void wind_bfChanged(); + + void wind_knpChanged(); + + void wind_kmhChanged(); + + void wind_rChanged(); + + void wind_ltrChanged(); + + void visibilityChanged(); + + void neerslChanged(); + + void luchtd_barChanged(); + + void luchtdmmhgChanged(); + + void luchtdinHgChanged(); + + void hwChanged(); + + void mwChanged(); + + void lwChanged(); + + void twChanged(); + + void rvChanged(); + + void grChanged(); + + void gr_wChanged(); + + void capeChanged(); + + void sndChanged(); + + void snvChanged(); + + void condChanged(); + + void iconCodeChanged(); + + void sameenvChanged(); + + void icoonChanged(); + +private: + long m_tijd; + QString m_tijd_nl; + int m_offset; + float m_temp; + int m_wind_ms; + int m_wind_bf; + int m_wind_knp; + int m_wind_kmh; + int m_wind_r; + QString m_wind_ltr; + int m_visibility; + int m_neersl; + float m_luchtd_bar; + float m_luchtdmmhg; + float m_luchtdinHg; + int m_hw; + int m_mw; + int m_lw; + int m_tw; + int m_rv; + int m_gr; + int m_gr_w; + QString m_cape; + int m_snd; + int m_snv; + int m_cond; + int m_iconCode; + QString m_sameenv; + QString m_icoon; +}; + +#endif // WEATHERDATA_H diff --git a/mvc/data/weatherdetailsdata.cpp b/mvc/data/weatherdetailsdata.cpp new file mode 100644 index 0000000..04e993e --- /dev/null +++ b/mvc/data/weatherdetailsdata.cpp @@ -0,0 +1,84 @@ +#include "weatherdetailsdata.h" +#include +#include + +WeatherDetailsData::WeatherDetailsData(QObject *parent) : QObject{parent} {} + +WeatherDetailsData::WeatherDetailsData(std::shared_ptr &ctrl) + : m_localWeatherCtrl(ctrl) {} + +QString WeatherDetailsData::weatherStatus() const { return m_weatherStatus; } + +void WeatherDetailsData::setWeatherStatus(QString newWeatherStatus) { + if (m_weatherStatus == newWeatherStatus) + return; + m_weatherStatus = newWeatherStatus; + emit weatherStatusChanged(); +} + +double WeatherDetailsData::temperature() const { return m_temperature; } + +void WeatherDetailsData::setTemperature(double newTemperature) { + if (qFuzzyCompare(m_temperature, newTemperature)) + return; + m_temperature = newTemperature; + emit temperatureChanged(); +} + +double WeatherDetailsData::humidity() const { return m_humidity; } + +void WeatherDetailsData::setHumidity(double newHumidity) { + if (qFuzzyCompare(m_humidity, newHumidity)) + return; + m_humidity = newHumidity; + emit humidityChanged(); +} + +double WeatherDetailsData::windSpeed() const { return m_windSpeed; } + +void WeatherDetailsData::setWindSpeed(double newWindSpeed) { + if (qFuzzyCompare(m_windSpeed, newWindSpeed)) + return; + m_windSpeed = newWindSpeed; + emit windSpeedChanged(); +} + +double WeatherDetailsData::precipitation() const { return m_precipitation; } + +void WeatherDetailsData::setPrecipitation(double newPrecipitation) { + if (qFuzzyCompare(m_precipitation, newPrecipitation)) + return; + m_precipitation = newPrecipitation; + emit precipitationChanged(); +} + +void WeatherDetailsData::populate(double temperature, double humidity, + double windSpeed, double precipitation, + QString weatherStatus) { + setTemperature(temperature); + setHumidity(humidity); + setWindSpeed(windSpeed); + setPrecipitation(precipitation); + setWeatherStatus(weatherStatus); +} + +void WeatherDetailsData::populate(QGeoCoordinate coord) { + QJsonObject obj = m_localWeatherCtrl->fetchCurrentWeatherData(coord); + QJsonArray liveweerArray = obj["liveweer"].toArray(); + QJsonObject firstEntry = liveweerArray.at(0).toObject(); + QString temp = verifyForDash(firstEntry["temp"].toString()); + QString humidity = verifyForDash(firstEntry["lv"].toString()); + QString windSpeed = verifyForDash(firstEntry["windkmh"].toString()); + QString precipitation = verifyForDash(firstEntry["neerslagints"].toString()); + QString weatherStatus = verifyForDash(firstEntry["image"].toString()); + populate(temp.toDouble(), humidity.toDouble(), windSpeed.toDouble(), + precipitation.toDouble(), weatherStatus); +} + +QString WeatherDetailsData::getImgSrc(QString status) { + return QString("qrc:/images/live_icons/%1.png").arg(status); +} + +QString WeatherDetailsData::verifyForDash(QString data) { + return data == "-" ? "0" : data; +} diff --git a/mvc/data/weatherdetailsdata.h b/mvc/data/weatherdetailsdata.h new file mode 100644 index 0000000..674d0b8 --- /dev/null +++ b/mvc/data/weatherdetailsdata.h @@ -0,0 +1,81 @@ +#ifndef WEATHERDETAILSDATA_H +#define WEATHERDETAILSDATA_H + +#include +#include "../controller/weathercontroller.h" +#include "enums/weatherstatus.h" +#include + +class WeatherDetailsData : public QObject { + Q_OBJECT +public: + enum LIVE_ICON_NAME { + zonnig, + bliksem, + regen, + buien, + hagel, + mist, + sneeuw, + bewolkt, + halfbewolkt, + zwaarbewolkt, + lichtbewolkt, + nachtmist, + helderenacht, + wolkennacht + }; + Q_ENUM(LIVE_ICON_NAME) + + explicit WeatherDetailsData(QObject *parent = nullptr); + WeatherDetailsData(std::shared_ptr &ctrl); + + Q_PROPERTY(double temperature READ temperature WRITE setTemperature NOTIFY + temperatureChanged FINAL) + Q_PROPERTY(double humidity READ humidity WRITE setHumidity NOTIFY + humidityChanged FINAL) + Q_PROPERTY(double windSpeed READ windSpeed WRITE setWindSpeed NOTIFY + windSpeedChanged FINAL) + Q_PROPERTY(double precipitation READ precipitation WRITE setPrecipitation + NOTIFY precipitationChanged FINAL) + Q_PROPERTY(QString weatherStatus READ weatherStatus WRITE setWeatherStatus + NOTIFY weatherStatusChanged FINAL) + + QString weatherStatus() const; + void setWeatherStatus(QString newWeatherStatus); + + double temperature() const; + void setTemperature(double newTemperature); + + double humidity() const; + void setHumidity(double newHumidity); + + double windSpeed() const; + void setWindSpeed(double newWindSpeed); + + double precipitation() const; + void setPrecipitation(double newPrecipitation); + + void populate(double temperature, double humidity, double windSpeed, + double precipitation, QString weatherStatus); + Q_INVOKABLE void populate(QGeoCoordinate coord); + Q_INVOKABLE QString getImgSrc(QString status); + +signals: + void weatherStatusChanged(); + void temperatureChanged(); + void humidityChanged(); + void windSpeedChanged(); + void precipitationChanged(); + +private: + QString m_weatherStatus = "regen"; + double m_temperature = 0.0; + double m_humidity = 0.0; + double m_windSpeed = 0.0; + double m_precipitation = 0.0; + std::shared_ptr m_localWeatherCtrl; + QString verifyForDash(QString data); +}; + +#endif // WEATHERDETAILSDATA_H diff --git a/mvc/enums/weatherstatus.h b/mvc/enums/weatherstatus.h new file mode 100644 index 0000000..e21c9cc --- /dev/null +++ b/mvc/enums/weatherstatus.h @@ -0,0 +1,20 @@ +#ifndef WEATHERSTATUS_H +#define WEATHERSTATUS_H + +#include + +enum WeatherStatus : uint8_t { + UNKNOWN = 0, + SUNNY, + PARTLY_CLOUDY, + CLOUDY, + RAINY, + SNOWING, + HAILSTORM, + FROZED_TO_DEAD, + THUNDERSTORM, + TSUNAMI + +}; + +#endif // WEATHERSTATUS_H diff --git a/mvc/model/mapdatamodel.cpp b/mvc/model/mapdatamodel.cpp new file mode 100644 index 0000000..30a7bea --- /dev/null +++ b/mvc/model/mapdatamodel.cpp @@ -0,0 +1,34 @@ +// #include "mapdatamodel.h" + +// MapDataModel::MapDataModel(QObject *parent) {} + +// int MapDataModel::rowCount(const QModelIndex &parent) const +// { +// return mDatas.size(); +// } + +// int MapDataModel::columnCount(const QModelIndex &parent) const +// { + +// } + +// QVariant MapDataModel::data(const QModelIndex &index, int role) const +// { +// if (!index.isValid()) +// return QVariant(); +// if ( role == Qt::DisplayRole) +// { +// if ( index.column() == 0) +// return mDatas[index.row()].waypoints(); +// if ( index.column() == 1) +// return mDatas[index.row()].longitude(); +// if ( index.column() == 2) +// return mDatas[index.row()].altitude(); +// } +// return QVariant(); +// } + +// void MapDataModel::populate() +// { + +// } diff --git a/mvc/model/mapdatamodel.h b/mvc/model/mapdatamodel.h new file mode 100644 index 0000000..b126a0e --- /dev/null +++ b/mvc/model/mapdatamodel.h @@ -0,0 +1,22 @@ +// #ifndef MAPDATAMODEL_H +// #define MAPDATAMODEL_H + +// #include +// #include +// #include "../data/mapdata.h" + +// class MapDataModel : public QAbstractItemModel +// { +// Q_OBJECT +// public: +// MapDataModel(QObject * parent = 0); +// int rowCount(const QModelIndex& parent = QModelIndex()) const; +// int columnCount(const QModelIndex& parent = QModelIndex()) const; +// QVariant data(const QModelIndex &index, int role) const; +// void populate(); + +// private: +// QList mDatas; +// }; + +// #endif // MAPDATAMODEL_H diff --git a/qml/CustomInputField.qml b/qml/CustomInputField.qml new file mode 100644 index 0000000..d75bfcd --- /dev/null +++ b/qml/CustomInputField.qml @@ -0,0 +1,50 @@ +import QtQuick +import QtQuick.Controls + +Rectangle { + property alias logoSrc: logoImage.source + property alias placeHolderText: textInput.placeholderText + property alias text: textInput.text + + Rectangle + { + id: logoRect + width: parent.width * 0.1 + height: parent.height + anchors.left: parent.left + anchors.top: parent.top + anchors.verticalCenter: parent.verticalCenter + color: "transparent" + Image + { + id: logoImage + anchors.fill: parent + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + z: parent.z + 1 + } + } + + Rectangle + { + id: inputRect + width: parent.width * 0.9 + height: parent.height + anchors.left: logoRect.right + anchors.top: parent.top + anchors.verticalCenter: parent.verticalCenter + color: "transparent" + radius: parent.radius + TextField + { + id: textInput + anchors.fill: parent + onPressed: textInput.forceActiveFocus() + background: Rectangle{ + width: inputRect.width + height:inputRect.height + radius: inputRect.radius + } + } + } +} diff --git a/qml/HomeScreen.qml b/qml/HomeScreen.qml new file mode 100644 index 0000000..8a81c38 --- /dev/null +++ b/qml/HomeScreen.qml @@ -0,0 +1,97 @@ +import QtQuick +import QtQuick.Controls + +Rectangle { + id: root + anchors.fill: parent + border.color: "black" + border.width: 2 + color: "transparent" + readonly property int radius: width * 0.025 + + Component.onCompleted: + { + mapView.positionSource.start() + weatherDetailsData.populate(mapView.positionSource.position.coordinate) + } + + CustomInputField + { + id: inputFrom + width: parent.width - (2 * anchors.margins) + height: parent.height * 0.05 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.margins: parent.width * 0.01 + anchors.topMargin: parent.width * 0.02 + logoSrc: "qrc:/images/start_location.svg" + placeHolderText: "Start" + z: mapView.z + 1 + radius: root.radius + } + + CustomInputField + { + id: inputTo + width: parent.width - (2 * anchors.margins) + height: parent.height * 0.05 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: inputFrom.bottom + anchors.margins: parent.width * 0.01 + logoSrc: "qrc:/images/Destination_location.png" + placeHolderText: "Destination" + z: mapView.z + 1 + radius: root.radius + } + + MapView + { + id: mapView + anchors.fill: parent + radius: root.radius + } + + Button + { + id: myLocationButton + width: parent.width * 0.1 + height: width + anchors.right: parent.right + anchors.bottom: weatherDetails.top + anchors.margins: parent.width * 0.01 + background: Rectangle + { + anchors.fill: parent + color:"darkgray" + radius: width/2 + Image + { + source: "qrc:/images/centerToGPS.png" + fillMode: Image.PreserveAspectFit + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + onClicked: + { + mapView.resetZoomLevel() + } + z: mapView.z + 1 + } + + WeatherDetailsView + { + id: weatherDetails + width: parent.width + height: parent.height * 0.1 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + z: mapView.z + 1 + radius: root.radius + imgSrc: weatherDetailsData.getImgSrc(weatherDetailsData.weatherStatus) + temperature: weatherDetailsData.temperature + precipitation: weatherDetailsData.precipitation + windSpeed: weatherDetailsData.windSpeed + humidity: weatherDetailsData.humidity + } +} diff --git a/qml/MapView.qml b/qml/MapView.qml new file mode 100644 index 0000000..e734eae --- /dev/null +++ b/qml/MapView.qml @@ -0,0 +1,107 @@ +import QtQuick +import QtLocation +import QtPositioning + +Rectangle { + property alias positionSource: positionSource + property variant waypoints: mapData.waypoints + + function resetZoomLevel() + { + map.zoomLevel = mapData.defaultZoomLevel + map.bearing = 0; + positionSource.start() + } + + Plugin { + id: mapPlugin + name: "osm" + PluginParameter { + name: "osm.mapping.providersrepository.disabled" + value: "true" + } + PluginParameter { + name: "osm.mapping.providersrepository.address" + value: "http://maps-redirect.qt.io/osm/5.6/" + } + } + + PositionSource + { + id: positionSource + updateInterval: mapData.gpsUpdateInterval + active: true + onPositionChanged: { + var coord = positionSource.position.coordinate; + map.visible = true; + map.center = positionSource.position.coordinate; + } + } + + MapQuickItem { + id: marker + zoomLevel: map.zoomLevel + anchorPoint.x: image.width/2 + anchorPoint.y: image.height + coordinate: positionSource.position.coordinate + + sourceItem: Image { + id: image + height: 40 + width: height*2/3 + source: "qrc:/images/currentLocationOfWaypoint.svg" + transform: Rotation { origin.x: marker.anchorPoint.x; origin.y: marker.anchorPoint.y; angle: map.bearing} + } + Component.onCompleted: map.addMapItem(marker) + } + + Map { + id: map + anchors.fill: parent + plugin: mapPlugin + center: QtPositioning.coordinate(59.91, 10.75) // Oslo + zoomLevel: mapData.zoomLevel + property geoCoordinate startCentroid + + PinchHandler { + id: pinch + target: null + onActiveChanged: if (active) { + map.startCentroid = map.toCoordinate(pinch.centroid.position, false) + } + onScaleChanged: (delta) => { + map.zoomLevel += Math.log2(delta) + map.alignCoordinateToPoint(map.startCentroid, pinch.centroid.position) + } + onRotationChanged: (delta) => { + map.bearing -= delta + map.alignCoordinateToPoint(map.startCentroid, pinch.centroid.position) + } + grabPermissions: PointerHandler.TakeOverForbidden + } + WheelHandler { + id: wheel + acceptedDevices: Qt.platform.pluginName === "cocoa" || Qt.platform.pluginName === "wayland" + ? PointerDevice.Mouse | PointerDevice.TouchPad + : PointerDevice.Mouse + rotationScale: 1/120 + property: "zoomLevel" + } + DragHandler { + id: drag + target: null + onTranslationChanged: (delta) => map.pan(-delta.x, -delta.y) + // onGrabChanged: positionSource.active = false + } + Shortcut { + enabled: map.zoomLevel < map.maximumZoomLevel + sequence: StandardKey.ZoomIn + onActivated: map.zoomLevel = Math.round(map.zoomLevel + 1) + } + Shortcut { + enabled: map.zoomLevel > map.minimumZoomLevel + sequence: StandardKey.ZoomOut + onActivated: map.zoomLevel = Math.round(map.zoomLevel - 1) + } + } +} diff --git a/qml/WaypointData.qml b/qml/WaypointData.qml new file mode 100644 index 0000000..68c2108 --- /dev/null +++ b/qml/WaypointData.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + +} diff --git a/qml/WeatherDetailsView.qml b/qml/WeatherDetailsView.qml new file mode 100644 index 0000000..44c55ba --- /dev/null +++ b/qml/WeatherDetailsView.qml @@ -0,0 +1,119 @@ +import QtQuick + +Rectangle { + id: root + property alias imgSrc: image.source + property alias temperature: tempValue.text + property alias humidity: humidityValue.text + property alias precipitation: precipValue.text + property alias windSpeed: windValue.text + + property double rightMargin: root.width * 0.05 + + Rectangle + { + id: imageRect + width: parent.width * 0.2 + height: parent.height + anchors.left: parent.left + anchors.top: parent.top + radius: 15 + color: "black" + Image + { + id: image + fillMode: Image.PreserveAspectFit + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + Rectangle + { + id: tempRect + width: parent.width * 0.4 + height: parent.height * 0.5 + anchors.left: imageRect.right + anchors.top: parent.top + Text + { + id: tempLabel + text: "Temp (C):" + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + } + Text + { + id: tempValue + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: root.rightMargin + } + } + Rectangle + { + id: humidityRect + width: parent.width * 0.4 + height: parent.height * 0.5 + anchors.left: tempRect.right + anchors.top: parent.top + Text + { + id: humidityLabel + text: "Humidity (%):" + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + } + Text + { + id: humidityValue + anchors.right: parent.right + anchors.rightMargin: root.rightMargin + anchors.verticalCenter: parent.verticalCenter + } + } + Rectangle + { + id: precipRect + width: parent.width * 0.4 + height: parent.height * 0.5 + anchors.left: imageRect.right + anchors.top: tempRect.bottom + Text + { + id: precipLabel + text: "Precipitation (mm)" + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + } + Text + { + id: precipValue + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: root.rightMargin + } + } + Rectangle + { + id: windRect + width: parent.width * 0.4 + height: parent.height * 0.5 + anchors.left: tempRect.right + anchors.top: humidityRect.bottom + Text + { + id: windLabel + text: "Wind (km/h):" + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + } + Text + { + id: windValue + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: root.rightMargin + } + } +} diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..5aa5ed1 --- /dev/null +++ b/resources.qrc @@ -0,0 +1,28 @@ + + + images/Destination_location.png + images/start_location.png + images/weather_partly_cloudy_day.png + images/weather_rainy.png + images/weather_sunny.png + images/weather_thunderstorm.png + images/weather_tsunami.png + images/start_location.svg + images/centerToGPS.png + images/currentLocationOfWaypoint.svg + images/live_icons/bewolkt.png + images/live_icons/bliksem.png + images/live_icons/buien.png + images/live_icons/hagel.png + images/live_icons/halfbewolkt.png + images/live_icons/helderenacht.png + images/live_icons/lichtbewolkt.png + images/live_icons/mist.png + images/live_icons/nachtmist.png + images/live_icons/regen.png + images/live_icons/sneeuw.png + images/live_icons/wolkennacht.png + images/live_icons/zonnig.png + images/live_icons/zwaarbewolkt.png + +