From 2187e3a899c1920f508fd3468eada8c37d4712d7 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 06:10:17 +0200 Subject: [PATCH 1/9] initial commit --- .github/workflows/docs.yml | 1 + .gitignore | 135 +++++++- .pre-commit-config.yaml | 30 +- docs/types/attribution_alignment.md | 4 +- docs/types/cursor_rotation_behaviour.md | 2 + docs/types/pattern_fit.md | 4 +- .../tile_layer_evict_error_tile_strategy.md | 4 +- mkdocs.yml | 12 +- pyproject.toml | 45 ++- src/flet_map/__init__.py | 44 +++ src/flet_map/circle_layer.py | 26 +- src/flet_map/map.py | 248 ++++++++------ src/flet_map/marker_layer.py | 49 +-- src/flet_map/polygon_layer.py | 24 +- src/flet_map/polyline_layer.py | 23 +- src/flet_map/rich_attribution.py | 19 +- src/flet_map/simple_attribution.py | 10 +- src/flet_map/source_attribution.py | 20 +- src/flet_map/tile_layer.py | 83 ++--- src/flet_map/types.py | 314 ++++++++++-------- src/flutter/flet_map/pubspec.yaml | 2 +- 21 files changed, 715 insertions(+), 384 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index df8cc00..8da82a1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - dev paths: - 'LICENSE' - 'CHANGELOG.md' diff --git a/.gitignore b/.gitignore index 006e58f..891876a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,135 @@ -*.egg-info/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python build/ develop-eggs/ dist/ -.DS_store -.venv/ \ No newline at end of file +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock +uv.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# VS Code +.vscode/ + +# PDM +.pdm.toml +__pypackages__/ + +.DS_Store diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e69f06f..0997bef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,27 @@ repos: - - repo: https://github.com/pycqa/isort - rev: 5.13.2 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.5 hooks: - - id: isort - - repo: https://github.com/ambv/black - rev: 22.12.0 + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format + + - repo: https://github.com/executablebooks/mdformat + rev: 0.7.22 hooks: - - id: black + - id: mdformat + additional_dependencies: + - mdformat-ruff + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/aio-libs/sort-all + rev: v1.3.0 + hooks: + - id: sort-all diff --git a/docs/types/attribution_alignment.md b/docs/types/attribution_alignment.md index 6637a43..3d12fe2 100644 --- a/docs/types/attribution_alignment.md +++ b/docs/types/attribution_alignment.md @@ -1 +1,3 @@ -::: flet_map.types.AttributionAlignment \ No newline at end of file +::: flet_map.types.AttributionAlignment +options: +separate_signature: false diff --git a/docs/types/cursor_rotation_behaviour.md b/docs/types/cursor_rotation_behaviour.md index fad995e..cd3ebd4 100644 --- a/docs/types/cursor_rotation_behaviour.md +++ b/docs/types/cursor_rotation_behaviour.md @@ -1 +1,3 @@ ::: flet_map.types.CursorRotationBehaviour +options: +separate_signature: false diff --git a/docs/types/pattern_fit.md b/docs/types/pattern_fit.md index cbf9086..ba387b8 100644 --- a/docs/types/pattern_fit.md +++ b/docs/types/pattern_fit.md @@ -1 +1,3 @@ -::: flet_map.types.PatternFit \ No newline at end of file +::: flet_map.types.PatternFit +options: +separate_signature: false diff --git a/docs/types/tile_layer_evict_error_tile_strategy.md b/docs/types/tile_layer_evict_error_tile_strategy.md index e418982..cb484f7 100644 --- a/docs/types/tile_layer_evict_error_tile_strategy.md +++ b/docs/types/tile_layer_evict_error_tile_strategy.md @@ -1 +1,3 @@ -::: flet_map.types.TileLayerEvictErrorTileStrategy \ No newline at end of file +::: flet_map.types.TileLayerEvictErrorTileStrategy +options: +separate_signature: false diff --git a/mkdocs.yml b/mkdocs.yml index 192418b..829cf25 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -140,6 +140,7 @@ plugins: # - footnotes - search: lang: en + - open-in-new-tab - mike: alias_type: symlink - glightbox @@ -164,21 +165,16 @@ plugins: show_labels: false show_if_no_docstring: true docstring_section_style: spacy + separate_signature: true inherited_members: true preload_modules: [ flet ] filters: - "!^_" # Exclude private members starting with only one underscore - - "!before_update" - - "!before_event" - - "!clean" - - "!did_mount" - - "!init" - - "!is_isolated" - - "!update" - - "!will_unmount" extensions: - griffe_modernized_annotations + - griffe_warnings_deprecated inventories: + - url: https://docs.flet.dev/objects.inv - url: https://docs.python.org/3/objects.inv domains: [ py, std ] - url: https://typing-extensions.readthedocs.io/en/latest/objects.inv diff --git a/pyproject.toml b/pyproject.toml index 6fa5417..cb6e178 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,22 +20,40 @@ Issues = "https://github.com/flet-dev/flet-map/issues" "flutter.flet_map" = ["**/*"] [dependency-groups] +test = [ + "pytest >=7.2.0", +] dev = [ - "pre-commit>=4.2.0", - "ruff>=0.11.7", + "pre-commit >=4.2.0", + "ruff >=0.11.7", + { include-group = 'test' }, +] +docs-coverage = [ + "docstr-coverage >=2.3.2", ] docs = [ - "mkdocs", - "mkdocs-material", - "mkdocstrings[python]", - "mkdocstrings-python-xref", - "mike", - "markdown>=3.6", - "pymdown-extensions", - "mkdocs-glightbox", - "mkdocs-section-index", - "griffe-modernized-annotations", - "pygments>=2.16", + "mkdocs >=1.6.1", + "mkdocs-material >=9.6.15", + "mkdocstrings-python >=1.16.12", + "mkdocstrings-python-xref >=1.16.3", + "mike >=2.1.3", + "markdown >=3.6", + "pymdown-extensions >=10.16", + "mkdocs-exclude >=1.0.2", + "mkdocs-glightbox >=0.4.0", + "mkdocs-open-in-new-tab >=1.0.8", + "mkdocs-section-index >=0.3.10", + "griffe-modernized-annotations >=1.0.8", + "griffe-warnings-deprecated >=1.1.0", + "pygments >=2.16", + "markdown-exec[ansi] >=1.11.0", + "pydocstyle >=6.3.0", + "linkcheckmd >=1.4.0", + { include-group = 'docs-coverage' }, +] +all = [ + { include-group = 'dev' }, + { include-group = 'docs' }, ] [build-system] @@ -64,6 +82,7 @@ select = [ "I" ] preview = true +pydocstyle = { convention = 'google' } [tool.ruff.format] quote-style = "double" diff --git a/src/flet_map/__init__.py b/src/flet_map/__init__.py index 65a5bc6..5bcbc45 100644 --- a/src/flet_map/__init__.py +++ b/src/flet_map/__init__.py @@ -39,3 +39,47 @@ TileDisplay, TileLayerEvictErrorTileStrategy, ) + +__all__ = [ + "AttributionAlignment", + "Camera", + "CameraFit", + "CircleLayer", + "CircleMarker", + "CursorKeyboardRotationConfiguration", + "CursorRotationBehaviour", + "DashedStrokePattern", + "DottedStrokePattern", + "FadeInTileDisplay", + "ImageSourceAttribution", + "InstantaneousTileDisplay", + "InteractionConfiguration", + "InteractionFlag", + "KeyboardConfiguration", + "Map", + "MapEvent", + "MapEventSource", + "MapHoverEvent", + "MapLatitudeLongitude", + "MapLatitudeLongitudeBounds", + "MapPointerEvent", + "MapPositionChangeEvent", + "MapTapEvent", + "Marker", + "MarkerLayer", + "MultiFingerGesture", + "PatternFit", + "PolygonLayer", + "PolygonMarker", + "PolylineLayer", + "PolylineMarker", + "RichAttribution", + "SimpleAttribution", + "SolidStrokePattern", + "SourceAttribution", + "StrokePattern", + "TextSourceAttribution", + "TileDisplay", + "TileLayer", + "TileLayerEvictErrorTileStrategy", +] diff --git a/src/flet_map/circle_layer.py b/src/flet_map/circle_layer.py index 9b16d5c..eddec6f 100644 --- a/src/flet_map/circle_layer.py +++ b/src/flet_map/circle_layer.py @@ -1,17 +1,18 @@ -from typing import List, Optional +from typing import Optional import flet as ft from .map_layer import MapLayer from .types import MapLatitudeLongitude -__all__ = ["CircleMarker", "CircleLayer"] +__all__ = ["CircleLayer", "CircleMarker"] @ft.control("CircleMarker") class CircleMarker(ft.Control): """ - A circular marker displayed on the Map at the specified location through the [`CircleLayer`][(p).]. + A circular marker displayed on the Map at the specified + location through the [`CircleLayer`][(p).]. Raises: AssertionError: If the [`border_stroke_width`][(c).] is negative. @@ -29,14 +30,16 @@ class CircleMarker(ft.Control): border_color: Optional[ft.ColorValue] = None """ The color of the circle border line. - - Needs [`border_stroke_width`][..] to be greater than `0.0` in order to be visible. + + Note: + [`border_stroke_width`][..] must to be greater than + `0.0` in order for this color to be visible. """ border_stroke_width: ft.Number = 0.0 """ - The stroke width for the circle border. - + The stroke width for the circle border. + Note: Must be non-negative. """ @@ -48,9 +51,10 @@ class CircleMarker(ft.Control): def before_update(self): super().before_update() - assert ( - self.border_stroke_width >= 0 - ), "border_stroke_width must be greater than or equal to 0" + assert self.border_stroke_width >= 0, ( + f"border_stroke_width must be greater than or equal to 0, " + f"got {self.border_stroke_width}" + ) @ft.control("CircleLayer") @@ -59,5 +63,5 @@ class CircleLayer(MapLayer): A layer to display [`CircleMarker`][(p).]s. """ - circles: List[CircleMarker] + circles: list[CircleMarker] """A list of [`CircleMarker`][(p).]s to display.""" diff --git a/src/flet_map/map.py b/src/flet_map/map.py index 5789088..3ae3fc5 100644 --- a/src/flet_map/map.py +++ b/src/flet_map/map.py @@ -22,14 +22,12 @@ @ft.control("Map") class Map(ft.ConstrainedControl): """ - An interactive map control that allows displaying various layers. + An interactive map that displays various layers. """ layers: list[MapLayer] """ A list of layers to be displayed (stack-like) on the map. - - Value is of type [`MapLayer`][(p).]. """ initial_center: MapLatitudeLongitude = field( @@ -37,8 +35,6 @@ class Map(ft.ConstrainedControl): ) """ The initial center of the map. - - Value is of type `MapLatitudeLongitude`. """ initial_rotation: ft.Number = 0.0 @@ -48,7 +44,7 @@ class Map(ft.ConstrainedControl): initial_zoom: ft.Number = 13.0 """ - The zoom when the map is first loaded. + The zoom when the map is first loaded. If initial_camera_fit is defined this has no effect. """ @@ -67,110 +63,96 @@ class Map(ft.ConstrainedControl): keep_alive: bool = False """ Whether to enable the built in keep-alive functionality. - - If the map is within a complex layout, such as a `ListView`, + + If the map is within a complex layout, such as a [`ListView`][flet.ListView], the map will reset to it's inital position after it appears back into view. To ensure this doesn't happen, enable this flag to prevent it from rebuilding. """ max_zoom: Optional[ft.Number] = None """ - The maximum (highest) zoom level of every layer. + The maximum (highest) zoom level of every layer. Each layer can specify additional zoom level restrictions. """ min_zoom: Optional[ft.Number] = None """ - The minimum (smallest) zoom level of every layer. + The minimum (smallest) zoom level of every layer. Each layer can specify additional zoom level restrictions. """ animation_curve: ft.AnimationCurve = ft.AnimationCurve.FAST_OUT_SLOWIN """ - The default animation curve to be used for map-animations - when calling instance methods like `zoom_in()`, `rotate_from()`, `move_to()` etc. + The default animation curve to be used for map-animations + when calling instance methods like [`zoom_in()`][(c).zoom_in], + [`rotate_from()`][(c).rotate_from], + [`move_to()`][(c).move_to] etc. """ animation_duration: ft.DurationValue = field( default_factory=lambda: ft.Duration(milliseconds=500) ) """ - The default animation duration to be used for map-animations - when calling instance methods like `zoom_in()`, `rotate_from()`, `move_to()` etc. + The default animation duration to be used for map-animations + when calling instance methods like [`zoom_in()`][(c).zoom_in], + [`rotate_from()`][(c).rotate_from], + [`move_to()`][(c).move_to] etc. """ initial_camera_fit: Optional[CameraFit] = None """ - Defines the visible bounds when the map is first loaded. - Takes precedence over `initial_center`/`initial_zoom`. + Defines the visible bounds when the map is first loaded. + Takes precedence over [`initial_center`][..]/[`initial_zoom`][..]. """ - on_init: ft.OptionalControlEventHandler["Map"] = None + on_init: Optional[ft.ControlEventHandler["Map"]] = None """ Fires when the map is initialized. """ - on_tap: ft.OptionalEventHandler[MapTapEvent["Map"]] = None + on_tap: Optional[ft.EventHandler[MapTapEvent]] = None """ Fires when a tap event occurs. - - Event handler argument is of type [`MapTapEvent`][(p).]. """ - on_hover: ft.OptionalEventHandler[MapHoverEvent["Map"]] = None + on_hover: Optional[ft.EventHandler[MapHoverEvent]] = None """ Fires when a hover event occurs. - - Event handler argument is of type [`MapHoverEvent`][(p).]. """ - on_secondary_tap: ft.OptionalEventHandler[MapTapEvent["Map"]] = None + on_secondary_tap: Optional[ft.EventHandler[MapTapEvent]] = None """ Fires when a secondary tap event occurs. - - Event handler argument is of type [`MapTapEvent`][(p).]. """ - on_long_press: ft.OptionalEventHandler[MapTapEvent["Map"]] = None + on_long_press: Optional[ft.EventHandler[MapTapEvent]] = None """ Fires when a long press event occurs. - - Event handler argument is of type [`MapTapEvent`][(p).]. """ - on_event: ft.OptionalEventHandler[MapEvent["Map"]] = None + on_event: Optional[ft.EventHandler[MapEvent]] = None """ Fires when any map events occurs. - - Event handler argument is of type [`MapEvent`][(p).]. """ - on_position_change: ft.OptionalEventHandler[MapPositionChangeEvent["Map"]] = None + on_position_change: Optional[ft.EventHandler[MapPositionChangeEvent]] = None """ Fires when the map position changes. - - Event handler argument is of type [`MapPositionChangeEvent`][(p).]. """ - on_pointer_down: ft.OptionalEventHandler[MapPointerEvent["Map"]] = None + on_pointer_down: Optional[ft.EventHandler[MapPointerEvent]] = None """ Fires when a pointer down event occurs. - - Event handler argument is of type [`MapPointerEvent`][(p).]. """ - on_pointer_cancel: ft.OptionalEventHandler[MapPointerEvent["Map"]] = None + on_pointer_cancel: Optional[ft.EventHandler[MapPointerEvent]] = None """ Fires when a pointer cancel event occurs. - - Event handler argument is of type [`MapPointerEvent`][(p).]. """ - on_pointer_up: ft.OptionalEventHandler[MapPointerEvent["Map"]] = None + on_pointer_up: Optional[ft.EventHandler[MapPointerEvent]] = None """ Fires when a pointer up event occurs. - - Event handler argument is of type [`MapPointerEvent`][(p).]. """ async def rotate_from_async( @@ -185,9 +167,12 @@ async def rotate_from_async( Args: degree: The number of degrees to increment to the current rotation. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ await self._invoke_method_async( method_name="rotate_from", @@ -211,13 +196,19 @@ def rotate_from( Args: degree: The number of degrees to increment to the current rotation. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ asyncio.create_task( self.rotate_from_async( - degree, animation_curve, animation_duration, cancel_ongoing_animations + degree=degree, + animation_curve=animation_curve, + animation_duration=animation_duration, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) @@ -231,9 +222,12 @@ async def reset_rotation_async( Resets the map's rotation to 0 degrees. Args: - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ await self._invoke_method_async( method_name="reset_rotation", @@ -254,13 +248,18 @@ def reset_rotation( Resets the map's rotation to 0 degrees. Args: - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ asyncio.create_task( self.reset_rotation_async( - animation_curve, animation_duration, cancel_ongoing_animations + animation_curve=animation_curve, + animation_duration=animation_duration, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) @@ -274,9 +273,12 @@ async def zoom_in_async( Zooms in by one zoom-level from the current one. Args: - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ await self._invoke_method_async( method_name="zoom_in", @@ -297,13 +299,18 @@ def zoom_in( Zooms in by one zoom-level from the current one. Args: - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ asyncio.create_task( self.zoom_in_async( - animation_curve, animation_duration, cancel_ongoing_animations + animation_curve=animation_curve, + animation_duration=animation_duration, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) @@ -317,9 +324,12 @@ async def zoom_out_async( Zooms out by one zoom-level from the current one. Args: - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ await self._invoke_method_async( method_name="zoom_out", @@ -340,13 +350,18 @@ def zoom_out( Zooms out by one zoom-level from the current one. Args: - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ asyncio.create_task( self.zoom_out_async( - animation_curve, animation_duration, cancel_ongoing_animations + animation_curve=animation_curve, + animation_duration=animation_duration, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) @@ -362,9 +377,12 @@ async def zoom_to_async( Args: zoom: The zoom level to zoom to. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ await self._invoke_method_async( method_name="zoom_to", @@ -388,13 +406,19 @@ def zoom_to( Args: zoom: The zoom level to zoom to. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ asyncio.create_task( self.zoom_to_async( - zoom, animation_curve, animation_duration, cancel_ongoing_animations + zoom=zoom, + animation_curve=animation_curve, + animation_duration=animation_duration, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) @@ -413,12 +437,16 @@ async def move_to_async( Args: destination: The destination point to move to. - zoom: The zoom level to be applied. If provided, must be greater than or equal to `0.0`. + zoom: The zoom level to be applied. If provided, + must be greater than or equal to `0.0`. rotation: Rotation (in degrees) to be applied. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. offset: The offset to be used. Only works when `rotation` is `None`. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. Raises: AssertionError: If `zoom` is not `None` and is negative. @@ -452,25 +480,29 @@ def move_to( Args: destination: The destination point to move to. - zoom: The zoom level to be applied. If provided, must be greater than or equal to `0.0`. + zoom: The zoom level to be applied. If provided, + must be greater than or equal to `0.0`. rotation: Rotation (in degrees) to be applied. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. offset: The offset to be used. Only works when `rotation` is `None`. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. Raises: AssertionError: If `zoom` is not `None` and is negative. """ asyncio.create_task( self.move_to_async( - destination, - zoom, - rotation, - animation_curve, - animation_duration, - offset, - cancel_ongoing_animations, + destination=destination, + zoom=zoom, + rotation=rotation, + animation_curve=animation_curve, + animation_duration=animation_duration, + offset=offset, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) @@ -488,9 +520,12 @@ async def center_on_async( Args: point: The point on which to center the map. zoom: The zoom level to be applied. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ await self._invoke_method_async( method_name="center_on", @@ -517,16 +552,19 @@ def center_on( Args: point: The point on which to center the map. zoom: The zoom level to be applied. - animation_curve: The curve of the animation. If None (the default), `Map.animation_curve` will be used. - animation_duration: The duration of the animation. If None (the default), `Map.animation_duration` will be used. - cancel_ongoing_animations: Whether to cancel/stop all ongoing map-animations before starting this new one. + animation_curve: The curve of the animation. If None (the default), + [`Map.animation_curve`][(p).] will be used. + animation_duration: The duration of the animation. + If None (the default), [`Map.animation_duration`][(p).] will be used. + cancel_ongoing_animations: Whether to cancel/stop all + ongoing map-animations before starting this new one. """ asyncio.create_task( self.center_on_async( - point, - zoom, - animation_curve, - animation_duration, - cancel_ongoing_animations, + point=point, + zoom=zoom, + animation_curve=animation_curve, + animation_duration=animation_duration, + cancel_ongoing_animations=cancel_ongoing_animations, ) ) diff --git a/src/flet_map/marker_layer.py b/src/flet_map/marker_layer.py index 522e4cc..d6055ca 100644 --- a/src/flet_map/marker_layer.py +++ b/src/flet_map/marker_layer.py @@ -1,5 +1,5 @@ from dataclasses import field -from typing import List, Optional +from typing import Optional import flet as ft @@ -12,16 +12,18 @@ @ft.control("Marker") class Marker(ft.Control): """ - A marker displayed on the Map at the specified location through the [`MarkerLayer`][(p).]. + A marker displayed on the Map at the specified location + through the [`MarkerLayer`][(p).]. Raises: - AssertionError: If the [`content`][(c).] is not visible, or if [`height`][(c).] or [`width`][(c).] are negative. + AssertionError: If the [`content`][(c).] is not visible, or + if [`height`][(c).] or [`width`][(c).] are negative. """ content: ft.Control """ The content to be displayed at [`coordinates`][..]. - + Note: Must be provided and visible. """ @@ -29,24 +31,29 @@ class Marker(ft.Control): coordinates: MapLatitudeLongitude """ The coordinates of the marker. - - This will be the center of the marker, if `alignment=ft.Alignment.center()`. + + This will be the center of the marker, + if [`alignment`][..] is [`flet.Alignment.CENTER`][. """ rotate: Optional[bool] = None """ - Whether to counter rotate this marker to the map's rotation, to keep a fixed orientation. - So, when `True`, this marker will always appear upright and vertical from the user's perspective. - - Note: this is not used to apply a custom rotation in degrees to the marker. - - Defaults to the value of the parent [`MarkerLayer.rotate`][(p).]. + Whether to counter rotate this marker to the map's rotation, + to keep a fixed orientation. + So, when `True`, this marker will always appear upright and + vertical from the user's perspective. + + If `None`, defaults to the value of the parent [`MarkerLayer.rotate`][(p).]. + + Note: + This is not used to apply a custom rotation in degrees to this marker. + """ height: ft.Number = 30.0 """ The height of the [`content`][..] Control. - + Note: Must be non-negative. """ @@ -54,7 +61,7 @@ class Marker(ft.Control): width: ft.Number = 30.0 """ The width of the [`content`][..] Control. - + Note: Must be non-negative. """ @@ -62,7 +69,7 @@ class Marker(ft.Control): alignment: Optional[ft.Alignment] = None """ Alignment of the marker relative to the normal center at [`coordinates`][..]. - + Defaults to the value of the parent [`MarkerLayer.alignment`][(m).]. """ @@ -79,19 +86,21 @@ class MarkerLayer(MapLayer): A layer to display Markers. """ - markers: List[Marker] + markers: list[Marker] """ - List of [`Marker`][(m).]s to display. + A list of [`Marker`][(m).]s to display. """ alignment: Optional[ft.Alignment] = field( - default_factory=lambda: ft.Alignment.center() + default_factory=lambda: ft.Alignment.CENTER ) """ - Alignment of each marker relative to its normal center at `Marker.coordinates`. + The alignment of each marker relative to its normal center at + [`Marker.coordinates`][(m).]. """ rotate: bool = False """ - Whether to counter-rotate `markers` to the map's rotation, to keep a fixed orientation. + Whether to counter-rotate `markers` to the map's rotation, + to keep a fixed orientation. """ diff --git a/src/flet_map/polygon_layer.py b/src/flet_map/polygon_layer.py index aca75e3..2e3c5b3 100644 --- a/src/flet_map/polygon_layer.py +++ b/src/flet_map/polygon_layer.py @@ -1,11 +1,11 @@ -from typing import List, Optional +from typing import Optional import flet as ft from .map_layer import MapLayer from .types import MapLatitudeLongitude -__all__ = ["PolygonMarker", "PolygonLayer"] +__all__ = ["PolygonLayer", "PolygonMarker"] @ft.control("PolygonMarker") @@ -14,7 +14,7 @@ class PolygonMarker(ft.Control): A marker for the [`PolygonLayer`][(p).]. """ - coordinates: List[MapLatitudeLongitude] + coordinates: list[MapLatitudeLongitude] """ The points for the outline of this polygon. """ @@ -47,7 +47,7 @@ class PolygonMarker(ft.Control): border_stroke_width: ft.Number = 0.0 """ The width of the border outline. - + Note: Must be non-negative. """ @@ -59,7 +59,7 @@ class PolygonMarker(ft.Control): rotate_label: bool = False """ - Whether to rotate the label counter to the camera's rotation, + Whether to rotate the label counter to the camera's rotation, to ensure it remains upright. """ @@ -75,9 +75,9 @@ class PolygonMarker(ft.Control): def before_update(self): super().before_update() - assert ( - self.border_stroke_width >= 0 - ), "border_stroke_width must be greater than or equal to 0" + assert self.border_stroke_width >= 0, ( + "border_stroke_width must be greater than or equal to 0" + ) @ft.control("PolygonLayer") @@ -86,7 +86,7 @@ class PolygonLayer(MapLayer): A layer to display PolygonMarkers. """ - polygons: List[PolygonMarker] + polygons: list[PolygonMarker] """ A list of [`PolygonMarker`][(p).]s to display. """ @@ -108,14 +108,14 @@ class PolygonLayer(MapLayer): simplification_tolerance: ft.Number = 0.3 """ - + """ use_alternative_rendering: bool = False """ Whether to use an alternative rendering pathway to draw polygons onto the - underlying `Canvas`, which can be more performant in 'some' circumstances. - + underlying `Canvas`, which can be more performant in 'some' circumstances. + This will not always improve performance, and there are other important considerations before enabling it. It is intended for use when prior profiling indicates more performance is required after other methods are diff --git a/src/flet_map/polyline_layer.py b/src/flet_map/polyline_layer.py index 95669cf..dd21826 100644 --- a/src/flet_map/polyline_layer.py +++ b/src/flet_map/polyline_layer.py @@ -6,7 +6,7 @@ from .map_layer import MapLayer from .types import MapLatitudeLongitude, SolidStrokePattern, StrokePattern -__all__ = ["PolylineMarker", "PolylineLayer"] +__all__ = ["PolylineLayer", "PolylineMarker"] @ft.control("PolylineMarker") @@ -43,7 +43,7 @@ class PolylineMarker(ft.Control): stroke_width: ft.Number = 1.0 """ The width of the stroke. - + Note: Must be non-negative. """ @@ -51,7 +51,7 @@ class PolylineMarker(ft.Control): border_stroke_width: ft.Number = 0.0 """ The width of the stroke with of the line border. - + Note: Must be non-negative. """ @@ -79,10 +79,13 @@ class PolylineMarker(ft.Control): def before_update(self): super().before_update() - assert ( - self.border_stroke_width >= 0 - ), "border_stroke_width must be greater than or equal to 0" - assert self.stroke_width >= 0, "stroke_width must be greater than or equal to 0" + assert self.border_stroke_width >= 0, ( + f"border_stroke_width must be greater than or equal to 0, " + f"got {self.border_stroke_width}" + ) + assert self.stroke_width >= 0, ( + f"stroke_width must be greater than or equal to 0, got {self.stroke_width}" + ) @ft.control("PolylineLayer") @@ -93,7 +96,7 @@ class PolylineLayer(MapLayer): polylines: list[PolylineMarker] """ - List of [`PolylineMarker`][(p).]s to be drawn. + List of [`PolylineMarker`][(p).]s to be drawn. """ culling_margin: ft.Number = 10.0 @@ -104,12 +107,12 @@ class PolylineLayer(MapLayer): min_hittable_radius: ft.Number = 10.0 """ The minimum radius of the hittable area around each polyline in logical pixels. - + The entire visible area is always hittable, but if the visible area is smaller than this, then this will be the hittable area. """ simplification_tolerance: ft.Number = 0.3 """ - + """ diff --git a/src/flet_map/rich_attribution.py b/src/flet_map/rich_attribution.py index c97e54b..d24ab87 100644 --- a/src/flet_map/rich_attribution.py +++ b/src/flet_map/rich_attribution.py @@ -1,5 +1,5 @@ from dataclasses import field -from typing import List, Optional +from typing import Optional import flet as ft @@ -17,11 +17,11 @@ class RichAttribution(MapLayer): (displayed in a popup controlled by an icon button adjacent to the images). """ - attributions: List[SourceAttribution] + attributions: list[SourceAttribution] """ List of attributions to display. - - [`TextSourceAttribution`][(p).]s are shown in a popup box, + + [`TextSourceAttribution`][(p).]s are shown in a popup box, unlike [`ImageSourceAttribution`][(p).], which are visible permanently. """ @@ -44,10 +44,12 @@ class RichAttribution(MapLayer): default_factory=lambda: ft.Duration() ) """ - The popup box will be open by default and be hidden this long after the map is initialised. - + The popup box will be open by default and be hidden this + long after the map is initialised. + This is useful with certain sources/tile servers that make immediate - attribution mandatory and are not attributed with a permanently visible [`ImageSourceAttribution`][(p).]. + attribution mandatory and are not attributed with a permanently + visible [`ImageSourceAttribution`][(p).]. """ permanent_height: ft.Number = 24.0 @@ -58,6 +60,7 @@ class RichAttribution(MapLayer): show_flutter_map_attribution: bool = True """ - Whether to add an additional attribution logo and text for [`flutter-map`](https://docs.fleaflet.dev/), + Whether to add an additional attribution logo and text + for [`flutter-map`](https://docs.fleaflet.dev/), on which 'flet-map' package is based for map-renderings. """ diff --git a/src/flet_map/simple_attribution.py b/src/flet_map/simple_attribution.py index a63376e..b1cca4b 100644 --- a/src/flet_map/simple_attribution.py +++ b/src/flet_map/simple_attribution.py @@ -1,5 +1,5 @@ from dataclasses import field -from typing import Union +from typing import Optional, Union import flet as ft @@ -17,19 +17,17 @@ class SimpleAttribution(MapLayer): text: Union[str, ft.Text] """ The attribution message to be displayed. - - Value is of type `str` and `ft.Text`. """ - alignment: ft.Alignment = field(default_factory=lambda: ft.Alignment.bottom_right()) + alignment: ft.Alignment = field(default_factory=lambda: ft.Alignment.BOTTOM_RIGHT) """ The alignment of this attribution on the map. """ bgcolor: ft.ColorValue = ft.Colors.SURFACE """ - The color of the box containing the `text`. + The color of the box containing the [`text`][..]. """ - on_click: ft.OptionalControlEventHandler["SimpleAttribution"] = None + on_click: Optional[ft.ControlEventHandler["SimpleAttribution"]] = None """Fired when this attribution is clicked/pressed.""" diff --git a/src/flet_map/source_attribution.py b/src/flet_map/source_attribution.py index 06aff74..4cc81fb 100644 --- a/src/flet_map/source_attribution.py +++ b/src/flet_map/source_attribution.py @@ -6,7 +6,7 @@ __all__ = ["ImageSourceAttribution", "SourceAttribution", "TextSourceAttribution"] -@dataclass(kw_only=True) +@dataclass class SourceAttribution(ft.BaseControl): """ Abstract class for source attribution controls: @@ -19,8 +19,10 @@ class SourceAttribution(ft.BaseControl): @ft.control("ImageSourceAttribution") class ImageSourceAttribution(SourceAttribution): """ - An image attribution permanently displayed adjacent to the open/close icon of a [`RichAttribution`][(p).] control. - For it to be displayed, it should be part of a [`RichAttribution.attributions`][(p).] list. + An image attribution permanently displayed adjacent to the + open/close icon of a [`RichAttribution`][(p).] control. + For it to be displayed, it should be part of a + [`RichAttribution.attributions`][(p).] list. Raises: AssertionError: If the [`image`][(c).] is not visible. @@ -29,7 +31,7 @@ class ImageSourceAttribution(SourceAttribution): image: ft.Image """ The `Image` to be displayed. - + Note: Must be provided and visible. """ @@ -37,13 +39,14 @@ class ImageSourceAttribution(SourceAttribution): height: ft.Number = 24.0 """ The height of the image. - Should be the same as [`RichAttribution.permanent_height`][(p).], otherwise layout issues may occur. + Should be the same as [`RichAttribution.permanent_height`][(p).], + otherwise layout issues may occur. """ tooltip: Optional[str] = None """Tooltip text to be displayed when the image is hovered over.""" - on_click: ft.OptionalControlEventHandler["ImageSourceAttribution"] = None + on_click: Optional[ft.ControlEventHandler["ImageSourceAttribution"]] = None """Fired when this attribution is clicked/pressed.""" def before_update(self): @@ -55,7 +58,8 @@ def before_update(self): class TextSourceAttribution(SourceAttribution): """ A text source attribution displayed on the Map. - For it to be displayed, it should be part of a [`RichAttribution.attributions`][(p).] list. + For it to be displayed, it should be part of a + [`RichAttribution.attributions`][(p).] list. """ text: str @@ -69,5 +73,5 @@ class TextSourceAttribution(SourceAttribution): Whether to add the '©' character to the start of [`text`][..] automatically. """ - on_click: ft.OptionalControlEventHandler["TextSourceAttribution"] = None + on_click: Optional[ft.ControlEventHandler["TextSourceAttribution"]] = None """Fired when this attribution is clicked/pressed.""" diff --git a/src/flet_map/tile_layer.py b/src/flet_map/tile_layer.py index dbc2210..84b54c1 100644 --- a/src/flet_map/tile_layer.py +++ b/src/flet_map/tile_layer.py @@ -1,5 +1,5 @@ from dataclasses import field -from typing import Dict, List, Optional +from typing import Optional import flet as ft @@ -20,17 +20,20 @@ class TileLayer(MapLayer): Displays square raster images in a continuous grid, sourced from the provided [`url_template`][(c).] and [`fallback_url`][(c).]. - Typically the first layer to be added to a [`Map`][(p).], as it provides the tiles on which + Typically the first layer to be added to a [`Map`][(p).], + as it provides the tiles on which other layers are displayed. Raises: AssertionError: If one or more of the following is negative: - [`tile_size`][(c).], [`min_native_zoom`][(c).], [`max_native_zoom`][(c).], [`zoom_offset`][(c).], [`max_zoom`][(c).], [`min_zoom`][(c).] + [`tile_size`][(c).], [`min_native_zoom`][(c).], + [`max_native_zoom`][(c).], [`zoom_offset`][(c).], + [`max_zoom`][(c).], [`min_zoom`][(c).] """ url_template: str """ - The URL template is a string that contains placeholders, + The URL template is a string that contains placeholders, which, when filled in, create a URL/URI to a specific tile. """ @@ -38,20 +41,21 @@ class TileLayer(MapLayer): """ Fallback URL template, used if an error occurs when fetching tiles from the [`url_template`][..]. - + Note that specifying this (non-none) will result in tiles not being cached in memory. This is to avoid issues where the [`url_template`][..] is flaky, to prevent different tilesets being displayed at the same time. - - It is expected that this follows the same retina support behaviour as [`url_template`][..]. + + It is expected that this follows the same retina support behaviour + as [`url_template`][..]. """ - subdomains: List[str] = field(default_factory=lambda: ["a", "b", "c"]) + subdomains: list[str] = field(default_factory=lambda: ["a", "b", "c"]) """ List of subdomains used in the URL template. - For example, if [`subdomains`][..] is set to `["a", "b", "c"]` and the - `url_template` is `"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"`, + For example, if [`subdomains`][..] is set to `["a", "b", "c"]` and the + `url_template` is `"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"`, the resulting tile URLs will be: - `"https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"` @@ -61,7 +65,7 @@ class TileLayer(MapLayer): tile_bounds: Optional[MapLatitudeLongitudeBounds] = None """ - Defines the bounds of the map. + Defines the bounds of the map. Only tiles that fall within these bounds will be loaded. """ @@ -69,7 +73,7 @@ class TileLayer(MapLayer): """ The size in pixels of each tile image. Should be a positive power of 2. - + Note: Must be greater than or equal to `0.0`. """ @@ -80,10 +84,10 @@ class TileLayer(MapLayer): Tiles from below this zoom level will not be displayed, instead tiles at this zoom level will be displayed and scaled. - + This should usually be 0 (as default), as most tile sources will support zoom levels onwards from this. - + Note: Must be greater than or equal to `0.0`. """ @@ -94,23 +98,24 @@ class TileLayer(MapLayer): Tiles from above this zoom level will not be displayed, instead tiles at this zoom level will be displayed and scaled. - + Most tile servers support up to zoom level `19`, which is the default. Otherwise, this should be specified. - + Note: Must be greater than or equal to `0.0`. """ zoom_reverse: bool = False """ - Whether the zoom number used in tile URLs will be reversed (`max_zoom - zoom` instead of `zoom`). + Whether the zoom number used in tile URLs will be reversed + (`max_zoom - zoom` instead of `zoom`). """ zoom_offset: ft.Number = 0.0 """ The zoom number used in tile URLs will be offset with this value. - + Note: Must be greater than or equal to `0.0`. """ @@ -137,14 +142,15 @@ class TileLayer(MapLayer): enable_retina_mode: bool = False """ Whether to enable retina mode. - Retina mode improves the resolution of map tiles, particularly on high density displays. + Retina mode improves the resolution of map tiles, particularly on + high density displays. """ - additional_options: Dict[str, str] = field(default_factory=dict) + additional_options: dict[str, str] = field(default_factory=dict) """ Static information that should replace placeholders in the [`url_template`][..]. Applying API keys, for example, is a good usecase of this parameter. - + Example: ```python TileLayer( @@ -162,12 +168,12 @@ class TileLayer(MapLayer): The maximum zoom level up to which this layer will be displayed (inclusive). The main usage for this property is to display a different `TileLayer` when zoomed far in. - + Prefer [`max_native_zoom`][..] for setting the maximum zoom level supported by the - tile source. - + tile source. + Typically set to infinity so that there are tiles always displayed. - + Note: Must be greater than or equal to `0.0`. """ @@ -176,7 +182,7 @@ class TileLayer(MapLayer): """ The minimum zoom level at which this layer is displayed (inclusive). Typically `0.0`. - + Note: Must be greater than or equal to `0.0`. """ @@ -184,21 +190,21 @@ class TileLayer(MapLayer): error_image_src: Optional[str] = None """ The source of the tile image to show in place of the tile that failed to load. - + See [`on_image_error`][..] property for details on the error. """ - evict_error_tile_strategy: Optional[ - TileLayerEvictErrorTileStrategy - ] = TileLayerEvictErrorTileStrategy.NONE + evict_error_tile_strategy: Optional[TileLayerEvictErrorTileStrategy] = ( + TileLayerEvictErrorTileStrategy.NONE + ) """ - If a tile was loaded with error, + If a tile was loaded with error, the tile provider will be asked to evict the image based on this strategy. """ display_mode: TileDisplay = field(default_factory=lambda: FadeInTileDisplay()) """ - + Defines how tiles are displayed on the map. """ @@ -207,11 +213,12 @@ class TileLayer(MapLayer): The package name of the user agent. """ - on_image_error: ft.OptionalControlEventHandler["TileLayer"] = None + on_image_error: Optional[ft.ControlEventHandler["TileLayer"]] = None """ Fires if an error occurs when fetching the tiles. - - Event handler argument `data` property contains information about the error. + + Event handler argument [`data`][flet.Event.data] property contains + information about the error. """ def before_update(self): @@ -220,10 +227,12 @@ def before_update(self): f"tile_size must be greater than or equal to 0, got {self.tile_size}" ) assert self.min_native_zoom >= 0, ( - f"min_native_zoom must be greater than or equal to 0, got {self.min_native_zoom}" + f"min_native_zoom must be greater than or equal to 0, " + f"got {self.min_native_zoom}" ) assert self.max_native_zoom >= 0, ( - f"max_native_zoom must be greater than or equal to 0, got {self.max_native_zoom}" + f"max_native_zoom must be greater than or equal to 0, " + f"got {self.max_native_zoom}" ) assert self.zoom_offset >= 0, ( f"zoom_offset must be greater than or equal to 0, got {self.zoom_offset}" diff --git a/src/flet_map/types.py b/src/flet_map/types.py index 9938525..132c66d 100644 --- a/src/flet_map/types.py +++ b/src/flet_map/types.py @@ -1,36 +1,39 @@ from dataclasses import dataclass, field from enum import Enum, IntFlag -from typing import List, Optional +from typing import TYPE_CHECKING, Optional import flet as ft +if TYPE_CHECKING: + from .map import Map # noqa + __all__ = [ - "TileLayerEvictErrorTileStrategy", "AttributionAlignment", - "PatternFit", "Camera", - "StrokePattern", - "SolidStrokePattern", + "CameraFit", + "CursorKeyboardRotationConfiguration", + "CursorRotationBehaviour", "DashedStrokePattern", "DottedStrokePattern", - "MapLatitudeLongitude", - "MapLatitudeLongitudeBounds", - "InteractionFlag", - "MultiFingerGesture", + "FadeInTileDisplay", + "InstantaneousTileDisplay", "InteractionConfiguration", + "InteractionFlag", + "KeyboardConfiguration", + "MapEvent", "MapEventSource", - "CameraFit", - "MapTapEvent", "MapHoverEvent", - "MapPositionChangeEvent", + "MapLatitudeLongitude", + "MapLatitudeLongitudeBounds", "MapPointerEvent", - "MapEvent", + "MapPositionChangeEvent", + "MapTapEvent", + "MultiFingerGesture", + "PatternFit", + "SolidStrokePattern", + "StrokePattern", "TileDisplay", - "InstantaneousTileDisplay", - "FadeInTileDisplay", - "KeyboardConfiguration", - "CursorRotationBehaviour", - "CursorKeyboardRotationConfiguration", + "TileLayerEvictErrorTileStrategy", ] @@ -54,7 +57,8 @@ class TileLayerEvictErrorTileStrategy(Enum): """ Evict images for tiles which failed to load and: - do not belong to the current zoom level AND/OR - - are not visible, respecting the pruning buffer (the maximum of the `keep_buffer` and `pan_buffer`). + - are not visible, respecting the pruning buffer + (the maximum of the `keep_buffer` and `pan_buffer`). """ @@ -78,7 +82,7 @@ class PatternFit(Enum): """ Don't apply any specific fit to the pattern - repeat exactly as specified, and stop when the last point is reached. - + Not recommended, as it may leave a gap between the final segment and the last point, making it unclear where the line ends. """ @@ -107,8 +111,8 @@ class PatternFit(Enum): Uses the pattern exactly, truncating the final dash if it does not fit, or extending the final dash to the last point if it would not normally reach that point (there is a gap at that location). - - Only useful when working with [`DashedStrokePattern`][(p).]. + + Only useful when working with [`DashedStrokePattern`][(p).]. Similar to `APPEND_DOT` for `DottedStrokePattern`. """ @@ -141,7 +145,7 @@ class Camera: """ -@dataclass(kw_only=True) +@dataclass class StrokePattern: """ Determines whether a stroke should be solid, dotted, or dashed, @@ -155,7 +159,7 @@ class StrokePattern: - [`DottedStrokePattern`][(p).] """ - _type: str = "" + _type: Optional[str] = field(init=False, repr=False, compare=False, default=None) @dataclass @@ -172,19 +176,21 @@ class DashedStrokePattern(StrokePattern): A stroke pattern of alternating dashes and gaps, defined by [`segments`][(c).]. Raises: - AssertionError: If `segments` does not contain at least two items, or has an odd length. + AssertionError: If [`segments`][(c).] does not contain at least two items, + or has an odd length. """ - segments: List[ft.Number] = field(default_factory=list) + segments: list[ft.Number] = field(default_factory=list) """ A list of alternating dash and gap lengths, in pixels. - + Note: Must contain at least two items, and have an even length. """ pattern_fit: PatternFit = PatternFit.SCALE_UP """ - Determines how this stroke pattern should be fit to a line when their lengths are not equal or multiples. + Determines how this stroke pattern should be fit to a line when their lengths + are not equal or multiples. """ def __post_init__(self): @@ -204,25 +210,27 @@ class DottedStrokePattern(StrokePattern): spacing_factor: ft.Number = 1.5 """ - The multiplier used to calculate the spacing between dots in a dotted polyline, + The multiplier used to calculate the spacing between dots in a dotted polyline, with respect to `Polyline.stroke_width` / `Polygon.border_stroke_width`. - A value of `1.0` will result in spacing equal to the `stroke_width`. + A value of `1.0` will result in spacing equal to the `stroke_width`. Increasing the value increases the spacing with the same scaling. - + May also be scaled by the use of [`PatternFit.SCALE_UP`][(p).]. - + Note: Must be non-negative. """ pattern_fit: PatternFit = PatternFit.SCALE_UP """ - Determines how this stroke pattern should be fit to a line when their lengths are not equal or multiples. + Determines how this stroke pattern should be fit to a line when their + lengths are not equal or multiples. """ def __post_init__(self): - assert ( - self.spacing_factor > 0 - ), "spacing_factor must be greater than or equal to 0.0" + assert self.spacing_factor > 0, ( + f"spacing_factor must be greater than or equal to 0.0, " + f"got {self.spacing_factor}" + ) self._type = "dotted" @@ -303,7 +311,8 @@ class InteractionFlag(IntFlag): def has_flag(left_flags: int, right_flags: int) -> bool: """ Returns: - `True` if `left_flags` has at least one member in `right_flags` (intersection). + `True` if `left_flags` has at least one member + in `right_flags` (intersection). """ return left_flags & right_flags != 0 @@ -312,7 +321,9 @@ def has_multi_finger(flags: int) -> bool: """ Returns: `True` if any multi-finger gesture flags - ([`MultiFingerGesture.PINCH_MOVE`][(p).], [`MultiFingerGesture.PINCH_ZOOM`][(p).], [`MultiFingerGesture.ROTATE`][(p).]) are enabled. + ([`MultiFingerGesture.PINCH_MOVE`][(p).], + [`MultiFingerGesture.PINCH_ZOOM`][(p).], + [`MultiFingerGesture.ROTATE`][(p).]) are enabled. """ return InteractionFlag.has_flag( flags, @@ -398,7 +409,9 @@ class MultiFingerGesture(IntFlag): """Pinch move gesture, which allows moving the map by dragging with two fingers.""" PINCH_ZOOM = 1 << 1 - """Pinch zoom gesture, which allows zooming in and out by pinching with two fingers.""" + """ + Pinch zoom gesture, which allows zooming in and out by pinching with two fingers. + """ ROTATE = 1 << 2 """Rotate gesture, which allows rotating the map by twisting two fingers.""" @@ -411,43 +424,50 @@ class MultiFingerGesture(IntFlag): class InteractionConfiguration: enable_multi_finger_gesture_race: bool = False """ - If `True`, then [`rotation_threshold`][..] and [`pinch_zoom_threshold`][..] and [`pinch_move_threshold`][..] will race. - If multiple gestures win at the same time, - then precedence: [`pinch_zoom_win_gestures`][..] > [`rotation_win_gestures`][..] > [`pinch_move_win_gestures`][..] + If `True`, then [`rotation_threshold`][..] and [`pinch_zoom_threshold`][..] + and [`pinch_move_threshold`][..] will race. + If multiple gestures win at the same time, then precedence: + [`pinch_zoom_win_gestures`][..] > [`rotation_win_gestures`][..] > [`pinch_move_win_gestures`][..] """ pinch_move_threshold: ft.Number = 40.0 """ - Map starts to move when `pinch_move_threshold` has been achieved - or another multi finger gesture wins which allows [`MultiFingerGesture.PINCH_MOVE`][(p).]. - - Note: - If [`InteractionConfiguration.flags`][(p).] doesn't contain [`InteractionFlag.PINCH_MOVE`][(p).] + Map starts to move when `pinch_move_threshold` has been achieved + or another multi finger gesture wins which allows + [`MultiFingerGesture.PINCH_MOVE`][(p).]. + + Note: + If [`InteractionConfiguration.flags`][(p).] doesn't contain + [`InteractionFlag.PINCH_MOVE`][(p).] or [`enable_multi_finger_gesture_race`][..] is false then pinch move cannot win. """ scroll_wheel_velocity: ft.Number = 0.005 """ - The used velocity how fast the map should zoom in or out by scrolling with the scroll wheel of a mouse. + The used velocity how fast the map should zoom in or out by scrolling + with the scroll wheel of a mouse. """ pinch_zoom_threshold: ft.Number = 0.5 """ - Map starts to zoom when `pinch_zoom_threshold` has been achieved or - another multi finger gesture wins which allows [`MultiFingerGesture.PINCH_ZOOM`][(p).]. - - Note: - If [`InteractionConfiguration.flags`][(p).] doesn't contain [`InteractionFlag.PINCH_ZOOM`][(p).] + Map starts to zoom when `pinch_zoom_threshold` has been achieved or + another multi finger gesture wins which allows + [`MultiFingerGesture.PINCH_ZOOM`][(p).]. + + Note: + If [`InteractionConfiguration.flags`][(p).] + doesn't contain [`InteractionFlag.PINCH_ZOOM`][(p).] or [`enable_multi_finger_gesture_race`][..] is false then zoom cannot win. """ rotation_threshold: ft.Number = 20.0 """ - Map starts to rotate when `rotation_threshold` has been achieved or + Map starts to rotate when `rotation_threshold` has been achieved or another multi finger gesture wins which allows [`MultiFingerGesture.ROTATE`][(p).]. - - Note: - If [`InteractionConfiguration.flags`][(p).] doesn't contain [`InteractionFlag.ROTATE`][(p).] + + Note: + If [`InteractionConfiguration.flags`][(p).] + doesn't contain [`InteractionFlag.ROTATE`][(p).] or [`enable_multi_finger_gesture_race`][..] is false then rotate cannot win. """ @@ -458,8 +478,8 @@ class InteractionConfiguration: rotation_win_gestures: MultiFingerGesture = MultiFingerGesture.ROTATE """ - When [`rotation_threshold`[..] wins over [`pinch_zoom_threshold`[..] and - [`pinch_move_threshold`[..] then `rotation_win_gestures` gestures will be used. + When [`rotation_threshold`[..] wins over [`pinch_zoom_threshold`[..] and + [`pinch_move_threshold`[..] then `rotation_win_gestures` gestures will be used. """ pinch_move_win_gestures: MultiFingerGesture = ( @@ -467,9 +487,11 @@ class InteractionConfiguration: ) """ When [`pinch_move_threshold`][..] wins over [`rotation_threshold`][..] - and [`pinch_zoom_threshold`][..] then `pinch_move_win_gestures` gestures will be used. - - By default [`MultiFingerGesture.PINCH_MOVE`][(p).] and [`MultiFingerGesture.PINCH_ZOOM`][(p).] + and [`pinch_zoom_threshold`][..] then `pinch_move_win_gestures` gestures + will be used. + + By default [`MultiFingerGesture.PINCH_MOVE`][(p).] + and [`MultiFingerGesture.PINCH_ZOOM`][(p).] gestures will take effect see [`MultiFingerGesture`][(p).] for custom settings. """ @@ -477,10 +499,12 @@ class InteractionConfiguration: MultiFingerGesture.PINCH_ZOOM | MultiFingerGesture.PINCH_MOVE ) """ - When [`pinch_zoom_threshold`][..] wins over [`rotation_threshold`][..] and [`pinch_move_threshold`][..] + When [`pinch_zoom_threshold`][..] wins over [`rotation_threshold`][..] + and [`pinch_move_threshold`][..] then `pinch_zoom_win_gestures` gestures will be used. - - By default [`MultiFingerGesture.PINCH_ZOOM`][(p).] and [`MultiFingerGesture.PINCH_MOVE`][(p).] + + By default [`MultiFingerGesture.PINCH_ZOOM`][(p).] + and [`MultiFingerGesture.PINCH_MOVE`][(p).] gestures will take effect see `MultiFingerGesture` for custom settings. """ @@ -489,7 +513,7 @@ class InteractionConfiguration: ) """ Options to configure how keyboard keys may be used to control the map. - + Keyboard movements using the arrow keys are enabled by default. """ @@ -497,7 +521,8 @@ class InteractionConfiguration: field(default_factory=lambda: CursorKeyboardRotationConfiguration()) ) """ - Options to control the keyboard and mouse cursor being used together to rotate the map. + Options to control the keyboard and mouse cursor being used together + to rotate the map. """ @@ -542,12 +567,13 @@ class MapEventSource(Enum): FLING_ANIMATION_CONTROLLER = "flingAnimationController" """ - The `MapEvent` is caused by the `AnimationController` while performing the fling gesture. + The `MapEvent` is caused by the `AnimationController` while + performing the fling gesture. """ DOUBLE_TAP_ZOOM_ANIMATION_CONTROLLER = "doubleTapZoomAnimationController" """ - The `MapEvent` is caused by the `AnimationController` + The `MapEvent` is caused by the `AnimationController` while performing the double tap zoom in animation. """ @@ -570,30 +596,35 @@ class MapEventSource(Enum): """The `MapEvent` is caused by a 'CTRL + drag' rotation gesture.""" KEYBOARD = "keyboard" - """The `MapEvent` is caused by a keyboard key. See [`KeyboardConfiguration`][(p).] for details.""" + """ + The `MapEvent` is caused by a keyboard key. + See [`KeyboardConfiguration`][(p).] for details. + """ @dataclass class CameraFit: """ - Defines how the camera should fit the bounds or coordinates, depending on which one was provided. + Defines how the camera should fit the bounds or coordinates, + depending on which one was provided. Raises: - AssertionError: If both [`bounds`][(c).] and [`coordinates`][(c).] are `None` or not `None`. + AssertionError: If both [`bounds`][(c).] and [`coordinates`][(c).] + are `None` or not `None`. """ bounds: Optional[MapLatitudeLongitudeBounds] = None """ The bounds which the camera should contain once it is fitted. - + Note: If this is not `None`, [`coordinates`][..] should be `None`, and vice versa. """ - coordinates: Optional[List[MapLatitudeLongitude]] = None + coordinates: Optional[list[MapLatitudeLongitude]] = None """ The coordinates which the camera should contain once it is fitted. - + Note: If this is not `None`, [`bounds`][..] should be `None`, and vice versa. """ @@ -601,7 +632,9 @@ class CameraFit: max_zoom: Optional[ft.Number] = None """ The inclusive upper zoom limit used for the resulting fit. - If the zoom level calculated for the fit exceeds the `max_zoom` value, `max_zoom` will be used instead. + + If the zoom level calculated for the fit exceeds the `max_zoom` value, + `max_zoom` will be used instead. """ min_zoom: ft.Number = 0.0 @@ -615,7 +648,8 @@ class CameraFit: force_integer_zoom_level: bool = False """ - Whether the zoom level of the resulting fit should be rounded to the nearest integer level. + Whether the zoom level of the resulting fit should be rounded to the + nearest integer level. """ def __post_init__(self): @@ -625,31 +659,31 @@ def __post_init__(self): @dataclass -class MapTapEvent(ft.TapEvent): +class MapTapEvent(ft.TapEvent["Map"]): coordinates: MapLatitudeLongitude """Coordinates of the point at which the tap occured.""" @dataclass -class MapHoverEvent(ft.HoverEvent): +class MapHoverEvent(ft.HoverEvent["Map"]): coordinates: MapLatitudeLongitude @dataclass -class MapPositionChangeEvent(ft.Event[ft.EventControlType]): +class MapPositionChangeEvent(ft.Event["Map"]): coordinates: MapLatitudeLongitude camera: Camera has_gesture: bool @dataclass -class MapPointerEvent(ft.PointerEvent): +class MapPointerEvent(ft.PointerEvent["Map"]): coordinates: MapLatitudeLongitude """Coordinates of the point at which the tap occured.""" @dataclass -class MapEvent(ft.Event[ft.EventControlType]): +class MapEvent(ft.Event["Map"]): source: MapEventSource """Who/what issued the event.""" @@ -657,7 +691,7 @@ class MapEvent(ft.Event[ft.EventControlType]): """The map camera after the event.""" -@dataclass(kw_only=True) +@dataclass class TileDisplay: """ Defines how the tile should get displayed on the map. @@ -669,7 +703,7 @@ class TileDisplay: - `FadeInTileDisplay` """ - _type: str = "" + _type: Optional[str] = field(init=False, repr=False, compare=False, default=None) @dataclass @@ -682,9 +716,9 @@ class InstantaneousTileDisplay(TileDisplay): """ def __post_init__(self): - assert ( - 0.0 <= self.opacity <= 1.0 - ), "start_opacity must be between 0.0 and 1.0 (inclusive)" + assert 0.0 <= self.opacity <= 1.0, ( + "start_opacity must be between 0.0 and 1.0 (inclusive)" + ) self._type = "instantaneous" @@ -710,12 +744,12 @@ class FadeInTileDisplay(TileDisplay): """ def __post_init__(self): - assert ( - 0.0 <= self.start_opacity <= 1.0 - ), "start_opacity must be between 0.0 and 1.0 (inclusive)" - assert ( - 0.0 <= self.reload_start_opacity <= 1.0 - ), "reload_start_opacity must be between 0.0 and 1.0 (inclusive)" + assert 0.0 <= self.start_opacity <= 1.0, ( + "start_opacity must be between 0.0 and 1.0 (inclusive)" + ) + assert 0.0 <= self.reload_start_opacity <= 1.0, ( + "reload_start_opacity must be between 0.0 and 1.0 (inclusive)" + ) self._type = "fadein" @@ -733,13 +767,15 @@ class KeyboardConfiguration: scales the velocity of the concerned gesture. Info: - See [`CursorKeyboardRotationConfiguration`][(p).] for options to control the keyboard and + See [`CursorKeyboardRotationConfiguration`][(p).] for options + to control the keyboard and mouse cursor being used together to rotate the map. """ autofocus: bool = True """ - Whether to request focus as soon as the map control appears (and to enable keyboard controls). + Whether to request focus as soon as the map control appears + (and to enable keyboard controls). """ animation_curve_duration: ft.DurationValue = field( @@ -747,7 +783,8 @@ class KeyboardConfiguration: ) """ Duration of the curved (`ft.Curve.EASE_IN`) portion of the animation occuring - after a key down event (and after a key up event if [`animation_curve_reverse_duration`][..] is `None`) + after a key down event (and after a key up event if + [`animation_curve_reverse_duration`][..] is `None`) """ animation_curve_reverse_duration: Optional[ft.DurationValue] = field( @@ -762,7 +799,8 @@ class KeyboardConfiguration: animation_curve_curve: bool = True """ - Curve of the curved portion of the animation occuring after key down and key up events. + Curve of the curved portion of the animation occuring after + key down and key up events. """ enable_arrow_keys_panning: bool = True @@ -772,47 +810,53 @@ class KeyboardConfiguration: enable_qe_rotating: bool = True """ - Whether to allow the Q & E keys (*) to rotate the map (Q rotates anticlockwise, E rotates clockwise). - - QE are only the physical and logical keys on QWERTY keyboards. - On non- QWERTY keyboards, such as AZERTY, + Whether to allow the `Q` & `E` keys (*) to rotate the map (`Q` rotates + anticlockwise, `E` rotates clockwise). + + QE are only the physical and logical keys on QWERTY keyboards. + On non- QWERTY keyboards, such as AZERTY, the keys in the same position as on the QWERTY keyboard is used (ie. AE on AZERTY). """ enable_rf_zooming: bool = True """ - Whether to allow the R & F keys to zoom the map (R zooms IN (increases zoom level), F zooms OUT (decreases zoom level)). - - RF are only the physical and logical keys on QWERTY keyboards. - On non- QWERTY keyboards, such as AZERTY, + Whether to allow the `R` & `F` keys to zoom the map (`R` zooms IN + (increases zoom level), `F` zooms OUT (decreases zoom level)). + + RF are only the physical and logical keys on QWERTY keyboards. + On non- QWERTY keyboards, such as AZERTY, the keys in the same position as on the QWERTY keyboard is used (ie. RF on AZERTY). """ enable_wasd_panning: bool = True """ - Whether to allow the W, A, S, D keys (*) to pan the map (in the directions UP, LEFT, DOWN, RIGHT respectively). - - WASD are only the physical and logical keys on QWERTY keyboards. - On non- QWERTY keyboards, such as AZERTY, - the keys in the same position as on the QWERTY keyboard is used (ie. ZQSD on AZERTY). - - If enabled, it is recommended to enable `enable_arrow_keys_panning` to provide panning functionality easily for left handed users. + Whether to allow the `W`, `A`, `S`, `D` keys (*) to pan the map + (in the directions UP, LEFT, DOWN, RIGHT respectively). + + WASD are only the physical and logical keys on QWERTY keyboards. + On non- QWERTY keyboards, such as AZERTY, + the keys in the same position as on the QWERTY keyboard is + used (ie. ZQSD on AZERTY). + + If enabled, it is recommended to enable `enable_arrow_keys_panning` + to provide panning functionality easily for left handed users. """ leap_max_of_curve_component: ft.Number = 0.6 """ The percentage (0.0 - 1.0) of the curve animation component that is driven to (from 0), then in reverse from (to 0). - + Reducing means the leap occurs quicker (assuming a consistent curve animation duration). Also see `*_leap_velocity_multiplier` properties to change the distance of the leap assuming a consistent leap duration. - - For example, if set to 1, then the leap will take `animation_curve_duration + animation_curve_reverse_duration` + + For example, if set to 1, then the leap will take + `animation_curve_duration + animation_curve_reverse_duration` to complete. - Must be greater than 0 and less than or equal to 1. - To disable leaping, or change the maximum length of the key press + Must be greater than 0 and less than or equal to 1. + To disable leaping, or change the maximum length of the key press that will trigger a leap, see [`perform_leap_trigger_duration`[..]. """ @@ -820,7 +864,7 @@ class KeyboardConfiguration: """ The maximum angular difference to apply per frame to the camera's rotation during a rotation animation. - + Measured in degrees. Negative numbers will flip the standard rotation keys. """ @@ -828,7 +872,7 @@ class KeyboardConfiguration: """ The maximum zoom level difference to apply per frame to the camera's zoom level during a zoom animation. - + Measured in zoom levels. Negative numbers will flip the standard zoom keys. """ @@ -836,9 +880,9 @@ class KeyboardConfiguration: """ The amount to scale the panning offset velocity by during a leap animation. - The larger the number, the larger the movement during a leap. + The larger the number, the larger the movement during a leap. To change the duration of a leap, see [`leap_max_of_curve_component`[..]. - + This may cause the pan velocity to exceed [`max_pan_velocity`[..]. """ @@ -848,17 +892,17 @@ class KeyboardConfiguration: The larger the number, the larger the rotation difference during a leap. To change the duration of a leap, see [`leap_max_of_curve_component`[..]. - + This may cause the pan velocity to exceed [`max_rotate_velocity`[..]. """ zoom_leap_velocity_multiplier: ft.Number = 3 """ The amount to scale the zooming velocity by during a leap animation. - + The larger the number, the larger the zoom difference during a leap. To change the duration of a leap, see [`leap_max_of_curve_component`[..]. - + This may cause the pan velocity to exceed [`max_zoom_velocity`[..]. """ @@ -868,10 +912,10 @@ class KeyboardConfiguration: """ Maximum duration between the key down and key up events of an animation which will trigger a 'leap'. - + To customize the leap itself, see the [`leap_max_of_curve_component`[..] & `[*leap_velocity_multiplier`[..] properties. - + Set to `None` to disable leaping. """ @@ -881,7 +925,8 @@ def disabled(cls) -> "KeyboardConfiguration": Disable keyboard control of the map. Info: - `CursorKeyboardRotationConfiguration` may still be active, and is not disabled if this is disabled. + `CursorKeyboardRotationConfiguration` may still be active, + and is not disabled if this is disabled. """ return KeyboardConfiguration( enable_arrow_keys_panning=False, @@ -901,7 +946,8 @@ class CursorRotationBehaviour(Enum): OFFSET = "offset" """ - Offset the current rotation of the map to the angle at which the user drags their cursor. + Offset the current rotation of the map to the angle at which the + user drags their cursor. """ SET_NORTH = "setNorth" @@ -922,17 +968,19 @@ class CursorKeyboardRotationConfiguration: set_north_on_click: bool = True """ - Whether to set the North of the map to the clicked angle, - when the user clicks their mouse without dragging - (a `on_pointer_down` event followed by `on_pointer_up` without a change in rotation). + Whether to set the North of the map to the clicked angle, + when the user clicks their mouse without dragging + (a `on_pointer_down` event followed by `on_pointer_up` + without a change in rotation). """ behavior: CursorRotationBehaviour = CursorRotationBehaviour.OFFSET """ The behaviour of the cursor/keyboard rotation function in terms of the angle that the map is rotated to. - - Does not disable cursor/keyboard rotation, or adjust its triggers: see `is_key_trriger`. + + Does not disable cursor/keyboard rotation, or + adjust its triggers: see `is_key_trriger`. """ # TODO diff --git a/src/flutter/flet_map/pubspec.yaml b/src/flutter/flet_map/pubspec.yaml index 6662430..1a61760 100644 --- a/src/flutter/flet_map/pubspec.yaml +++ b/src/flutter/flet_map/pubspec.yaml @@ -29,4 +29,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 \ No newline at end of file + flutter_lints: ^3.0.0 From 38b8d15b382fa1a3c323efec7a6f06f6b0f5883d Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 17:34:28 +0200 Subject: [PATCH 2/9] remove mdformat from pre-commit --- .pre-commit-config.yaml | 7 ------- src/flet_map/map.py | 4 +++- src/flet_map/marker_layer.py | 10 +++++++--- src/flet_map/polygon_layer.py | 3 ++- src/flet_map/types.py | 11 +++++++---- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0997bef..8f2d1ab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,13 +8,6 @@ repos: # Run the formatter. - id: ruff-format - - repo: https://github.com/executablebooks/mdformat - rev: 0.7.22 - hooks: - - id: mdformat - additional_dependencies: - - mdformat-ruff - - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: diff --git a/src/flet_map/map.py b/src/flet_map/map.py index 3ae3fc5..ecc33e3 100644 --- a/src/flet_map/map.py +++ b/src/flet_map/map.py @@ -451,7 +451,9 @@ async def move_to_async( Raises: AssertionError: If `zoom` is not `None` and is negative. """ - assert zoom is None or zoom >= 0, "zoom must be greater than or equal to zero" + assert zoom is None or zoom >= 0, ( + f"zoom must be greater than or equal to zero, got {zoom}" + ) await self._invoke_method_async( method_name="move_to", arguments={ diff --git a/src/flet_map/marker_layer.py b/src/flet_map/marker_layer.py index d6055ca..e73758e 100644 --- a/src/flet_map/marker_layer.py +++ b/src/flet_map/marker_layer.py @@ -70,14 +70,18 @@ class Marker(ft.Control): """ Alignment of the marker relative to the normal center at [`coordinates`][..]. - Defaults to the value of the parent [`MarkerLayer.alignment`][(m).]. + Defaults to the value of the parent [`MarkerLayer.alignment`][(p).]. """ def before_update(self): super().before_update() assert self.content.visible, "content must be visible" - assert self.height >= 0, "height must be greater than or equal to 0" - assert self.width >= 0, "width must be greater than or equal to 0" + assert self.height >= 0, ( + f"height must be greater than or equal to 0, got {self.height}" + ) + assert self.width >= 0, ( + f"width must be greater than or equal to 0, got {self.width}" + ) @ft.control("MarkerLayer") diff --git a/src/flet_map/polygon_layer.py b/src/flet_map/polygon_layer.py index 2e3c5b3..0b78a9e 100644 --- a/src/flet_map/polygon_layer.py +++ b/src/flet_map/polygon_layer.py @@ -76,7 +76,8 @@ class PolygonMarker(ft.Control): def before_update(self): super().before_update() assert self.border_stroke_width >= 0, ( - "border_stroke_width must be greater than or equal to 0" + f"border_stroke_width must be greater than or equal to 0, " + f"got {self.border_stroke_width}" ) diff --git a/src/flet_map/types.py b/src/flet_map/types.py index 132c66d..24b8367 100644 --- a/src/flet_map/types.py +++ b/src/flet_map/types.py @@ -194,7 +194,9 @@ class DashedStrokePattern(StrokePattern): """ def __post_init__(self): - assert len(self.segments) >= 2, "segments must contain at least two items" + assert len(self.segments) >= 2, ( + f"segments must contain at least two items, got {len(self.segments)}" + ) assert len(self.segments) % 2 == 0, "segments must have an even length" self._type = "dashed" @@ -717,7 +719,7 @@ class InstantaneousTileDisplay(TileDisplay): def __post_init__(self): assert 0.0 <= self.opacity <= 1.0, ( - "start_opacity must be between 0.0 and 1.0 (inclusive)" + f"opacity must be between 0.0 and 1.0 inclusive, got {self.opacity}" ) self._type = "instantaneous" @@ -745,10 +747,11 @@ class FadeInTileDisplay(TileDisplay): def __post_init__(self): assert 0.0 <= self.start_opacity <= 1.0, ( - "start_opacity must be between 0.0 and 1.0 (inclusive)" + f"start_opacity must be between 0.0 and 1.0 inclusive, got {self.start_opacity}" ) assert 0.0 <= self.reload_start_opacity <= 1.0, ( - "reload_start_opacity must be between 0.0 and 1.0 (inclusive)" + f"reload_start_opacity must be between 0.0 and 1.0 inclusive, " + f"got {self.reload_start_opacity}" ) self._type = "fadein" From 16abc8e8e13f8503e09705ed940d1b77e88ad40b Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 20:18:53 +0200 Subject: [PATCH 3/9] update mkdocs watch items | unshow `Event.get_event_field_type` --- mkdocs.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 829cf25..a9f018f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -131,9 +131,13 @@ validation: unrecognized_links: warn anchors: warn +# Watch watch: - docs - - src + - src/flet_map + - CHANGELOG.md + - LICENSE + - README.md # Plugins plugins: @@ -170,6 +174,7 @@ plugins: preload_modules: [ flet ] filters: - "!^_" # Exclude private members starting with only one underscore + - "!get_event_field_type" extensions: - griffe_modernized_annotations - griffe_warnings_deprecated From 660daee73fecd8065df10debd878b230bf883eba Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 20:23:32 +0200 Subject: [PATCH 4/9] fix separate_signature in Enums --- docs/types/attribution_alignment.md | 4 ++-- docs/types/cursor_rotation_behaviour.md | 4 ++-- docs/types/pattern_fit.md | 4 ++-- docs/types/tile_layer_evict_error_tile_strategy.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/types/attribution_alignment.md b/docs/types/attribution_alignment.md index 3d12fe2..d79b8e0 100644 --- a/docs/types/attribution_alignment.md +++ b/docs/types/attribution_alignment.md @@ -1,3 +1,3 @@ ::: flet_map.types.AttributionAlignment -options: -separate_signature: false + options: + separate_signature: false diff --git a/docs/types/cursor_rotation_behaviour.md b/docs/types/cursor_rotation_behaviour.md index cd3ebd4..2a68916 100644 --- a/docs/types/cursor_rotation_behaviour.md +++ b/docs/types/cursor_rotation_behaviour.md @@ -1,3 +1,3 @@ ::: flet_map.types.CursorRotationBehaviour -options: -separate_signature: false + options: + separate_signature: false diff --git a/docs/types/pattern_fit.md b/docs/types/pattern_fit.md index ba387b8..a7d6253 100644 --- a/docs/types/pattern_fit.md +++ b/docs/types/pattern_fit.md @@ -1,3 +1,3 @@ ::: flet_map.types.PatternFit -options: -separate_signature: false + options: + separate_signature: false diff --git a/docs/types/tile_layer_evict_error_tile_strategy.md b/docs/types/tile_layer_evict_error_tile_strategy.md index cb484f7..87b1cbe 100644 --- a/docs/types/tile_layer_evict_error_tile_strategy.md +++ b/docs/types/tile_layer_evict_error_tile_strategy.md @@ -1,3 +1,3 @@ ::: flet_map.types.TileLayerEvictErrorTileStrategy -options: -separate_signature: false + options: + separate_signature: false From 8d365ed8ed7399291415d47fc31a9e98fb73de50 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 20:50:42 +0200 Subject: [PATCH 5/9] test group all --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8da82a1..5deff20 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -53,7 +53,7 @@ jobs: - name: Install dependencies run: | uv pip install -e . - uv pip install --group docs + uv pip install --group all - name: Deploy to GitHub Pages run: uv run mkdocs gh-deploy --force From cc3bde9dfa870a554681b2d9e55f7c66cca61733 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 21:24:30 +0200 Subject: [PATCH 6/9] fixes --- docs/types/interaction_flag.md | 2 + docs/types/multi_finger_gesture.md | 2 + src/flet_map/types.py | 63 ++++++++++++++++++------------ 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/docs/types/interaction_flag.md b/docs/types/interaction_flag.md index fe567cb..7a0f3c0 100644 --- a/docs/types/interaction_flag.md +++ b/docs/types/interaction_flag.md @@ -1 +1,3 @@ ::: flet_map.types.InteractionFlag + options: + separate_signature: false diff --git a/docs/types/multi_finger_gesture.md b/docs/types/multi_finger_gesture.md index 1eb7516..20e654d 100644 --- a/docs/types/multi_finger_gesture.md +++ b/docs/types/multi_finger_gesture.md @@ -1 +1,3 @@ ::: flet_map.types.MultiFingerGesture + options: + separate_signature: false diff --git a/src/flet_map/types.py b/src/flet_map/types.py index 24b8367..8eb729b 100644 --- a/src/flet_map/types.py +++ b/src/flet_map/types.py @@ -266,8 +266,8 @@ class InteractionFlag(IntFlag): Flags to enable/disable certain interaction events on the map. Example: - - `InteractionFlag.ALL` to enable all events - - `InteractionFlag.NONE` to disable all events + - [`InteractionFlag.ALL`][(p).] to enable all events + - [`InteractionFlag.NONE`][(p).] to disable all events """ NONE = 0 @@ -314,7 +314,7 @@ def has_flag(left_flags: int, right_flags: int) -> bool: """ Returns: `True` if `left_flags` has at least one member - in `right_flags` (intersection). + in `right_flags` (intersection). """ return left_flags & right_flags != 0 @@ -323,9 +323,9 @@ def has_multi_finger(flags: int) -> bool: """ Returns: `True` if any multi-finger gesture flags - ([`MultiFingerGesture.PINCH_MOVE`][(p).], - [`MultiFingerGesture.PINCH_ZOOM`][(p).], - [`MultiFingerGesture.ROTATE`][(p).]) are enabled. + ([`MultiFingerGesture.PINCH_MOVE`][(p).], + [`MultiFingerGesture.PINCH_ZOOM`][(p).], + [`MultiFingerGesture.ROTATE`][(p).]) are enabled. """ return InteractionFlag.has_flag( flags, @@ -340,7 +340,8 @@ def has_multi_finger(flags: int) -> bool: def has_drag(flags: int) -> bool: """ Returns: - `True` if the [`DRAG`][..] interactive flag is enabled. + `True` if the [`DRAG`][flet_map.InteractionFlag.DRAG] interaction + flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.DRAG) @@ -348,7 +349,8 @@ def has_drag(flags: int) -> bool: def has_fling_animation(flags: int) -> bool: """ Returns: - `True` if the [`FLING_ANIMATION`][..] interactive flag is enabled. + `True` if the [`FLING_ANIMATION`][flet_map.InteractionFlag.FLING_ANIMATION] + interaction flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.FLING_ANIMATION) @@ -356,7 +358,8 @@ def has_fling_animation(flags: int) -> bool: def has_pinch_move(flags: int) -> bool: """ Returns: - `True` if the [`PINCH_MOVE`][..] interactive flag is enabled. + `True` if the [`PINCH_MOVE`][flet_map.InteractionFlag.PINCH_MOVE] + interaction flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.PINCH_MOVE) @@ -364,7 +367,8 @@ def has_pinch_move(flags: int) -> bool: def has_fling_pinch_zoom(flags: int) -> bool: """ Returns: - `True` if the [`PINCH_ZOOM`][..] interactive flag is enabled. + `True` if the [`PINCH_ZOOM`][flet_map.InteractionFlag.PINCH_ZOOM] + interaction flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.PINCH_ZOOM) @@ -372,7 +376,9 @@ def has_fling_pinch_zoom(flags: int) -> bool: def has_double_tap_drag_zoom(flags: int) -> bool: """ Returns: - `True` if the [`DOUBLE_TAP_DRAG_ZOOM`][..] interactive flag is enabled. + `True` if the + [`DOUBLE_TAP_DRAG_ZOOM`][flet_map.InteractionFlag.DOUBLE_TAP_DRAG_ZOOM] + interaction flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.DOUBLE_TAP_DRAG_ZOOM) @@ -380,7 +386,8 @@ def has_double_tap_drag_zoom(flags: int) -> bool: def has_double_tap_zoom(flags: int) -> bool: """ Returns: - `True` if the [`DOUBLE_TAP_ZOOM`][..] interactive flag is enabled. + `True` if the [`DOUBLE_TAP_ZOOM`][flet_map.InteractionFlag.DOUBLE_TAP_ZOOM] + interaction flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.DOUBLE_TAP_ZOOM) @@ -396,7 +403,7 @@ def has_rotate(flags: int) -> bool: def has_scroll_wheel_zoom(flags: int) -> bool: """ Returns: - `True` if the [`SCROLL_WHEEL_ZOOM`][..] interactive flag is enabled. + `True` if the [`SCROLL_WHEEL_ZOOM`][..] interaction flag is enabled. """ return InteractionFlag.has_flag(flags, InteractionFlag.SCROLL_WHEEL_ZOOM) @@ -429,7 +436,8 @@ class InteractionConfiguration: If `True`, then [`rotation_threshold`][..] and [`pinch_zoom_threshold`][..] and [`pinch_move_threshold`][..] will race. If multiple gestures win at the same time, then precedence: - [`pinch_zoom_win_gestures`][..] > [`rotation_win_gestures`][..] > [`pinch_move_win_gestures`][..] + [`pinch_zoom_win_gestures`][..] > [`rotation_win_gestures`][..] > + [`pinch_move_win_gestures`][..] """ pinch_move_threshold: ft.Number = 40.0 @@ -480,8 +488,8 @@ class InteractionConfiguration: rotation_win_gestures: MultiFingerGesture = MultiFingerGesture.ROTATE """ - When [`rotation_threshold`[..] wins over [`pinch_zoom_threshold`[..] and - [`pinch_move_threshold`[..] then `rotation_win_gestures` gestures will be used. + When [`rotation_threshold`][..] wins over [`pinch_zoom_threshold`][..] and + [`pinch_move_threshold`][..] then `rotation_win_gestures` gestures will be used. """ pinch_move_win_gestures: MultiFingerGesture = ( @@ -785,7 +793,8 @@ class KeyboardConfiguration: default_factory=lambda: ft.Duration(milliseconds=450) ) """ - Duration of the curved (`ft.Curve.EASE_IN`) portion of the animation occuring + Duration of the curved ([`Curve.EASE_IN`][flet.Curve.EASE_IN]) + portion of the animation occuring after a key down event (and after a key up event if [`animation_curve_reverse_duration`][..] is `None`) """ @@ -860,7 +869,7 @@ class KeyboardConfiguration: Must be greater than 0 and less than or equal to 1. To disable leaping, or change the maximum length of the key press - that will trigger a leap, see [`perform_leap_trigger_duration`[..]. + that will trigger a leap, see [`perform_leap_trigger_duration`][..]. """ max_rotate_velocity: ft.Number = 3 @@ -884,9 +893,9 @@ class KeyboardConfiguration: The amount to scale the panning offset velocity by during a leap animation. The larger the number, the larger the movement during a leap. - To change the duration of a leap, see [`leap_max_of_curve_component`[..]. + To change the duration of a leap, see [`leap_max_of_curve_component`][..]. - This may cause the pan velocity to exceed [`max_pan_velocity`[..]. + This may cause the pan velocity to exceed [`max_pan_velocity`][..]. """ rotate_leap_velocity_multiplier: ft.Number = 3 @@ -894,9 +903,9 @@ class KeyboardConfiguration: The amount to scale the rotation velocity by during a leap animation The larger the number, the larger the rotation difference during a leap. - To change the duration of a leap, see [`leap_max_of_curve_component`[..]. + To change the duration of a leap, see [`leap_max_of_curve_component`][..]. - This may cause the pan velocity to exceed [`max_rotate_velocity`[..]. + This may cause the pan velocity to exceed [`max_rotate_velocity`][..]. """ zoom_leap_velocity_multiplier: ft.Number = 3 @@ -904,9 +913,9 @@ class KeyboardConfiguration: The amount to scale the zooming velocity by during a leap animation. The larger the number, the larger the zoom difference during a leap. To - change the duration of a leap, see [`leap_max_of_curve_component`[..]. + change the duration of a leap, see [`leap_max_of_curve_component`][..]. - This may cause the pan velocity to exceed [`max_zoom_velocity`[..]. + This may cause the pan velocity to exceed [`max_zoom_velocity`][..]. """ perform_leap_trigger_duration: Optional[ft.DurationValue] = field( @@ -916,8 +925,10 @@ class KeyboardConfiguration: Maximum duration between the key down and key up events of an animation which will trigger a 'leap'. - To customize the leap itself, see the [`leap_max_of_curve_component`[..] & - `[*leap_velocity_multiplier`[..] properties. + To customize the leap itself, see the [`leap_max_of_curve_component`][..] & + `*leap_velocity_multiplier` ([`zoom_leap_velocity_multiplier`][..], + [`pan_leap_velocity_multiplier`][..] and [`rotate_leap_velocity_multiplier`][..]) + properties. Set to `None` to disable leaping. """ From 6e50f48b4e1b92ec7a398ffaadf698470f4be409 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 21:41:20 +0200 Subject: [PATCH 7/9] fixes --- src/flet_map/types.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/flet_map/types.py b/src/flet_map/types.py index 8eb729b..3bf91d7 100644 --- a/src/flet_map/types.py +++ b/src/flet_map/types.py @@ -755,7 +755,8 @@ class FadeInTileDisplay(TileDisplay): def __post_init__(self): assert 0.0 <= self.start_opacity <= 1.0, ( - f"start_opacity must be between 0.0 and 1.0 inclusive, got {self.start_opacity}" + f"start_opacity must be between 0.0 and 1.0 inclusive, " + f"got {self.start_opacity}" ) assert 0.0 <= self.reload_start_opacity <= 1.0, ( f"reload_start_opacity must be between 0.0 and 1.0 inclusive, " @@ -803,8 +804,8 @@ class KeyboardConfiguration: default_factory=lambda: ft.Duration(milliseconds=600) ) """ - Duration of the curved (reverse `ft.Curve.EASE_IN`) portion of the animation - occuring after a key up event. + Duration of the curved (reverse [`Curve.EASE_IN`][flet.Curve.EASE_IN]) + portion of the animation occuring after a key up event. Set to `None` to use [`animation_curve_duration`][..]. """ @@ -939,7 +940,7 @@ def disabled(cls) -> "KeyboardConfiguration": Disable keyboard control of the map. Info: - `CursorKeyboardRotationConfiguration` may still be active, + [`CursorKeyboardRotationConfiguration`][(p).] may still be active, and is not disabled if this is disabled. """ return KeyboardConfiguration( From e1c012b578c2bcda0b11a89622e37c69bea69f7b Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 21:51:40 +0200 Subject: [PATCH 8/9] lint dependency group --- .github/workflows/docs.yml | 2 +- pyproject.toml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5deff20..9c36a87 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -53,7 +53,7 @@ jobs: - name: Install dependencies run: | uv pip install -e . - uv pip install --group all + uv pip install --group docs --group lint - name: Deploy to GitHub Pages run: uv run mkdocs gh-deploy --force diff --git a/pyproject.toml b/pyproject.toml index cb6e178..b89c27d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,9 +23,12 @@ Issues = "https://github.com/flet-dev/flet-map/issues" test = [ "pytest >=7.2.0", ] +lint = [ + "ruff >=0.11.7", +] dev = [ "pre-commit >=4.2.0", - "ruff >=0.11.7", + { include-group = 'lint' }, { include-group = 'test' }, ] docs-coverage = [ From 0216fdc05117c1102e2b99407c0325b2cccada48 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 27 Jul 2025 22:36:12 +0200 Subject: [PATCH 9/9] last updates --- src/flet_map/marker_layer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flet_map/marker_layer.py b/src/flet_map/marker_layer.py index e73758e..801ee19 100644 --- a/src/flet_map/marker_layer.py +++ b/src/flet_map/marker_layer.py @@ -33,7 +33,7 @@ class Marker(ft.Control): The coordinates of the marker. This will be the center of the marker, - if [`alignment`][..] is [`flet.Alignment.CENTER`][. + if [`alignment`][..] is [`Alignment.CENTER`][flet.Alignment.CENTER]. """ rotate: Optional[bool] = None