diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index acc93a1..b9cd36f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -52,6 +52,9 @@ jobs: - name: Run linters run: tox -e linters + - name: Run tests + run: tox -e unit-tests + typecheck: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 016a067..744363f 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,5 @@ build/* venv *~ cache -.buildozer bin .idea/* diff --git a/Makefile b/Makefile index ed44567..1989bbd 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,5 @@ install: run: gweatherrouting -run-kivy: - python3 setup.kivy.py install - gweatherrouting-kivy - build-standalone: nuitka3 gweatherrouting/main.py --follow-imports --follow-stdlib diff --git a/README.md b/README.md index 3bc8fd2..0c3529b 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ <!-- [![PyPI version](https://badge.fury.io/py/gweatherrouting.svg)](https://badge.fury.io/py/gweatherrouting) --> GWeatherRouting is an opensource sailing route calculator written in python and: -- Gtk3 for the desktop version (Linux/Mac/Windows) -- Kivy for the mobile version (iOS/Android) [Read here](README.mobile.md) +- Gtk3 for the desktop version (Linux/Mac/*BSD/Windows) ![Routing in progress](https://github.com/dakk/gweatherrouting/raw/master/media/s3.png) @@ -40,7 +39,6 @@ GWeatherRouting is an opensource sailing route calculator written in python and: - Ortodromic track render - AIS rendering - Boat realtime dashboard -- Mobile version ## Installation diff --git a/README.mobile.md b/README.mobile.md deleted file mode 100644 index c74f6d0..0000000 --- a/README.mobile.md +++ /dev/null @@ -1,22 +0,0 @@ -# Gweatherrouting Mobile - -Mobile version is far from behind useful. Currently we are experimenting with kivy framework, and we're able to create a working APK with a map. - -![Mobile](https://github.com/dakk/gweatherrouting/raw/master/media/mobile.png) - -## Building for android - -Install buildozer: - -``` -git clone https://github.com/kivy/buildozer.git -cd buildozer -sudo python setup.py install -``` - - -Create an apk: - -``` -rm -rf .buildozer/android/platform/build-armeabi-v7a/build/python-installs/gweatherrouting/gweatherrouting* && buildozer android debug deploy run && buildozer android logcat | grep python -``` \ No newline at end of file diff --git a/android_recipes/eccodes/__init__.py b/android_recipes/eccodes/__init__.py deleted file mode 100644 index 9aa1cc3..0000000 --- a/android_recipes/eccodes/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# isort:skip_file -from pythonforandroid.recipe import Recipe -from pythonforandroid.logger import shprint -from pythonforandroid.util import ( - current_directory, - ensure_dir, - BuildInterruptingException, -) -from multiprocessing import cpu_count -from os.path import join -import sh - - -class EcccodesRecipe(Recipe): - version = "2.23.0" - built_libraries = {"libeccodes.so": "build/lib"} - url = "https://confluence.ecmwf.int/download/attachments/45757960/eccodes-{version}-Source.tar.gz" # ?api=v2' - need_stl_shared = True - patches = ["patches/log2.patch"] - - def prebuild_arch(self, arch): - self.apply_patches(arch) - - def get_recipe_env(self, arch): - env = super().get_recipe_env(arch) - return env - - def build_arch(self, arch): - source_dir = self.get_build_dir(arch.arch) - build_target = join(source_dir, "build") - install_target = join(build_target, "install") - - ensure_dir(build_target) - with current_directory(build_target): - env = self.get_recipe_env(arch) - shprint(sh.rm, "-rf", "CMakeFiles/", "CMakeCache.txt", _env=env) - shprint( - sh.cmake, - source_dir, - "-DIEEE_LE=1", - "-DIEEE_BE=1", - "-DENABLE_TESTS=OFF", - "-DENABLE_EXTRA_TESTS=OFF", - "-DENABLE_BUILD_TOOLS=OFF", - "-DENABLE_FORTRAN=OFF", - "-DENABLE_NETCDF=OFF", - "-DDISABLE_OS_CHECK=ON", - "-DCMAKE_SYSTEM_NAME=Android", - "-DCMAKE_POSITION_INDEPENDENT_CODE=1", - "-DCMAKE_ANDROID_ARCH_ABI={arch}".format(arch=arch.arch), - "-DCMAKE_ANDROID_NDK=" + self.ctx.ndk_dir, - "-DCMAKE_BUILD_TYPE=Release", - "-DCMAKE_INSTALL_PREFIX={}".format(install_target), - "-DANDROID_ABI={arch}".format(arch=arch.arch), - "-DBUILD_SHARED_LIBS=ON", - _env=env, - ) - shprint(sh.make, "-j" + str(cpu_count()), _env=env) - shprint(sh.make, "install", _env=env) - - -recipe = EcccodesRecipe() diff --git a/android_recipes/eccodes/patches/log2.patch b/android_recipes/eccodes/patches/log2.patch deleted file mode 100644 index 3356868..0000000 --- a/android_recipes/eccodes/patches/log2.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff -ur "eccodes-2.23.0-Source (copy 1)/src/grib_optimize_decimal_factor.c" eccodes-2.23.0-Source/src/grib_optimize_decimal_factor.c ---- "eccodes-2.23.0-Source (copy 1)/src/grib_optimize_decimal_factor.c" 2021-08-25 11:35:41.000000000 +0200 -+++ eccodes-2.23.0-Source/src/grib_optimize_decimal_factor.c 2021-11-12 11:20:43.331305877 +0100 -@@ -28,9 +28,7 @@ - return (int)(log(DBL_MAX) / log(10)) - 1; - } - --#ifdef ECCODES_ON_WINDOWS - #define log2(a) (log(a) / 1.44269504088896340736) --#endif - - static void factec(int* krep, const double pa, const int knbit, const long kdec, const int range, long* ke, int* knutil) - { diff --git a/buildozer.spec b/buildozer.spec deleted file mode 100644 index 08239a8..0000000 --- a/buildozer.spec +++ /dev/null @@ -1,416 +0,0 @@ -[app] - -# (str) Title of your application -title = GWeatherRouting - -# (str) Package name -package.name = gweatherrouting - -# (str) Package domain (needed for android/ios packaging) -package.domain = org.gweatherrouting - -# (str) Source code where the main.py live -source.dir = ./gweatherroutingkivy - -# (list) Source files to include (let empty to include all the files) -source.include_exts = py,png,jpg,kv,atlas,geojson,pol - -# (list) List of inclusions using pattern matching -#source.include_patterns = assets/*,images/*.png - -# (list) Source files to exclude (let empty to not exclude anything) -#source.exclude_exts = spec - -# (list) List of directory to exclude (let empty to not exclude anything) -#source.exclude_dirs = tests, bin, venv - -# (list) List of exclusions using pattern matching -#source.exclude_patterns = license,images/*/*.jpg - -# (str) Application versioning (method 1) -version = 0.1 - -# (str) Application versioning (method 2) -# version.regex = __version__ = ['"](.*)['"] -# version.filename = %(source.dir)s/main.py - -# (list) Application requirements -# comma separated e.g. requirements = sqlite3,kivy -requirements = python3,kivy,numpy,attrs,cffi,findlibs,eccodes,weatherrouting==0.1.6,colorlog,mapview,latlon3,pyproj,geojson_utils,PIL,requests,urllib3,chardet,idna,geojson,gpxpy,kivymd,bs4,pyserial,pynmea2,git+git://github.com/dakk/gweatherrouting@master#egg=gweatherrouting,git+git://github.com/ecmwf/eccodes-python@master#egg=eccodes -# (str) Custom source folders for requirements -# Sets custom source for any requirements with recipes -# requirements.source.kivy = ../../kivy -# requirements.source.gweatherrouting=../gweatherrouting - -# (str) Presplash of the application -#presplash.filename = %(source.dir)s/data/presplash.png - -# (str) Icon of the application -#icon.filename = %(source.dir)s/data/icon.png - -# (str) Supported orientation (one of landscape, sensorLandscape, portrait or all) -orientation = portrait - -# (list) List of service to declare -#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY - -# -# OSX Specific -# - -# -# author = © Copyright Info - -# change the major version of python used by the app -osx.python_version = 3 - -# Kivy version to use -osx.kivy_version = 1.9.1 - -# -# Android specific -# - -# (bool) Indicate if the application should be fullscreen or not -fullscreen = 0 - -# (string) Presplash background color (for android toolchain) -# Supported formats are: #RRGGBB #AARRGGBB or one of the following names: -# red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray, -# darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy, -# olive, purple, silver, teal. -#android.presplash_color = #FFFFFF - -# (string) Presplash animation using Lottie format. -# see https://lottiefiles.com/ for examples and https://airbnb.design/lottie/ -# for general documentation. -# Lottie files can be created using various tools, like Adobe After Effect or Synfig. -#android.presplash_lottie = "path/to/lottie/file.json" - -# (str) Adaptive icon of the application (used if Android API level is 26+ at runtime) -#icon.adaptive_foreground.filename = %(source.dir)s/data/icon_fg.png -#icon.adaptive_background.filename = %(source.dir)s/data/icon_bg.png - -# (list) Permissions -android.permissions = INTERNET - -# (list) features (adds uses-feature -tags to manifest) -#android.features = android.hardware.usb.host - -# (int) Target Android API, should be as high as possible. -#android.api = 27 - -# (int) Minimum API your APK will support. -#android.minapi = 21 - -# (int) Android SDK version to use -#android.sdk = 20 - -# (str) Android NDK version to use -#android.ndk = 19b - -# (int) Android NDK API to use. This is the minimum API your app will support, it should usually match android.minapi. -#android.ndk_api = 21 - -# (bool) Use --private data storage (True) or --dir public storage (False) -#android.private_storage = True - -# (str) Android NDK directory (if empty, it will be automatically downloaded.) -#android.ndk_path = - -# (str) Android SDK directory (if empty, it will be automatically downloaded.) -#android.sdk_path = - -# (str) ANT directory (if empty, it will be automatically downloaded.) -#android.ant_path = - -# (bool) If True, then skip trying to update the Android sdk -# This can be useful to avoid excess Internet downloads or save time -# when an update is due and you just want to test/build your package -# android.skip_update = False - -# (bool) If True, then automatically accept SDK license -# agreements. This is intended for automation only. If set to False, -# the default, you will be shown the license when first running -# buildozer. -# android.accept_sdk_license = False - -# (str) Android entry point, default is ok for Kivy-based app -#android.entrypoint = org.kivy.android.PythonActivity - -# (str) Full name including package path of the Java class that implements Android Activity -# use that parameter together with android.entrypoint to set custom Java class instead of PythonActivity -#android.activity_class_name = org.kivy.android.PythonActivity - -# (str) Extra xml to write directly inside the <manifest> element of AndroidManifest.xml -# use that parameter to provide a filename from where to load your custom XML code -#android.extra_manifest_xml = ./src/android/extra_manifest.xml - -# (str) Extra xml to write directly inside the <manifest><application> tag of AndroidManifest.xml -# use that parameter to provide a filename from where to load your custom XML arguments: -#android.extra_manifest_application_arguments = ./src/android/extra_manifest_application_arguments.xml - -# (str) Full name including package path of the Java class that implements Python Service -# use that parameter to set custom Java class instead of PythonService -#android.service_class_name = org.kivy.android.PythonService - -# (str) Android app theme, default is ok for Kivy-based app -# android.apptheme = "@android:style/Theme.NoTitleBar" - -# (list) Pattern to whitelist for the whole project -#android.whitelist = - -# (str) Path to a custom whitelist file -#android.whitelist_src = - -# (str) Path to a custom blacklist file -#android.blacklist_src = - -# (list) List of Java .jar files to add to the libs so that pyjnius can access -# their classes. Don't add jars that you do not need, since extra jars can slow -# down the build process. Allows wildcards matching, for example: -# OUYA-ODK/libs/*.jar -#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar - -# (list) List of Java files to add to the android project (can be java or a -# directory containing the files) -#android.add_src = - -# (list) Android AAR archives to add -#android.add_aars = - -# (list) Gradle dependencies to add -#android.gradle_dependencies = - -# (bool) Enable AndroidX support. Enable when 'android.gradle_dependencies' -# contains an 'androidx' package, or any package from Kotlin source. -# android.enable_androidx requires android.api >= 28 -#android.enable_androidx = False - -# (list) add java compile options -# this can for example be necessary when importing certain java libraries using the 'android.gradle_dependencies' option -# see https://developer.android.com/studio/write/java8-support for further information -# android.add_compile_options = "sourceCompatibility = 1.8", "targetCompatibility = 1.8" - -# (list) Gradle repositories to add {can be necessary for some android.gradle_dependencies} -# please enclose in double quotes -# e.g. android.gradle_repositories = "maven { url 'https://kotlin.bintray.com/ktor' }" -#android.add_gradle_repositories = - -# (list) packaging options to add -# see https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html -# can be necessary to solve conflicts in gradle_dependencies -# please enclose in double quotes -# e.g. android.add_packaging_options = "exclude 'META-INF/common.kotlin_module'", "exclude 'META-INF/*.kotlin_module'" -#android.add_packaging_options = - -# (list) Java classes to add as activities to the manifest. -#android.add_activities = com.example.ExampleActivity - -# (str) OUYA Console category. Should be one of GAME or APP -# If you leave this blank, OUYA support will not be enabled -#android.ouya.category = GAME - -# (str) Filename of OUYA Console icon. It must be a 732x412 png image. -#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png - -# (str) XML file to include as an intent filters in <activity> tag -#android.manifest.intent_filters = - -# (str) launchMode to set for the main activity -#android.manifest.launch_mode = standard - -# (list) Android additional libraries to copy into libs/armeabi -#android.add_libs_armeabi = libs/android/*.so -#android.add_libs_armeabi_v7a = libs/android-v7/*.so -#android.add_libs_arm64_v8a = libs/android-v8/*.so -#android.add_libs_x86 = libs/android-x86/*.so -#android.add_libs_mips = libs/android-mips/*.so - -# (bool) Indicate whether the screen should stay on -# Don't forget to add the WAKE_LOCK permission if you set this to True -#android.wakelock = False - -# (list) Android application meta-data to set (key=value format) -#android.meta_data = - -# (list) Android library project to add (will be added in the -# project.properties automatically.) -#android.library_references = - -# (list) Android shared libraries which will be added to AndroidManifest.xml using <uses-library> tag -#android.uses_library = - -# (str) Android logcat filters to use -#android.logcat_filters = *:S python:D - -# (bool) Android logcat only display log for activity's pid -#android.logcat_pid_only = False - -# (str) Android additional adb arguments -#android.adb_args = -H host.docker.internal - -# (bool) Copy library instead of making a libpymodules.so -#android.copy_libs = 1 - -# (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86, x86_64 -android.arch = armeabi-v7a - -# (int) overrides automatic versionCode computation (used in build.gradle) -# this is not the same as app version and should only be edited if you know what you're doing -# android.numeric_version = 1 - -# (bool) enables Android auto backup feature (Android API >=23) -android.allow_backup = True - -# (str) XML file for custom backup rules (see official auto backup documentation) -# android.backup_rules = - -# (str) If you need to insert variables into your AndroidManifest.xml file, -# you can do so with the manifestPlaceholders property. -# This property takes a map of key-value pairs. (via a string) -# Usage example : android.manifest_placeholders = [myCustomUrl:\"org.kivy.customurl\"] -# android.manifest_placeholders = [:] - -# (bool) disables the compilation of py to pyc/pyo files when packaging -# android.no-compile-pyo = True - -# -# Python for android (p4a) specific -# - -# (str) python-for-android URL to use for checkout -#p4a.url = - -# (str) python-for-android fork to use in case if p4a.url is not specified, defaults to upstream (kivy) -#p4a.fork = kivy - -# (str) python-for-android branch to use, defaults to master -#p4a.branch = master - -# (str) python-for-android specific commit to use, defaults to HEAD, must be within p4a.branch -#p4a.commit = HEAD - -# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) -#p4a.source_dir = - -# (str) The directory in which python-for-android should look for your own build recipes (if any) -p4a.local_recipes = android_recipes - -# (str) Filename to the hook for p4a -#p4a.hook = - -# (str) Bootstrap to use for android builds -#p4a.bootstrap = sdl2 - -# (int) port number to specify an explicit --port= p4a argument (eg for bootstrap flask) -#p4a.port = - -# Control passing the --use-setup-py vs --ignore-setup-py to p4a -# "in the future" --use-setup-py is going to be the default behaviour in p4a, right now it is not -# Setting this to false will pass --ignore-setup-py, true will pass --use-setup-py -# NOTE: this is general setuptools integration, having pyproject.toml is enough, no need to generate -# setup.py if you're using Poetry, but you need to add "toml" to source.include_exts. -#p4a.setup_py = false - -# (str) extra command line arguments to pass when invoking pythonforandroid.toolchain -#p4a.extra_args = - - -# -# iOS specific -# - -# (str) Path to a custom kivy-ios folder -#ios.kivy_ios_dir = ../kivy-ios -# Alternately, specify the URL and branch of a git checkout: -ios.kivy_ios_url = https://github.com/kivy/kivy-ios -ios.kivy_ios_branch = master - -# Another platform dependency: ios-deploy -# Uncomment to use a custom checkout -#ios.ios_deploy_dir = ../ios_deploy -# Or specify URL and branch -ios.ios_deploy_url = https://github.com/phonegap/ios-deploy -ios.ios_deploy_branch = 1.10.0 - -# (bool) Whether or not to sign the code -ios.codesign.allowed = false - -# (str) Name of the certificate to use for signing the debug version -# Get a list of available identities: buildozer ios list_identities -#ios.codesign.debug = "iPhone Developer: <lastname> <firstname> (<hexstring>)" - -# (str) The development team to use for signing the debug version -#ios.codesign.development_team.debug = <hexstring> - -# (str) Name of the certificate to use for signing the release version -#ios.codesign.release = %(ios.codesign.debug)s - -# (str) The development team to use for signing the release version -#ios.codesign.development_team.release = <hexstring> - -# (str) URL pointing to .ipa file to be installed -# This option should be defined along with `display_image_url` and `full_size_image_url` options. -#ios.manifest.app_url = - -# (str) URL pointing to an icon (57x57px) to be displayed during download -# This option should be defined along with `app_url` and `full_size_image_url` options. -#ios.manifest.display_image_url = - -# (str) URL pointing to a large icon (512x512px) to be used by iTunes -# This option should be defined along with `app_url` and `display_image_url` options. -#ios.manifest.full_size_image_url = - - -[buildozer] - -# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) -log_level = 2 - -# (int) Display warning if buildozer is run as root (0 = False, 1 = True) -warn_on_root = 1 - -# (str) Path to build artifact storage, absolute or relative to spec file -# build_dir = ./.buildozer - -# (str) Path to build output (i.e. .apk, .ipa) storage -# bin_dir = ./bin - -# ----------------------------------------------------------------------------- -# List as sections -# -# You can define all the "list" as [section:key]. -# Each line will be considered as a option to the list. -# Let's take [app] / source.exclude_patterns. -# Instead of doing: -# -#[app] -#source.exclude_patterns = license,data/audio/*.wav,data/images/original/* -# -# This can be translated into: -# -#[app:source.exclude_patterns] -#license -#data/audio/*.wav -#data/images/original/* -# - - -# ----------------------------------------------------------------------------- -# Profiles -# -# You can extend section / key with a profile -# For example, you want to deploy a demo version of your application without -# HD content. You could first change the title to add "(demo)" in the name -# and extend the excluded directories to remove the HD content. -# -#[app@demo] -#title = My Application (demo) -# -#[app:source.exclude_patterns@demo] -#images/hd/* -# -# Then, invoke the command line with the "demo" profile: -# -#buildozer --profile demo android debug diff --git a/gweatherrouting/core/gribmanager.py b/gweatherrouting/core/gribmanager.py index 17c84d8..9d4555a 100644 --- a/gweatherrouting/core/gribmanager.py +++ b/gweatherrouting/core/gribmanager.py @@ -8,9 +8,6 @@ from gweatherrouting.core.grib import Grib from gweatherrouting.core.storage import GRIB_DIR, TEMP_DIR, Storage -# except: -# from gweatherrouting.core.utils.dummy_storage import Storage - # from bs4 import BeautifulSoup logger = logging.getLogger("gweatherrouting") diff --git a/gweatherrouting/core/utils/__init__.py b/gweatherrouting/core/utils/__init__.py index f84f54d..87daf37 100644 --- a/gweatherrouting/core/utils/__init__.py +++ b/gweatherrouting/core/utils/__init__.py @@ -22,12 +22,8 @@ from geojson_utils import point_in_polygon from typing import Dict, Callable -# try: from gweatherrouting.core.storage import * # noqa: F401, F403 -# except: -# from .dummy_storage import * # noqa: F401, F403 - this_dir, this_fn = os.path.split(__file__) COUNTRIES = json.load(open(this_dir + "/../../data/countries.geojson", "r")) COUNTRY_SHAPES = [] diff --git a/gweatherrouting/core/utils/dummy_storage.py b/gweatherrouting/core/utils/dummy_storage.py deleted file mode 100644 index cc53c00..0000000 --- a/gweatherrouting/core/utils/dummy_storage.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -APP_NAME = "gweatherrouting" -DATA_DIR = "/storage" -GRIB_DIR = DATA_DIR -TEMP_DIR = DATA_DIR - - -class Storage(dict): - __init = False - - def __init__(self, filename=None, parent=None, *args, **kwargs): - super(Storage, self).__init__(*args, **kwargs) - - self.__parent = parent - self.__filename = filename - self.__handlers = {} - - for arg in args: - if isinstance(arg, dict): - for k, v in arg.iteritems(): - self[k] = v - - if kwargs: - for k, v in kwargs.iteritems(): - self[k] = v - - if parent is not None: - self.__init = True - - def __getattr__(self, attr): - return self.get(attr) - - def __setattr__(self, key, value): - self.__setitem__(key, value) - self.save() - self.notify_change(key, value) - - def __setitem__(self, key, value): - super(Storage, self).__setitem__(key, value) - self.__dict__.update({key: value}) - self.save() - self.notify_change(key, value) - - def __delattr__(self, item): - self.__delitem__(item) - self.save() - - def __delitem__(self, key): - super(Storage, self).__delitem__(key) - del self.__dict__[key] - self.save() - - def loadData(self, data): - for x in data: - if isinstance(data[x], dict): - self[x].loadData(data[x]) - else: - self[x] = data[x] - - def save(self): - if self.__parent: - return self.__parent.save() - - if not self.__filename or not self.__init: - return - - def load(self): - if self.__parent: - return - - if not self.__filename: - return - - return - - def loadOrSaveDefault(self): - try: - self.load() - self.__init = True - except: - self.__init = True - self.save() - - def to_dict(self): - d = {} - for x in self: - if x.find("Storage") != -1: - continue - - if isinstance(self[x], dict): - d[x] = self[x].to_dict() - else: - d[x] = self[x] - - return d - - def register_on_change(self, k, handler): - if k not in self.__handlers: - self.__handlers[k] = [] - - self.__handlers[k].append(handler) - handler(self[k]) - - def notify_change(self, k, v): - print("notify change", k, v) - if not self.__init: - return - - if k not in self.__handlers: - return - - for x in self.__handlers[k]: - x(v) diff --git a/gweatherrouting/kivy/__init__.py b/gweatherrouting/kivy/__init__.py deleted file mode 100644 index 39ece76..0000000 --- a/gweatherrouting/kivy/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -# isort:skip_file -from .gribscreen import GribScreen # noqa: F401 -from .trackscreen import TrackScreen # noqa: F401 -from .settingsscreen import SettingsScreen # noqa: F401 -from .regattascreen import RegattaScreen # noqa: F401 diff --git a/gweatherrouting/kivy/app.kv b/gweatherrouting/kivy/app.kv deleted file mode 100644 index 28b5aea..0000000 --- a/gweatherrouting/kivy/app.kv +++ /dev/null @@ -1,201 +0,0 @@ -#:kivy 1.0 - -#:import name gweatherrouting.kivy.gribscreen -##:include gweatherrouting/kivy/gribscreen.kv - -#:import name gweatherrouting.kivy.trackscreen -##:include gweatherrouting/kivy/trackscreen.kv - -#:import name gweatherrouting.kivy.settingsscreen -##:include gweatherrouting/kivy/settingsscreen.kv - -#:import name gweatherrouting.kivy.regattascreen -##:include gweatherrouting/kivy/regattascreen.kv - -#:import name gweatherrouting.kivy.timepickerdialog -##:include gweatherrouting/kivy/timepickerdialog.kv - - -MDScreen: - id: mainScreen - - MDToolbar: - id: toolbar - title: "GWR" - pos_hint: {"top": 1} - elevation: 10 - md_bg_color: app.theme_cls.accent_color - right_action_items: [["dots-vertical", lambda x: app.callback()]] - left_action_items: [["menu", lambda x: navDrawer.set_state("open")]] - - # MDBottomAppBar: - # MDToolbar: - # title: "Title" - # icon: "git" - # type: "bottom" - # left_action_items: [["menu", lambda x: x]] - # on_action_button: app.callback(self.icon) - - MDNavigationLayout: - y: toolbar.height - size_hint_y: None - height: root.height - toolbar.height - - ScreenManager: - id: screenManager - - MDScreen: - name: "Map Screen" - id: mapScreen - - MDBoxLayout: - orientation: "vertical" - - MDBoxLayout: - size_hint_y: None - height: "50dp" - orientation: "horizontal" - - MDIconButton: - icon: "step-backward-2" - user_font_size: "28sp" - on_press: app.onBackwardClick(hours=12) - - MDIconButton: - icon: "step-backward" - user_font_size: "28sp" - on_press: app.onBackwardClick(hours=1) - - MDIconButton: - icon: "step-forward" - user_font_size: "28sp" - on_press: app.onForwardClick(hours=1) - - # MDIconButton: - # icon: "play" - # user_font_size: "28sp" - # on_press: app.onPlayClick() - - MDIconButton: - icon: "step-forward-2" - user_font_size: "28sp" - on_press: app.onForwardClick(hours=12) - - MDIconButton: - icon: "clock" - user_font_size: "28sp" - on_press: app.openTimePickerDialog() - - MDLabel: - pos_hint: {"right": 1} - id: timeLabel - text: str(app.timeControl.time) - - - MapView - id: mapView - zoom: 8 - lat: 39 - lon: 9 - - on_touch_up: app.onMapTouchDown(args) - - GribScreen: - name: "Grib Screen" - id: gribScreen - - - TrackScreen: - name: "Track Screen" - id: trackScreen - - MDScreen: - name: "Routings Screen" - id: routingScreen - - MDLabel: - text: "Screen 2" - halign: "center" - - RegattaScreen: - name: "Settings Screen" - id: regattaScreen - - SettingsScreen: - name: "Settings Screen" - id: settingsScreen - - - MDNavigationDrawer: - id: navDrawer - - MDBoxLayout: - orientation: "vertical" - padding: "8dp" - spacing: "8dp" - - ScrollView: - MDList: - id: drawerList - - OneLineListItem: - text: "Map" - - on_press: - root.ids.navDrawer.set_state("close") - root.ids.screenManager.current = mapScreen.name - - IconLeftWidget: - icon: "map" - theme_text_color: "Custom" - text_color: app.theme_cls.accent_color - - OneLineListItem: - text: "Regatta" - - on_press: - root.ids.navDrawer.set_state("close") - root.ids.screenManager.current = regattaScreen.name - - IconLeftWidget: - icon: "plus" - - OneLineListItem: - text: "Gribs" - - on_press: - root.ids.navDrawer.set_state("close") - root.ids.screenManager.current = gribScreen.name - - IconLeftWidget: - icon: "language-python" - - OneLineListItem: - text: "Tracks & POIs" - - on_press: - root.ids.navDrawer.set_state("close") - root.ids.screenManager.current = trackScreen.name - - IconLeftWidget: - icon: "minus" - - OneLineListItem: - text: "Routings" - - on_press: - root.ids.navDrawer.set_state("close") - root.ids.screenManager.current = routingScreen.name - - IconLeftWidget: - icon: "plus" - - OneLineListItem: - text: "Settings" - - on_press: - root.ids.navDrawer.set_state("close") - root.ids.screenManager.current = settingsScreen.name - - IconLeftWidget: - icon: "map" \ No newline at end of file diff --git a/gweatherrouting/kivy/app.py b/gweatherrouting/kivy/app.py deleted file mode 100644 index 7ca7986..0000000 --- a/gweatherrouting/kivy/app.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" - -import os - -from kivy.app import Builder - -# from kivy_garden.mapview import MapView -from kivymd.app import MDApp - -from ..core import TimeControl -from .maplayers import GribMapLayer, POIMapLayer, TrackMapLayer -from .timepickerdialog import TimePickerDialog - - -class GWeatherRoutingApp(MDApp): - def __init__(self, core): - super(GWeatherRoutingApp, self).__init__() - self.timeControl = TimeControl() - self.core = core - - def build(self): - strs = [ - "gribscreen.kv", - "trackscreen.kv", - "regattascreen.kv", - "settingsscreen.kv", - "timepickerdialog.kv", - "app.kv", - ] - - ss = "" - for x in strs: - f = open(os.path.abspath(os.path.dirname(__file__)) + "/" + x, "r") - ss += f.read() + "\n" - f.close() - - # self.root = Builder.load_file(os.path.abspath(os.path.dirname(__file__)) + "/app.kv") - self.root = Builder.load_string(ss) - return self.root - - def on_start(self): - # Setup map - self.root.ids.mapView.add_layer( - GribMapLayer(self.core.gribManager, self.timeControl) - ) - self.root.ids.mapView.add_layer( - POIMapLayer(self.core.poiManager, self.timeControl) - ) - self.root.ids.mapView.add_layer( - TrackMapLayer(self.core.trackManager, self.timeControl) - ) - - # Setup grib - self.root.ids.gribScreen.gribManager = self.core.gribManager - # self.root.ids.gribScreen.updateLocalGribs() - - self.root.ids.trackScreen.trackManager = self.core.trackManager - self.root.ids.trackScreen.poiManager = self.core.poiManager - - def onMapTouchDown(self, k): - # print (k) - # TODO: open an menu with add poi / point to track - pass - - def onForwardClick(self, hours): - self.timeControl.increase(hours=hours) - self.root.ids.timeLabel.text = str(self.timeControl.time) - - def onBackwardClick(self, hours): - self.timeControl.decrease(hours=hours) - self.root.ids.timeLabel.text = str(self.timeControl.time) - - def openTimePickerDialog(self): - dialog = TimePickerDialog(self.timeControl.time) - dialog.open() diff --git a/gweatherrouting/kivy/gribscreen.kv b/gweatherrouting/kivy/gribscreen.kv deleted file mode 100644 index 045ff91..0000000 --- a/gweatherrouting/kivy/gribscreen.kv +++ /dev/null @@ -1,50 +0,0 @@ -#:kivy 1.0 -<LocalGribListItem>: - IconLeftWidget: - icon: root.icon - - RightBody: - orientation: 'horizontal' - - MDIconButton: - icon: 'delete' - margin_right: dp(10) - on_release: root.remove() - - MDCheckbox: - active: root.active - on_active: - root.active = self.active - root.setEnabled(self.active) - -<RemoteGribListItem>: - on_press: root.callback(self.url) - - IconLeftWidget: - icon: "tailwind" - - -<GribScreen>: - MDProgressBar: - id: downloadProgress - value: 0 - pos_hint: { 'bottom': 1 } - - ScrollView: - MDList: - id: gribList - - MDFloatingActionButton: - icon: "cloud-download" - elevation_normal: 12 - pos_hint: {'right': 1, 'bottom': 0.86} - md_bg_color: app.theme_cls.primary_color - on_release: root.openGribDownloadDialog() - - - # MDFloatingActionButton: - # icon: "folder-open" - # elevation_normal: 12 - # pos_hint: {'center_x': .6, 'bottom': 0.86 } - # md_bg_color: app.theme_cls.primary_color - # on_release: root.openGribDownloadDialog() \ No newline at end of file diff --git a/gweatherrouting/kivy/gribscreen.py b/gweatherrouting/kivy/gribscreen.py deleted file mode 100644 index 09324e2..0000000 --- a/gweatherrouting/kivy/gribscreen.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -from threading import Thread - -from kivy.properties import BooleanProperty, ObjectProperty, StringProperty -from kivymd.uix.boxlayout import MDBoxLayout -from kivymd.uix.dialog import MDDialog -from kivymd.uix.list import IRightBodyTouch, ThreeLineAvatarIconListItem -from kivymd.uix.screen import MDScreen - - -class RemoteGribListItem(ThreeLineAvatarIconListItem): - callback = ObjectProperty(None) - url = StringProperty("") - - -class LocalGribListItem(ThreeLineAvatarIconListItem): - icon = StringProperty("tailwind") - active = BooleanProperty(False) - gribManager = ObjectProperty() - updateCallback = ObjectProperty() - - def setEnabled(self, enabled): - self.gribManager.changeState(self.text, enabled) - - def remove(self): - self.gribManager.remove(self.text) - self.updateCallback() - - -class RightBody(IRightBodyTouch, MDBoxLayout): - pass - - -class GribScreen(MDScreen): - gribManager = None - gribDownloadDialog = None - - def updateLocalGribs(self): - gribList = self.ids.gribList - gribList.clear_widgets() - self.gribManager.refreshLocalGribs() - - for x in self.gribManager.localGribs: - gribList.add_widget( - LocalGribListItem( - text=x.name, - updateCallback=self.updateLocalGribs, - gribManager=self.gribManager, - active=self.gribManager.isEnabled(x.name), - secondary_text="Start at %s and is valid for %s hours" - % (str(x.startTime), str(x.lastForecast)), - tertiary_text="Centre: %s" % x.centre, - ) - ) - - def on_pre_enter(self, *args): - self.updateLocalGribs() - - def onSelectRemoteGrib(self, uri): - self.gribDownloadDialog.dismiss() - self.ids.downloadProgress.value = 0 - - def onGribDownloadPercentage(percentage): - self.ids.downloadProgress.value = percentage - - def onGribDownloadCompleted(g): - self.updateLocalGribs() - - t = Thread( - target=self.gribManager.download, - args=( - uri, - onGribDownloadPercentage, - onGribDownloadCompleted, - ), - ) - t.start() - - def openGribDownloadDialog(self): - if not self.gribDownloadDialog: - items = [] - - for x in self.gribManager.getDownloadList(): - items.append( - RemoteGribListItem( - text=x[0], - secondary_text="Start at %s, %s" % (str(x[3]), str(x[2])), - tertiary_text="Centre: %s" % x[1], - url=x[4], - callback=self.onSelectRemoteGrib, - ) - ) - - self.gribDownloadDialog = MDDialog( - title="Download a GRIB", type="simple", items=items - ) - - self.gribDownloadDialog.open() - - def openFileChooserDialog(self): - pass diff --git a/gweatherrouting/kivy/maplayers/__init__.py b/gweatherrouting/kivy/maplayers/__init__.py deleted file mode 100644 index 7638375..0000000 --- a/gweatherrouting/kivy/maplayers/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -# isort:skip_file -from .gribmaplayer import GribMapLayer # noqa: F401 -from .poimaplayer import POIMapLayer # noqa: F401 -from .trackmaplayer import TrackMapLayer # noqa: F401 diff --git a/gweatherrouting/kivy/maplayers/gribmaplayer.py b/gweatherrouting/kivy/maplayers/gribmaplayer.py deleted file mode 100644 index e908022..0000000 --- a/gweatherrouting/kivy/maplayers/gribmaplayer.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -import math - -from kivy.graphics import Color, Line - -# from kivy.graphics.tesselator import TYPE_POLYGONS, WINDING_ODD, Tesselator -# from kivy.metrics import dp -# from kivy.properties import ObjectProperty, StringProperty -# from kivy.utils import get_color_from_hex -# from kivy_garden.mapview.constants import CACHE_DIR -# from kivy_garden.mapview.downloader import Downloader -from kivy_garden.mapview.view import MapLayer - -from ...common import windColor - - -class GribMapLayer(MapLayer): - def __init__(self, gribManager, timeControl, **kwargs): - super().__init__(**kwargs) - self.gribManager = gribManager - self.timeControl = timeControl - self.timeControl.connect("time-change", self.onTimeChange) - self.arrowOpacity = 0.3 - - def onTimeChange(self, t): - self.draw() - - def reposition(self): - """Function called when :class:`MapView` is moved. You must recalculate - the position of your children.""" - self.draw() - - def unload(self): - """Called when the view want to completly unload the layer.""" - pass - - def drawWindArrow(self, x, y, wdir, wspeed): - wdir = -math.radians(wdir) - - a, b, c = windColor(wspeed) - - length = 25 - RD = 30 - self.canvas.add(Color(a, b, c, self.arrowOpacity)) - - xy = [x, y, x + (length * math.sin(wdir)), y + (length * math.cos(wdir))] - self.canvas.add(Line(points=xy, width=1.5)) - - xy = [ - x + (length * math.sin(wdir)), - y + (length * math.cos(wdir)), - x + (4 * math.sin(wdir - math.radians(RD))), - y + (4 * math.cos(wdir - math.radians(RD))), - ] - self.canvas.add(Line(points=xy, width=1.5)) - - xy = [ - x + (length * math.sin(wdir)), - y + (length * math.cos(wdir)), - x + (4 * math.sin(wdir + math.radians(RD))), - y + (4 * math.cos(wdir + math.radians(RD))), - ] - self.canvas.add(Line(points=xy, width=1.5)) - - def draw(self): # noqa: C901 - view = self.parent - zoom = view.zoom - bbox = view.get_bbox() - - p1lat, p1lon, p2lat, p2lon = bbox - - bounds = ( - (min(p1lat, p2lat), min(p1lon, p2lon)), - (max(p1lat, p2lat), max(p1lon, p2lon)), - ) - data = self.gribManager.getWind2D(self.timeControl.time, bounds) - - self.canvas.clear() - - if not data or len(data) == 0: - return - - # scale = int(math.fabs(zoom - 8)) - if zoom > 8: - scale = 1 - elif zoom > 7: - scale = 2 - elif zoom > 6: - scale = 3 - elif zoom > 5: - scale = 4 - elif zoom > 4: - scale = 5 - elif zoom > 3: - scale = 6 - elif zoom > 2: - scale = 7 - elif zoom > 1: - scale = 8 - elif zoom > 0: - scale = 9 - - # Draw arrows - for x in data[::scale]: - for y in x[::scale]: - xx, yy = view.get_window_xy_from(y[2][0], y[2][1], zoom) - self.drawWindArrow(xx, yy, y[0], y[1]) diff --git a/gweatherrouting/kivy/maplayers/poimaplayer.py b/gweatherrouting/kivy/maplayers/poimaplayer.py deleted file mode 100644 index ca99298..0000000 --- a/gweatherrouting/kivy/maplayers/poimaplayer.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -from kivy.core.text import Label as CoreLabel -from kivy.graphics import Color, Line, Rectangle -from kivy_garden.mapview.view import MapLayer - - -class POIMapLayer(MapLayer): - def __init__(self, poiManager, timeControl, **kwargs): - super().__init__(**kwargs) - self.poiManager = poiManager - self.timeControl = timeControl - self.timeControl.connect("time-change", self.onTimeChange) - - def onTimeChange(self, t): - self.draw() - - def reposition(self): - """Function called when :class:`MapView` is moved. You must recalculate - the position of your children.""" - self.draw() - - def unload(self): - """Called when the view want to completly unload the layer.""" - pass - - def draw(self): - view = self.parent - zoom = view.zoom - # bbox = view.get_bbox() - self.canvas.clear() - - for tr in self.poiManager: - if not tr.visible: - continue - - x, y = view.get_window_xy_from(tr.position[0], tr.position[1], zoom) - - # self.canvas.add(Color(1-self.colour, self.colour, 0, 1)) - # self.rect = Rectangle(size=self.size, pos=self.pos) - # self.canvas.add(self.rect) - self.canvas.add(Color(1, 1, 1)) - label = CoreLabel(text=tr.name, font_size=16) - label.refresh() - text = label.texture - self.canvas.add( - Rectangle(size=text.size, pos=[x - 20, y - 20], texture=text) - ) - # self.canvas.ask_update() - - # self.canvas.add(Triangle([x - 5, y - 5, x, y + 5, x + 5, y - 5])) - - # Style.Poi.Font.apply(cr) - # cr.move_to(x - 10, y - 10) - # cr.show_text(tr.name) - # cr.stroke() - - # Style.Poi.Triangle.apply(cr) - - self.canvas.add(Line(points=[x - 5, y - 5, x, y + 5])) - self.canvas.add(Line(points=[x + 5, y - 5, x, y + 5])) - self.canvas.add(Line(points=[x - 5, y - 5, x + 5, y - 5])) diff --git a/gweatherrouting/kivy/maplayers/trackmaplayer.py b/gweatherrouting/kivy/maplayers/trackmaplayer.py deleted file mode 100644 index b076661..0000000 --- a/gweatherrouting/kivy/maplayers/trackmaplayer.py +++ /dev/null @@ -1,156 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -# flake8: noqa: F841 -from kivy_garden.mapview.view import MapLayer - - -class TrackMapLayer(MapLayer): - def __init__(self, trackManager, timeControl, **kwargs): - super().__init__(**kwargs) - self.trackManager = trackManager - self.timeControl = timeControl - self.timeControl.connect("time-change", self.onTimeChange) - - def onTimeChange(self, t): - self.draw() - - def reposition(self): - """Function called when :class:`MapView` is moved. You must recalculate - the position of your children.""" - self.draw() - - def unload(self): - """Called when the view want to completly unload the layer.""" - pass - - def draw(self): - view = self.parent - zoom = view.zoom - bbox = view.get_bbox() - - p1lat, p1lon, p2lat, p2lon = bbox - - for tr in self.trackManager.routings: - if not tr.visible: - continue - - prevx = None - prevy = None - prevp = None - i = 0 - - for p in tr: - i += 1 - x, y = view.get_window_xy_from(p[0], p[1], zoom) - - # if prevp is None: - # Style.Track.RoutingTrackFont.apply(cr) - # cr.move_to(x+10, y) - # cr.show_text(tr.name) - # cr.stroke() - - # # Draw boat - # if prevp is not None: - # tprev = dateutil.parser.parse(prevp[2]) - # tcurr = dateutil.parser.parse(p[2]) - - # if tcurr >= self.timeControl.time and tprev < self.timeControl.time: - # dt = (tcurr-tprev).total_seconds() - # dl = utils.pointDistance(prevp[0], prevp[1], p[0], p[1]) / dt * - # (self.timeControl.time - tprev).total_seconds() - - # rp = utils.routagePointDistance (prevp[0], prevp[1], dl, math.radians(p[6])) - - # Style.Track.RoutingBoat.apply(cr) - # xx, yy = gpsmap.convert_geographic_to_screen (OsmGpsMap.MapPoint.new_degrees - # (rp[0], rp[1])) - # cr.arc(xx, yy, 7, 0, 2 * math.pi) - # cr.fill() - - # if prevx is not None and prevy is not None: - # Style.Track.RoutingTrack.apply(cr) - # cr.move_to (prevx, prevy) - # cr.line_to (x, y) - # cr.stroke() - - # Style.Track.RoutingTrackCircle.apply(cr) - # cr.arc(x, y, 5, 0, 2 * math.pi) - # cr.stroke() - - prevx = x - prevy = y - # prevp = p - - for tr in self.trackManager: - if not tr.visible: - continue - - active = False - if ( - self.trackManager.hasActive() - and self.trackManager.getActive().name == tr.name - ): - active = True - - prevx = None - prevy = None - i = 0 - - for p in tr: - i += 1 - x, y = view.get_window_xy_from(p[0], p[1], zoom) - - # if prevx is None: - # if active: - # Style.Track.TrackActiveFont.apply(cr) - # else: - # Style.Track.TrackInactiveFont.apply(cr) - - # cr.move_to(x-10, y-10) - # cr.show_text(tr.name) - # cr.stroke() - - # if active: - # Style.Track.TrackActivePoiFont.apply(cr) - # else: - # Style.Track.TrackInactivePoiFont.apply(cr) - - # cr.move_to(x-4, y+18) - # cr.show_text(str(i)) - # cr.stroke() - - # if prevx is not None and prevy is not None: - # if active: - # Style.Track.TrackActive.apply(cr) - # else: - # Style.Track.TrackInactive.apply(cr) - - # cr.move_to (prevx, prevy) - # cr.line_to (x, y) - # cr.stroke() - - # if active: - # Style.Track.TrackActive.apply(cr) - # else: - # Style.Track.TrackInactive.apply(cr) - - # Style.resetDash(cr) - - # cr.arc(x, y, 5, 0, 2 * math.pi) - # cr.stroke() - - prevx = x - prevy = y diff --git a/gweatherrouting/kivy/regattascreen.kv b/gweatherrouting/kivy/regattascreen.kv deleted file mode 100644 index e2b5238..0000000 --- a/gweatherrouting/kivy/regattascreen.kv +++ /dev/null @@ -1,21 +0,0 @@ -#:kivy 1.0 - -<RegattaScreen>: - MDTabs: - Tab: - title: "Map" - content_text: f"This is an example text for {self.title}" - - Tab: - title: "Gauges" - content_text: f"This is an example text for {self.title}" - - Tab: - title: "Logs" - content_text: f"This is an example text for {self.title}" - - Tab: - title: "Parameters" - content_text: f"This is an example text for {self.title}" - - \ No newline at end of file diff --git a/gweatherrouting/kivy/settingsscreen.kv b/gweatherrouting/kivy/settingsscreen.kv deleted file mode 100644 index a86ed27..0000000 --- a/gweatherrouting/kivy/settingsscreen.kv +++ /dev/null @@ -1,4 +0,0 @@ -#:kivy 1.0 - -<SettingsScreen>: - ScrollView: diff --git a/gweatherrouting/kivy/timepickerdialog.kv b/gweatherrouting/kivy/timepickerdialog.kv deleted file mode 100644 index 6474a39..0000000 --- a/gweatherrouting/kivy/timepickerdialog.kv +++ /dev/null @@ -1,29 +0,0 @@ -#:kivy 1.0 - -<TimePickerDialogContent>: - orientation: "vertical" - spacing: "12dp" - size_hint_y: None - height: "120dp" - - MDBoxLayout: - orientation: "horizontal" - - MDLabel: - id: dateLabel - text: root.datetime.strftime("%m/%d/%Y") - - MDRaisedButton: - text: "Set date" - on_press: root.openDatePicker() - - MDBoxLayout: - orientation: "horizontal" - - MDLabel: - id: timeLabel - text: root.datetime.strftime("%H:%M:%S") - - MDRaisedButton: - text: "Set time" - on_press: root.openTimePicker() \ No newline at end of file diff --git a/gweatherrouting/kivy/timepickerdialog.py b/gweatherrouting/kivy/timepickerdialog.py deleted file mode 100644 index e4be2ac..0000000 --- a/gweatherrouting/kivy/timepickerdialog.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -from kivymd.uix.boxlayout import MDBoxLayout -from kivymd.uix.button import MDFlatButton, MDRaisedButton -from kivymd.uix.dialog import MDDialog -from kivymd.uix.picker import MDDatePicker, MDTimePicker - - -class TimePickerDialogContent(MDBoxLayout): - def __init__(self, datetime): - self.datetime = datetime - super().__init__() - - def onTimeSave(self, instance, time): - self.datetime = self.datetime.replace(hour=time.hour, minute=time.minute) - self.ids.timeLabel.text = self.datetime.strftime("%H:%M") - - def onDateSave(self, instance, date, date_range): - self.datetime = self.datetime.replace( - year=date.year, month=date.month, day=date.day - ) - self.ids.dateLabel.text = self.datetime.strftime("%d/%m/%Y") - - def openTimePicker(self): - time_dialog = MDTimePicker() - time_dialog.set_time(self.datetime) - time_dialog.bind(time=self.onTimeSave) - time_dialog.open() - - def openDatePicker(self): - date_dialog = MDDatePicker( - year=self.datetime.year, month=self.datetime.month, day=self.datetime.day - ) - date_dialog.bind(on_save=self.onDateSave) - date_dialog.open() - - def getResult(self): - return self.datetime - - -class TimePickerDialog(MDDialog): - def __init__(self, datetime): - self.pickerContent = TimePickerDialogContent(datetime) - - super().__init__( - title="Set Date and Time", - type="custom", - content_cls=self.pickerContent, - buttons=[ - MDFlatButton( - text="CANCEL", - # theme_text_color="Custom", - # text_color=self.theme_cls.primary_color, - ), - MDRaisedButton( - text="SET", - # theme_text_color="Custom", - # text_color=self.theme_cls.primary_color, - ), - ], - ) - - def getResult(self): - return self.pickerContent.getResult() diff --git a/gweatherrouting/kivy/trackscreen.kv b/gweatherrouting/kivy/trackscreen.kv deleted file mode 100644 index 3ed32d6..0000000 --- a/gweatherrouting/kivy/trackscreen.kv +++ /dev/null @@ -1,25 +0,0 @@ -#:kivy 1.0 -<TrackListItem>: - IconLeftWidget: - icon: 'map-marker-path' - - RightCheckbox: - active: root.visible - # on_active: - # root.active = self.active - # root.setEnabled(self.active) - -<POIListItem>: - IconLeftWidget: - icon: 'map-marker' - - RightCheckbox: - active: root.visible - # on_active: - # root.active = self.active - # root.setEnabled(self.active) - -<TrackScreen>: - ScrollView: - MDList: - id: trackList diff --git a/gweatherrouting/kivy/trackscreen.py b/gweatherrouting/kivy/trackscreen.py deleted file mode 100644 index 324f504..0000000 --- a/gweatherrouting/kivy/trackscreen.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -from kivy.properties import BooleanProperty, ObjectProperty -from kivymd.uix.list import IRightBodyTouch, TwoLineIconListItem -from kivymd.uix.screen import MDScreen -from kivymd.uix.selectioncontrol import MDCheckbox - - -class TrackListItem(TwoLineIconListItem): - visible = BooleanProperty(False) - trackManager = ObjectProperty() - - # def setVisibility(self, enabled): - # self.gribManager.changeState(self.text, enabled) - - -class POIListItem(TwoLineIconListItem): - visible = BooleanProperty(False) - poiManager = ObjectProperty() - - -class RightCheckbox(IRightBodyTouch, MDCheckbox): - pass - - -class TrackScreen(MDScreen): - trackManager = None - poiManager = None - - def updateTracksAndPois(self): - trackList = self.ids.trackList - - for x in self.trackManager: - trackList.add_widget( - TrackListItem( - text=x.name, - trackManager=self.trackManager, - visible=x.visible, - secondary_text="Distance of %f nm, with %d track points" - % (x.length(), len(x.waypoints)), - ) - ) - - for x in self.poiManager: - trackList.add_widget( - POIListItem( - text=x.name, - poiManager=self.poiManager, - visible=x.visible, - secondary_text="Latitude: %f, Longitude: %f" - % (x.position[0], x.position[1]), - ) - ) - - def on_pre_enter(self, *args): - self.updateTracksAndPois() diff --git a/gweatherrouting/main.py b/gweatherrouting/main.py index 448e55d..a80f69d 100644 --- a/gweatherrouting/main.py +++ b/gweatherrouting/main.py @@ -21,12 +21,6 @@ logger = logging.getLogger("gweatherrouting") -def startUIKivy(): - from .kivy.app import GWeatherRoutingApp - - GWeatherRoutingApp(Core()).run() - - def startUIGtk(): import gi @@ -39,8 +33,5 @@ def startUIGtk(): Gtk.main() -# if __name__ == "__main__": -# startUIKivy() - -# if __name__ == "__main__": -# startUIGtk() +if __name__ == "__main__": + startUIGtk() diff --git a/gweatherroutingkivy/main.py b/gweatherroutingkivy/main.py deleted file mode 100644 index 573316c..0000000 --- a/gweatherroutingkivy/main.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" - -import logging - -from gweatherrouting.core import Core - -logger = logging.getLogger("gweatherrouting") - - -def startUIKivy(): - from gweatherrouting.kivy.app import GWeatherRoutingApp - - GWeatherRoutingApp(Core()).run() - - -if __name__ == "__main__": - startUIKivy() diff --git a/media/mobile.png b/media/mobile.png deleted file mode 100644 index 702ac5c..0000000 Binary files a/media/mobile.png and /dev/null differ diff --git a/requirements.kivy.txt b/requirements.kivy.txt deleted file mode 100644 index af59795..0000000 --- a/requirements.kivy.txt +++ /dev/null @@ -1,17 +0,0 @@ -requests -colorlog -eccodes==1.3.3 -geojson_utils -bs4 -vext -vext.gi -pyserial -pynmea2 -gpxpy -numpy -kivy[base] -mapview -briefcase -kivymd -nmeatoolkit -matplotlib \ No newline at end of file diff --git a/setup.kivy.py b/setup.kivy.py deleted file mode 100644 index 65f3f7b..0000000 --- a/setup.kivy.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2017-2024 Davide Gessa -""" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -For detail about GNU see <http://www.gnu.org/licenses/>. -""" -from setuptools import setup - -setup( - name="gweatherrouting", - version=0.1, - description="", - author="Davide Gessa", - setup_requires="setuptools", - author_email="gessadavide@gmail.com", - packages=[ - "gweatherrouting", - "gweatherrouting.core", - "gweatherrouting.kivy", - "gweatherrouting.kivy.maplayers", - "gweatherrouting.common", - ], - package_data={ - "gweatherrouting": [ - "data/*", - "data/boats/*", - "data/polars/*", - "data/symbols/*", - "data/s57/*", - "kivy/*.kv", - ] - }, - entry_points={ - "console_scripts": ["gweatherrouting-kivy=gweatherrouting.main:startUIKivy"], - }, - options={}, - executables={}, - install_requires=open("requirements.kivy.txt", "r").read().split("\n"), -) diff --git a/setup.py b/setup.py index bf6d562..e3e8695 100644 --- a/setup.py +++ b/setup.py @@ -39,8 +39,6 @@ "gweatherrouting.core.utils", "gweatherrouting.core.geo", "gweatherrouting.common", - "gweatherrouting.kivy", - "gweatherrouting.kivy.maplayers", "gweatherrouting.gtk", "gweatherrouting.gtk.maplayers", "gweatherrouting.gtk.settings", @@ -59,7 +57,6 @@ "gtk/*.glade", "gtk/settings/*.glade", "gtk/widgets/*.glade", - "kivy/*.kv", ] }, entry_points={ diff --git a/gweatherrouting/kivy/maplayers/isochronesmaplayer.py b/test/__init__.py similarity index 100% rename from gweatherrouting/kivy/maplayers/isochronesmaplayer.py rename to test/__init__.py diff --git a/gweatherrouting/kivy/regattascreen.py b/test/main.py similarity index 83% rename from gweatherrouting/kivy/regattascreen.py rename to test/main.py index 4c8f5f4..8cb053c 100644 --- a/gweatherrouting/kivy/regattascreen.py +++ b/test/main.py @@ -13,9 +13,8 @@ For detail about GNU see <http://www.gnu.org/licenses/>. """ -from kivymd.uix.screen import MDScreen +import unittest -class RegattaScreen(MDScreen): - def on_pre_enter(self, *args): - pass +if __name__ == "__main__": + unittest.main() diff --git a/gweatherrouting/kivy/settingsscreen.py b/test/test_placeholder.py similarity index 82% rename from gweatherrouting/kivy/settingsscreen.py rename to test/test_placeholder.py index ef83346..eeaa019 100644 --- a/gweatherrouting/kivy/settingsscreen.py +++ b/test/test_placeholder.py @@ -13,9 +13,10 @@ For detail about GNU see <http://www.gnu.org/licenses/>. """ -from kivymd.uix.screen import MDScreen +import unittest -class SettingsScreen(MDScreen): - def on_pre_enter(self, *args): - pass + +class TestPlaceholder(unittest.TestCase): + def test_place_holder(self): + self.assertEqual(True, True) diff --git a/tox.ini b/tox.ini index 796d49c..45eae88 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = linters,typecheck +envlist = linters,typecheck,unit-tests requires = tox>=4 skipsdist=True @@ -11,13 +11,21 @@ commands = python -I -m build --wheel -C=--build-option=-- -C=--build-option=-- -C=--build-option=-j4 +[testenv:unit-tests] +deps = + {[testenv]deps} + pytest + parameterized +commands = + pytest #-rP + + [testenv:flake8] deps = ; {[testenv]deps} flake8 commands = flake8 ./gweatherrouting - flake8 ./gweatherroutingkivy [testenv:isort] deps = @@ -43,7 +51,6 @@ deps = types-python-dateutil commands = mypy ./gweatherrouting - mypy ./gweatherroutingkivy [testenv:linters] deps =