diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f681fbd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +.git/ +tests/ + diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2d8d29a..6651fc9 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,12 +6,12 @@ Thank you for your interest in the Map Machine project. Since the primary goal o Suggest a tag to support ------------------------ -Please, create an issue with `icon` label. +Please, create an issue describing how you would like the feature to be visualized. Report a bug ------------ -Please, create an issue with `bug` and `generator` labels. +Please, create an issue describing the current behavior, expected behavior, and environment (most importantly, the OS version and Python version if it was not the recommended one). Fix a typo in documentation --------------------------- @@ -21,14 +21,36 @@ This action is not that easy as it supposed to be. We use [Moire](http://github. Modify the code --------------- -First of all, configure your workspace. +### First configure your workspace ### - * Install formatter, linter and test system: `pip install black flake8 pytest`. - * Be sure to run `git config --local core.hooksPath data/githooks` to enable Git hooks. +Make sure you have Python 3.9 development tools. E.g., for Ubuntu, run `apt install python3.9-dev python3.9-venv`. + +Activate virtual environment. E.g. for fish shell, run `source venv/bin/activate.fish`. + +Install the project in editable mode: + +```shell +pip install -e . +``` + +Install formatter, linter and test system: `pip install black flake8 mypy pytest pytest-cov`. + +Be sure to enable Git hooks: + +```shell +git config --local core.hooksPath data/githooks +``` + +If you are using PyCharm, you may want to set up user dictionary as well: + + + * `cp data/dictionary.xml .idea/dictionaries/.xml` + * in `.idea/dictionaries/.xml` change `%USERNAME%` to your username, + * restart PyCharm if it is launched. ### Code style ### -We use [Black](http://github.com/psf/black) code formatter with maximum 80 characters line lenght for all Python files within the project. Reformat a file is as simple as `black -l 80 `. +We use [Black](http://github.com/psf/black) code formatter with maximum 80 characters line length for all Python files within the project. Reformat a file is as simple as `black -l 80 `. Reformat everything with `black -l 80 map_machine tests`. If you create new Python file, make sure you add `__author__ = " "` and `__email__ = ""` string variables. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a22e424..42962ac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,7 +46,7 @@ jobs: pip install . - name: Check code style with Black run: | - black -l 80 --check map_machine tests + black -l 80 --check map_machine setup.py tests - name: Lint with Flake8 run: | flake8 --max-line-length=80 --ignore=E203,W503 diff --git a/.gitignore b/.gitignore index 9d28a3f..2ee3dca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,6 @@ # Generated files dist/ -doc/*.html -doc/*.svg -doc/*.wiki -missed_tags.yml out/ # Cache @@ -22,3 +18,10 @@ cache/ work precommit.py + +.idea +build +temp +venv* + +taginfo diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1223eaa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.9-slim-bullseye + +WORKDIR /app + +COPY . /app/ + +RUN \ + apt update && \ + apt install -y --no-install-recommends gcc libcairo2-dev libgeos-dev && \ + pip install --upgrade pip && \ + pip install . && \ + mkdir -p /maps/cache + +VOLUME ["/maps"] +ENTRYPOINT ["map-machine"] + diff --git a/README.md b/README.md index 5e54f23..3d085a1 100644 --- a/README.md +++ b/README.md @@ -2,105 +2,124 @@ **Map Machine** project consists of - * Python [OpenStreetMap](http://openstreetmap.org) renderer and tile generator (see [usage](#usage-example), [renderer documentation](#map-generation), [tile generation](#tile-generation)), - * [Röntgen icon set](#icon-set): unique CC-BY 4.0 icons. -The idea behind the Map Machine project is to **show all the richness of the OpenStreetMap data**: to have a possibility to *display any map feature* represented by OpenStreetMap data tags by means of colors, shapes, and icons. Map Machine is created for OpenStreetMap contributors: to display all changes one made on the map even if they are small, and for users: to dig down into the map and find every detail that was mapped. + * Python [OpenStreetMap](http://openstreetmap.org) renderer: + * SVG [map generation](#map-generation), + * SVG and PNG [tile generation](#tile-generation), + * [Röntgen](#röntgen-icon-set) icon set: unique CC-BY 4.0 map icons. -Unlike standard OpenStreetMap layers, **Map Machine is a playground for experiments** where one can easily try to support proposed tags, tags with little or even single usage, deprecated tags. +The idea behind the Map Machine project is to **show all the richness of the OpenStreetMap data**: to have a possibility to display any map feature represented by OpenStreetMap data tags by means of colors, shapes, and icons. Map Machine is created both for map contributors: to display all changes one made on the map even if they are small, and for map users: to dig down into the map and find every detail that was mapped. -Map Machine is intended to be highly configurable, so it can generate precise but messy maps for OSM contributors as well as pretty and clean maps for OSM users, can use slow algorithms for some experimental features. +Unlike standard OpenStreetMap layers, **Map Machine is a playground for experiments** where one can easily try to support any unsupported tag, proposed tagging scheme, tags with little or even single usage, deprecated ones that are still in use. + +Map Machine is intended to be highly configurable, so it can generate precise but messy maps for OSM contributors as well as pretty and clean maps for OSM users. It can also use some slow algorithms for experimental features. + +See + + * [installation instructions](#installation), + * [map features](#map-features), + * [using Röntgen as JOSM style](#use-röntgen-as-josm-map-paint-style). Usage example ------------- ```bash -map-machine render -b 2.284,48.860,2.290,48.865 +map-machine render -b=2.284,48.860,2.290,48.865 ``` will automatically download OSM data and write output SVG map of the specified area to `out/map.svg`. See [Map generation](#map-generation). ```bash -map-machine tile -b 2.361,48.871,2.368,48.875 +map-machine tile -b=2.361,48.871,2.368,48.875 ``` -will automatically download OSM data and write output PNG tiles that cover the specified area to `out/tiles` directory. See [Tile generation](#tile-generation). +will automatically download OSM data and write output PNG tiles that cover the specified area to the `out/tiles` directory. See [Tile generation](#tile-generation). + +Röntgen icon set +---------------- + +The central feature of the project is Röntgen icon set. It is a set of monochrome 14 × 14 px pixel-aligned icons specially created for Map Machine project. Unlike the Map Machine source code, which is under MIT license, all icons are under [CC BY](http://creativecommons.org/licenses/by/4.0/) license. So, with the appropriate credit icon set can be used outside the project. Some icons can be used as emoji symbols. + +All icons tend to support a common design style, which is heavily inspired by [Maki](https://github.com/mapbox/maki), [Osmic](https://github.com/gmgeo/osmic), and [Temaki](https://github.com/ideditor/temaki). + +![Icons](doc/grid.svg) + +Feel free to request new icons via issues for whatever you want to see on the map. No matter how frequently the tag is used in OpenStreetMap since the final goal is to cover all tags. However, commonly used tags have priority, other things being equal. + +Generate icon grid and sets of individual icons with `map-machine icons`. It will update `doc/grid.svg` file, and create SVG files in `out/icons_by_id` directory where files are named using shape identifiers (e.g. `power_tower_portal_2_level.svg`) and in `icons_by_name` directory where files are named using shape names (e.g. `Röntgen portal two-level transmission tower.svg`). Files from the last directory are used in OpenStreetMap wiki (e.g. [`File:Röntgen_portal_two-level_transmission_tower.svg`](https://wiki.openstreetmap.org/wiki/File:R%C3%B6ntgen_portal_two-level_transmission_tower.svg)). Map features ------------ +### Extra icons ### + +Map Machine uses icons to visualize tags for nodes and areas. But unlike other renderers, Map Machine can use more than one icon to visualize an entity and use colors to visualize [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) value or other entity properties (like [`material`](https://wiki.openstreetmap.org/wiki/Key:material) or [`genus`](https://wiki.openstreetmap.org/wiki/Key:genus)). + ### Isometric building shapes ### -With `--buildings isometric` or `--buildings isometric-no-parts` (not set by default), buildings are drawn using isometric shapes for walls and shade in proportion to [`building:levels`](https://wiki.openstreetmap.org/wiki/Key:building:levels), [`building:min_level`](https://wiki.openstreetmap.org/wiki/Key:building:min_level), [`height`](https://wiki.openstreetmap.org/wiki/Key:height) and [`min_height`](https://wiki.openstreetmap.org/wiki/Key:min_height) values. +With `--buildings isometric` or `--buildings isometric-no-parts` (not set by default), buildings are drawn using isometric shapes for walls and shade in proportion to [`building:levels`](https://wiki.openstreetmap.org/wiki/Key:building:levels), [`building:min_level`](https://wiki.openstreetmap.org/wiki/Key:building:min_level), [`height`](https://wiki.openstreetmap.org/wiki/Key:height), and [`min_height`](https://wiki.openstreetmap.org/wiki/Key:min_height) values. -![3D buildings](doc/buildings.png) +![3D buildings](doc/buildings.svg) ### Road lanes ### -To determine road width Map Machine uses the [`width`](https://wiki.openstreetmap.org/wiki/Key:width) tag value or estimates it based on the [`lanes`](https://wiki.openstreetmap.org/wiki/Key:lanes) value. +To determine road width Map Machine uses the [`width`](https://wiki.openstreetmap.org/wiki/Key:width) tag value or estimates it based on the [`lanes`](https://wiki.openstreetmap.org/wiki/Key:lanes) value. If lane value is specified, it also draws lane separators. This map style is highly inspired by Christoph Hormann's post [Navigating the Maze](http://blog.imagico.de/navigating-the-maze-part-2/). -![Road lanes](doc/lanes.png) +![Road lanes](doc/lanes.svg) ### Trees ### -Visualization of tree leaf types (broadleaved or needleleaved) and genus or taxon by means of icon shapes and leaf cycles (deciduous or evergreen) by means of color. +Visualization of tree leaf types (broadleaved or needle-leaved) and genus or taxon by means of icon shapes and leaf cycles (deciduous or evergreen) by means of color. -![Trees](doc/trees.png) +![Trees](doc/trees.svg) ### Viewpoint and camera direction ### -Visualize [`direction`](https://wiki.openstreetmap.org/wiki/Key:direction) tag for [`tourism`](https://wiki.openstreetmap.org/wiki/Key:tourism)=[`viewpoint`](https://wiki.openstreetmap.org/wiki/Tag:tourism=viewpoint) and [`camera:direction`](https://wiki.openstreetmap.org/wiki/Key:camera:direction) for [`man_made`](https://wiki.openstreetmap.org/wiki/Key:man_made)=[`surveillance`](https://wiki.openstreetmap.org/wiki/Tag:man_made=surveillance). +[`direction`](https://wiki.openstreetmap.org/wiki/Key:direction) tag values for [`tourism`](https://wiki.openstreetmap.org/wiki/Key:tourism) = [`viewpoint`](https://wiki.openstreetmap.org/wiki/Tag:tourism=viewpoint) and [`camera:direction`](https://wiki.openstreetmap.org/wiki/Key:camera:direction) for [`man_made`](https://wiki.openstreetmap.org/wiki/Key:man_made) = [`surveillance`](https://wiki.openstreetmap.org/wiki/Tag:man_made=surveillance) are rendered with sectors displaying the direction and angle (15º if angle is not specified) or the whole circle for panorama view. Radial gradient is used for surveillance and inverted radial gradient is used for viewpoints. -![Surveillance](doc/surveillance.png) +![Surveillance](doc/surveillance.svg) -![Viewpoints](doc/viewpoints.png) +![Viewpoints](doc/viewpoints.svg) ### Power tower design ### -Visualize [`design`](https://wiki.openstreetmap.org/wiki/Key:design) values used with [`power`](https://wiki.openstreetmap.org/wiki/Key:power)=[`tower`](https://wiki.openstreetmap.org/wiki/Tag:power=tower) tag. +Visualize [`design`](https://wiki.openstreetmap.org/wiki/Key:design) values used with [`power`](https://wiki.openstreetmap.org/wiki/Key:power) = [`tower`](https://wiki.openstreetmap.org/wiki/Tag:power=tower) tag. -![Power tower design](doc/power_tower_design.png) +![Power tower design](doc/icons_power.svg) -![Power tower design](doc/power.png) +![Power tower design](doc/power.svg) ### Colors ### -Map icons have [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) tag value if it is present, otherwise icons displayed with dark grey color by default, purple color for shop nodes, red color for emergency features, and special colors for natural features. Map Machine also takes into account [`building:colour`](https://wiki.openstreetmap.org/wiki/Key:building:colour), [`roof:colour`](https://wiki.openstreetmap.org/wiki/Key:roof:colour) and other `*:colour` tags. We also use [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) tag value to paint subway lines. +Map icons have [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) tag value if it is present, otherwise, icons are displayed with dark grey color by default, purple color for shop nodes, red color for emergency features, and special colors for natural features. Map Machine also takes into account [`building:colour`](https://wiki.openstreetmap.org/wiki/Key:building:colour), [`roof:colour`](https://wiki.openstreetmap.org/wiki/Key:roof:colour) and other `*:colour` tags, and uses [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) tag value to paint subway lines. -E.g. [`building:colour`](https://wiki.openstreetmap.org/wiki/Key:building:colour) visualization: - -![Building colors](doc/colors.png) +![Building colors](doc/colors.svg) ### Emergency ### -![Emergency](doc/emergency.png) +![Emergency](doc/icons_emergency.svg) ### Japanese map symbols ### -There are [special symbols](https://en.wikipedia.org/wiki/List_of_Japanese_map_symbols) appearing on Japanese maps. +Japanese maps usually use [special symbols](https://en.wikipedia.org/wiki/List_of_Japanese_map_symbols) called *chizukigou* (地図記号) which are different from standard map symbols used in other countries. They can be enabled with `--country jp` option. -![Japanese map symbols](doc/japanese.png) - -Icon set --------- - -The central feature of the project is Röntgen icon set. It is a set of monochrome 14 × 14 px pixel-aligned icons. Unlike the Map Machine source code, which is under MIT license, all icons are under [CC BY](http://creativecommons.org/licenses/by/4.0/) license. So, with the appropriate credit icon set can be used outside the project. Some icons can be used as emoji symbols. - -All icons tend to support common design style, which is heavily inspired by [Maki](https://github.com/mapbox/maki), [Osmic](https://github.com/gmgeo/osmic), and [Temaki](https://github.com/ideditor/temaki). - -Icons are used to visualize tags for nodes and areas. Unlike other renderers, Map Machine can use more than one icon to visualize an entity and use colors to visualize [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) value or other entity properties (like [`material`](https://wiki.openstreetmap.org/wiki/Key:material) or [`genus`](https://wiki.openstreetmap.org/wiki/Key:genus)). - -![Icons](doc/grid.png) - -Feel free to request new icons via issues for whatever you want to see on the map. No matter how frequently the tag is used in OpenStreetMap since final goal is to cover all tags. However, common used tags have priority, other things being equal. - -Generate icon grid and sets of individual icons with `map-machine icons`. It will create `out/icon_grid.svg` file, and SVG files in `out/icons_by_id` directory where files are named using shape identifiers (e.g. `power_tower_portal_2_level.svg`) and in `icons_by_name` directory where files are named using shape names (e.g. `Röntgen portal two-level transmission tower.svg`). Files from the last directory are used in OpenStreetMap wiki (e.g. [`File:Röntgen_portal_two-level_transmission_tower.svg`](https://wiki.openstreetmap.org/wiki/File:R%C3%B6ntgen_portal_two-level_transmission_tower.svg)). +![Japanese map symbols](doc/icons_japanese.svg) ### Shape combination ### -Map Machine constructs icons from the shapes extracted from the sketch SVG file. Some icons consists of just one shape, to construct other it may be necessary to combine two or more shapes. +One of the key features of Map Machine is constructing icons from the several shapes. -![Bus stop icon combination](doc/bus_stop.png) +#### Masts #### + +For [`man_made`](https://wiki.openstreetmap.org/wiki/Key:man_made) = [`mast`](https://wiki.openstreetmap.org/wiki/Tag:man_made=mast) distinguish types (communication, lighting, monitoring, and siren) and construction (freestanding or lattice, and using of guys) are rendered by combining 7 unique icon shapes. + +![Mast types](doc/mast.svg) + +#### Volcanoes #### + +For [`natural`](https://wiki.openstreetmap.org/wiki/Key:natural) = [`volcano`](https://wiki.openstreetmap.org/wiki/Tag:natural=volcano) status (active, dormant, extinct, or unspecified) and type (stratovolcano, shield, or scoria) are rendered by combining 7 unique icon shapes. + +![Volcano types](doc/volcano.svg) Wireframe view -------------- @@ -109,38 +128,40 @@ Wireframe view Visualize element creation time with `--mode time`. -![Creation time mode](doc/time.png) +![Creation time mode](doc/time.svg) ### Author mode ### Every way and node displayed with the random color picked for each author with `--mode author`. -![Author mode](doc/author.png) +![Author mode](doc/author.svg) Installation ------------ Requirements: Python 3.8. + * Install [cairo 2D graphic library](https://www.cairographics.org/download/), - * install Python packages: + * install [GEOS library](https://libgeos.org), + * install Python packages with the command: - -```bash -pip install -r requirements.txt -pip install . +```shell +pip install git+https://github.com/enzet/map-machine ``` +For more detailed instructions, see [instructions](doc/INSTALL.md). + Map generation -------------- -Command `render` is used to generates SVG map from OpenStreetMap data. You can run it using: +Command `render` is used to generate SVG map from OpenStreetMap data. You can run it using: ```bash map-machine render \ - -b ,,, \ - -o \ - -z \ + -b=,,, \ + -o= \ + -z= \ ``` @@ -148,11 +169,11 @@ map-machine render \ ```bash map-machine render \ - --boundary-box 2.284,48.860,2.290,48.865 \ - --output out/esplanade_du_trocadéro.svg + --boundary-box=2.284,48.860,2.290,48.865 \ + --output=out/esplanade_du_trocadéro.svg ``` -will download OSM data to `cache/2.284,48.860,2.290,48.865.osm` and write output SVG map of the specified area to `out/esplanade_du_trocadéro.svg`. +will download OSM data to `cache/2.284,48.860,2.290,48.865.osm` and write an output SVG map of the specified area to `out/esplanade_du_trocadéro.svg`. ### Arguments ### @@ -160,10 +181,10 @@ will download OSM data to `cache/2.284,48.860,2.290,48.865.osm` and write output |---|---| | `-i`, `--input` `` | input XML file name or names (if not specified, file will be downloaded using OpenStreetMap API) | | `-o`, `--output` `` | output SVG file name, default value: `out/map.svg` | -| `-b`, `--boundary-box` `,,,` | geo boundary box; if first value is negative, enclose the value with quotes and use space before `-` | +| `-b`, `--boundary-box` `,,,` | geo boundary box; if the first value is negative, use `=` sign or enclose the value with quotes and use space before `-`, e.g. `-b=-84.752,39.504,-84.749,39.508` or `-b " -84.752,39.504,-84.749,39.508"` | | `--cache` `` | path for temporary OSM files, default value: `cache` | -| `-z`, `--zoom` `` | OSM zoom level, default value: 18 | -| `-c`, `--coordinates` `,` | coordinates of any location inside the tile | +| `-z`, `--zoom` `` | OSM zoom level, default value: 18.0 | +| `-c`, `--coordinates` `,` | coordinates of any location inside the tile; if the first value is negative, use `=` sign or enclose the value with quotes and use space before `-`, e.g. `-c=-84.752,39.504` or `-c " -84.752,39.504"` | | `-s`, `--size` `,` | resulted image size | plus [map configuration options](#map-options) @@ -175,12 +196,12 @@ Command `tile` is used to generate PNG tiles for [slippy maps](https://wiki.open | Option | Description | |---|---| -| `-c`, `--coordinates` `,` | coordinates of any location inside the tile | +| `-c`, `--coordinates` `,` | coordinates of any location inside the tile; if the first value is negative, use `=` sign or enclose the value with quotes and use space before `-`, e.g. `-c=-84.752,39.504` or `-c " -84.752,39.504"` | | `-t`, `--tile` `//` | tile specification | | `--cache` `` | path for temporary OSM files, default value: `cache` | -| `-b`, `--boundary-box` `,,,` | construct the minimum amount of tiles that cover requested boundary box | -| `-z`, `--zoom` `` | OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, `16,17,18`, or `16,18-20`, default value: `18` | -| `-i`, `--input` `` | input OSM XML file name (if not specified, file will be downloaded using OpenStreetMap API) | +| `-b`, `--boundary-box` `,,,` | construct the minimum amount of tiles that cover the requested boundary box; if the first value is negative, use `=` sign or enclose the value with quotes and use space before `-`, e.g. `-b=-84.752,39.504,-84.749,39.508` or `-b " -84.752,39.504,-84.749,39.508"` | +| `-z`, `--zoom` `` | OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, `16,17,18`, or `16,18-20`, default value: `18` | +| `-i`, `--input` `` | input OSM XML file name (if not specified, the file will be downloaded using OpenStreetMap API) | plus [map configuration options](#map-options) @@ -196,8 +217,8 @@ or specify any geographical coordinates inside a tile: ```bash map-machine tile \ - --coordinates , \ - --zoom + --coordinates=, \ + --zoom= ``` Tile will be stored as SVG file `out/tiles/tile___.svg` and PNG file `out/tiles/tile___.svg`, where `x` and `y` are tile coordinates. `--zoom` option will be ignored if it is used with `--tile` option. @@ -205,7 +226,7 @@ Tile will be stored as SVG file `out/tiles/tile___.svg` and PN Example: ```bash -map-machine tile -c 55.7510637,37.6270761 -z 18 +map-machine tile -c=55.7510637,37.6270761 -z=18 ``` will generate SVG file `out/tiles/tile_18_158471_81953.svg` and PNG file `out/tiles/tile_18_158471_81953.png`. @@ -216,16 +237,16 @@ Specify boundary box to get the minimal set of tiles that covers the area: ```bash map-machine tile \ - --boundary-box ,,, \ - --zoom + --boundary-box=,,, \ + --zoom= ``` -Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files `out/tiles/tile___.svg` and PNG files `out/tiles/tile___.svg`, where `x` and `y` are tile coordinates. +The boundary box will be extended to the boundaries of the minimal tileset that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files `out/tiles/tile___.svg` and PNG files `out/tiles/tile___.svg`, where `x` and `y` are tile coordinates. Example: ```bash -map-machine tile -b 2.361,48.871,2.368,48.875 +map-machine tile -b=2.361,48.871,2.368,48.875 ``` will generate 36 PNG tiles at zoom level 18 from tile 18/132791/90164 all the way to 18/132796/90169 and two cached files `cache/2.360,48.869,2.370,48.877_18.svg` and `cache/2.360,48.869,2.370,48.877_18.png`. @@ -239,7 +260,7 @@ Command `server` is used to run tile server for slippy maps. map-machine server ``` -Stop server interrupting process with Ctrl + C. +Stop server interrupting the process with Ctrl + C. | Option | Description | |---|---| @@ -251,7 +272,7 @@ Stop server interrupting process with Ctrl + C. Create a minimal amount of tiles that cover specified boundary box for zoom levels 16, 17, 18, and 19: ```bash -map-machine tile -b 2.364,48.854,2.367,48.857 -z 16-19 +map-machine tile -b=2.364,48.854,2.367,48.857 -z=16-19 ``` Run tile server on 127.0.0.1:8080: @@ -290,14 +311,18 @@ Map configuration options used by `render` and `tile` commands: | Option | Description | |---|---| -| `--buildings` `` | building drawing mode: flat, isometric, isometric-no-parts, default value: `flat` | -| `--mode` `` | map drawing mode: normal, author, time, default value: `normal` | +| `--buildings` `` | building drawing mode: no, flat, isometric, isometric-no-parts, default value: `flat` | +| `--mode` `` | map drawing mode: normal, author, time, white, black, default value: `normal` | | `--overlap` `` | how many pixels should be left around icons and text, default value: 12 | -| `--labels` `` | label drawing mode: no, main, all, default value: `main` | +| `--labels` `` | label drawing mode: no, main, all, address, default value: `main` | | `--level` | display only this floor level, default value: `overground` | | `--seed` `` | seed for random | -| `--show-tooltips` | add tooltips with tags for icons in SVG files | +| `--tooltips` | add tooltips with tags for icons in SVG files | | `--country` | two-letter code (ISO 3166-1 alpha-2) of country, that should be used for location restrictions, default value: `world` | +| `--ignore-level-matching` | draw all map features ignoring the current level | +| `--roofs` | draw building roofs, set by default | +| `--building-colors` | paint walls (if isometric mode is enabled) and roofs with specified colors | +| `--show-overlapped` | show hidden nodes with a dot | MapCSS 0.2 generation --------------------- @@ -312,7 +337,8 @@ To create MapCSS with Map Machine style also for ways and relations, run `map-ma | `--ways` | add style for ways and relations | | `--lifecycle` | add icons for lifecycle tags; be careful: this will increase the number of node and area selectors by 9 times, set by default | -### Use Map Machine as JOSM map paint style ### +### Use Röntgen as JOSM map paint style ### + * Run `map-machine mapcss`. * Open [JOSM](https://josm.openstreetmap.de/). @@ -320,13 +346,13 @@ To create MapCSS with Map Machine style also for ways and relations, run `map-ma * Active styles: press +. * URL / File: set path to `out/map_machine_mapcss/map_machine.mapcss`. -To enable / disable Map Machine map paint style go to ViewMap Paint StylesMap Machine. +To enable/disable Map Machine map paint style go to ViewMap Paint StylesMap Machine. #### Example #### ![JOSM example](doc/josm.png) -Example of using Röntgen icons on top of Mapnik style in JOSM. Map Paint Styles look like: +Example of using Röntgen icons on top of Mapnik style in JOSM. Map Paint Styles look like this: * ✓ Mapnik (true) * ✓ Map Machine diff --git a/data/collections.json b/data/collections.json new file mode 100644 index 0000000..8672f3e --- /dev/null +++ b/data/collections.json @@ -0,0 +1,188 @@ +[ + { + "page": "Tag:roof:shape=skillion", + "id": "skillion_roof", + "tags": {"building": "apartments", "roof:shape": "skillion"}, + "row_key": "building:levels", + "row_values": ["1", "2", "3", "4", "5"] + }, + { + "page": "Tag:roof:shape=flat", + "id": "flat_roof", + "tags": {"building": "apartments", "roof:shape": "flat"}, + "row_key": "building:levels", + "row_values": ["1", "2", "3", "4", "5"] + }, + { + "page": "Tag:roof:shape=gabled", + "id": "gabled_roof", + "tags": {"building": "apartments", "roof:shape": "gabled"}, + "row_key": "building:levels", + "row_values": ["1", "2", "3", "4", "5"] + }, + { + "name": "Apartments", + "page": "Key:building:levels", + "id": "apartments", + "tags": {"building": "apartments"}, + "row_key": "roof:shape", + "row_values": ["gabled", "flat", "hipped", "pyramidal", "skillion"], + "column_key": "building:levels", + "column_values": ["1", "2", "3", "4", "5"] + }, + { + "name": "Outdoor seating", + "page": "Tag:leisure=outdoor_seating", + "id": "outdoor_seating", + "tags": {"leisure": "outdoor_seating"}, + "row_key": "weather_protection", + "row_values": ["parasol", "roof", "awning", "pavilion", "pergola"] + }, + { + "page": "Tag:artwork_type=statue", + "id": "statue", + "tags": {"tourism": "artwork", "artwork_type": "statue"}, + "row_tags": [ + {}, + {"amenity": "bench"} + ] + }, + { + "name": "Tank trap", + "page": "Key:tank_trap", + "id": "tank_trap", + "tags": {}, + "row_key": "tank_trap", + "row_values": ["czech_hedgehog", "dragons_teeth", "toblerone"] + }, + { + "page": "Tag:natural=tree", + "id": "trees", + "tags": {"natural": "tree"}, + "row_key": "leaf_type", + "row_values": ["broadleaved", "needleleaved", "palm"], + "column_key": "denotation", + "column_values": ["", "urban", "avenue"] + }, + { + "name": "Surveillance", + "page": "Tag:man_made=surveillance", + "id": "surveillance", + "tags": {"man_made": "surveillance"}, + "row_tags": [ + {}, + {"camera:type": "dome", "camera:mount": "ceiling"}, + {"camera:type": "dome", "camera:mount": "wall"} + ] + }, + { + "page": "Tag:artwork_type=stone", + "id": "artwork_stone", + "tags": {}, + "row_tags": [ + {"tourism": "artwork", "artwork_type": "stone"}, + {"tourism": "artwork", "artwork_type": "stone", "inscription": "*"} + ] + }, + { + "name": "Bench", + "page": "Tag:memorial=bench", + "id": "bench", + "tags": {}, + "row_tags": [ + {"amenity": "bench"}, + {"memorial": "bench"} + ] + }, + { + "name": "Mast", + "page": "Tag:man_made=mast", + "id": "mast", + "tags": {"man_made": "mast"}, + "row_key": "tower:construction", + "row_values": ["freestanding", "lattice", "guyed_tube", "guyed_lattice"], + "column_key": "tower:type", + "column_values": ["", "communication", "lighting", "monitoring", "siren"] + }, + { + "name": "Volcano", + "page": "Tag:natural=volcano", + "id": "volcano", + "tags": {"natural": "volcano"}, + "row_key": "volcano:type", + "row_values": ["stratovolcano", "shield", "scoria"], + "column_key": "volcano:status", + "column_values": ["", "active", "dormant", "extinct"] + }, + { + "id": "volcano_status", + "tags": {"natural": "volcano"}, + "row_key": "volcano:status", + "row_values": ["", "active", "dormant", "extinct"] + }, + { + "page": "Tag:tower:construction=guyed_tube", + "tags": {"man_made": "mast", "tower:construction": "guyed_tube"}, + "row_key": "tower:type", + "row_values": ["", "communication", "lighting", "monitoring", "siren"] + }, + { + "page": "Tag:tower:construction=guyed_lattice", + "tags": {"man_made": "mast", "tower:construction": "guyed_lattice"}, + "row_key": "tower:type", + "row_values": ["", "communication", "lighting", "monitoring", "siren"] + }, + { + "page": "Key:communication:mobile_phone", + "tags": {"communication:mobile_phone": "yes"} + }, + { + "name": "Traffic calming", + "page": "Key:traffic_calming", + "tags": {}, + "row_key": "traffic_calming", + "row_values": [ + "bump", "mini_bumps", "hump", "table", "cushion", "rumble_strip", + "dip", "double_dip" + ] + }, + { + "page": "Key:crane:type", + "tags": {"man_made": "crane"}, + "column_key": "crane:type", + "column_values": [ + "gantry_crane", "floor-mounted_crane", "portal_crane", + "travel_lift", "tower_crane" + ] + }, + { + "tags": {}, + "name": "Power tower and pole", + "row_key": "power", + "row_values": ["tower", "pole"], + "column_key": "design", + "column_values": [ + "one-level", "two-level", "three-level", "four-level", "asymmetric", + "triangle", "flag", "delta", "delta_two-level", "delta_three-level" + ] + }, + { + "tags": {}, + "name": "Power tower", + "row_key": "power", + "row_values": ["tower"], + "column_key": "design", + "column_values": [ + "donau", "donau_inverse", "barrel", "y-frame", "x-frame", "h-frame", + "guyed_h-frame", "portal", "portal_two-level", "portal_three-level" + ] + }, + { + "name": "Diving tower", + "page": "Tag:tower:type=diving", + "id": "diving", + "tags": {"man_made": "tower", "tower:type": "diving"}, + "row_key": "tower:platforms", + "row_values": ["", "1", "2", "3", "4"] + } +] diff --git a/data/dictionary.xml b/data/dictionary.xml new file mode 100644 index 0000000..e2e904f --- /dev/null +++ b/data/dictionary.xml @@ -0,0 +1,51 @@ + + + + addr + aeroway + arecaceae + betula + carto + changeset + cladr + dasharray + defs + dharmachakra + enzet + flinger + githooks + housenumber + josm + landuse + levelname + linalg + linecap + linejoin + lunokhod + mapcss + maxlat + maxlon + maxspeed + minlat + minlon + needleleaved + noninfringement + osmic + portolan + rasterize + rasterized + röntgen + scoria + subattributes + subelement + subparser + subtile + subtiles + svgwrite + taginfo + temaki + tileset + vartanov + + + diff --git a/data/githooks/commit-msg b/data/githooks/commit-msg index 0316258..de77a2e 100755 --- a/data/githooks/commit-msg +++ b/data/githooks/commit-msg @@ -1,4 +1,4 @@ -#!/usr/local/bin/python3 +#!/usr/bin/env python3 """ Commit message checking. """ @@ -12,30 +12,25 @@ SHORT_MESSAGE_MAX_LENGTH: int = 50 MESSAGE_MAX_LENGTH: int = 72 -def error(message: str): - """Print error message and return exit code 1.""" - print(f"Error: {message}") - sys.exit(1) - - def check_file(file_name: str) -> Optional[str]: """ Exit program with exit code 1 if commit message does not conform the rules. :param file_name: commit message file name """ - with open(file_name) as input_file: + with open(file_name, encoding="utf-8") as input_file: parts: List[str] = list(map(lambda x: x[:-1], input_file.readlines())) return check_commit_message(parts) def check_commit_message(parts: List[str]) -> Optional[str]: - + """Check whether the commit message is well-formed.""" short_message: str = parts[0] if short_message[0] != short_message[0].upper(): return ( - short_message + "\n^" + short_message + + "\n^" + "\nCommit message short description should start with uppercase " + "letter." ) @@ -77,7 +72,8 @@ def check_commit_message(parts: List[str]) -> Optional[str]: + '\nCommit message should end with ".".' ) - def up(text: str): + def first_letter_uppercase(text: str): + """Change first letter to upper case.""" return text[0].upper() + text[1:] verbs_1 = ["add", "fix", "check", "refactor"] @@ -99,28 +95,31 @@ def check_commit_message(parts: List[str]) -> Optional[str]: for verb in verbs_2: verbs[verb + "d"] = verb - for verb in verbs: - if short_message.startswith(f"{verb} ") or short_message.startswith( - f"{up(verb)} " - ): + for wrong_verb, right_verb in verbs.items(): + if short_message.startswith( + f"{wrong_verb} " + ) or short_message.startswith(f"{first_letter_uppercase(wrong_verb)} "): return ( - f'Commit message should start with the verb in infinitive ' - f'form. Please, use "{up(verbs[verb])} ..." instead of ' - f'"{up(verb)} ...".' + f"Commit message should start with the verb in infinitive " + f"form. Please, use " + f'"{first_letter_uppercase(right_verb)} ..." instead of ' + f'"{first_letter_uppercase(wrong_verb)} ...".' ) -def check(commit_message): +def check(commit_message: str) -> None: + """Print commit_message and checking result.""" print("\033[33m" + commit_message + "\033[0m") print(check_commit_message(commit_message.split("\n"))) def test(): + """Test rules.""" check("start with lowercase letter.") check("Added foo.") check("Created foo.") check("Doesn't end with dot") - check("Tooooooooooooooooooooooooooooooooooooooooooooo long") + check("To-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o long") if __name__ == "__main__": diff --git a/data/githooks/pre-commit b/data/githooks/pre-commit index 41ddc82..012262d 100755 --- a/data/githooks/pre-commit +++ b/data/githooks/pre-commit @@ -1,8 +1,10 @@ #!/bin/sh +python_files="map_machine setup.py tests data/githooks/commit-msg" + echo "Checking code format with Black..." -if ! black -l 80 --check tests map_machine; then - black -l 80 --diff --color tests map_machine +if ! black -l 80 --check ${python_files}; then + black -l 80 --diff --color ${python_files} echo "FAIL" exit 1 fi @@ -13,5 +15,5 @@ echo "Lint with Flake8..." flake8 \ --max-line-length=80 \ --ignore=E203,W503,ANN002,ANN003,ANN101,ANN102 \ - --exclude=work,python3.8 \ + ${python_files} \ || { echo "FAIL"; exit 1; } diff --git a/data/githooks/pre-push b/data/githooks/pre-push index 6c4e451..2576f9a 100755 --- a/data/githooks/pre-push +++ b/data/githooks/pre-push @@ -6,6 +6,7 @@ if [ ${files} == 0 ] ; then echo "OK" else echo "FAIL" + git status exit 1 fi @@ -14,6 +15,6 @@ data/githooks/pre-commit || { echo "FAIL"; exit 1; } # Unit tests with pytest. echo "Run tests with pytest..." -pytest -v || { echo "FAIL"; exit 1; } +pytest -vv || { echo "FAIL"; exit 1; } exit 0 diff --git a/data/pylintrc b/data/pylintrc new file mode 100644 index 0000000..87558c3 --- /dev/null +++ b/data/pylintrc @@ -0,0 +1,18 @@ +[MASTER] + +disable= + C0415, + R0902, + R0903, + R0912, + R0913, + R0914, + W0511, + W1203, + too-many-return-statements + +good-names=i,j,x,y,a,b,c,n + +[SIMILARITIES] + +ignore-imports=yes diff --git a/doc/INSTALL.md b/doc/INSTALL.md new file mode 100644 index 0000000..7ca8acc --- /dev/null +++ b/doc/INSTALL.md @@ -0,0 +1,25 @@ +Install +------- + +Map Machine requires [Python](https://www.python.org) 3.9, [pip](https://pip.pypa.io/en/stable/installation/), and two libraries: + + + * [cairo 2D graphic library](https://www.cairographics.org/download/), + * [GEOS library](https://libgeos.org). + +Installation examples +--------------------- + +### Ubuntu ### + +```shell +apt install libcairo2-dev libgeos-dev +pip install git+https://github.com/enzet/map-machine +``` + +### macOS ### + +```shell +brew install cairo geos +pip install git+https://github.com/enzet/map-machine +``` \ No newline at end of file diff --git a/doc/author.png b/doc/author.png deleted file mode 100644 index 612e1dd..0000000 Binary files a/doc/author.png and /dev/null differ diff --git a/doc/author.svg b/doc/author.svg new file mode 100644 index 0000000..1ea1f53 --- /dev/null +++ b/doc/author.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/buildings.png b/doc/buildings.png deleted file mode 100644 index 0fd76af..0000000 Binary files a/doc/buildings.png and /dev/null differ diff --git a/doc/buildings.svg b/doc/buildings.svg new file mode 100644 index 0000000..a745899 --- /dev/null +++ b/doc/buildings.svg @@ -0,0 +1,2 @@ + +27272424333131202027273030727252524434342727303060604141404026262020Data: © OpenStreetMap contributorsData: © OpenStreetMap contributorsRendering: Map MachineRendering: Map Machine \ No newline at end of file diff --git a/doc/colors.png b/doc/colors.png deleted file mode 100644 index 9da36b8..0000000 Binary files a/doc/colors.png and /dev/null differ diff --git a/doc/colors.svg b/doc/colors.svg new file mode 100644 index 0000000..2d82273 --- /dev/null +++ b/doc/colors.svg @@ -0,0 +1,2 @@ + +101010101212446611111717553338382828441111G1G11313161612;1412;1418181515141416161111101088771010664ter4ter4412121515 \ No newline at end of file diff --git a/doc/contributing.moi b/doc/contributing.moi index 6c2cf63..62951bd 100644 --- a/doc/contributing.moi +++ b/doc/contributing.moi @@ -4,7 +4,7 @@ Thank you for your interest in the Map Machine project. Since the primary goal \2 {Suggest a tag to support} {} -Please, create an issue with \m {icon} label. +Please, create an issue describing how you would like the feature to be visualized. /* \2 {Add an icon} {} @@ -12,7 +12,7 @@ Please, create an issue with \m {icon} label. \2 {Report a bug} {} -Please, create an issue with \m {bug} and \m {generator} labels. +Please, create an issue describing the current behavior, expected behavior, and environment (most importantly, the OS version and Python version if it was not the recommended one). \2 {Fix a typo in documentation} {} @@ -20,14 +20,31 @@ This action is not that easy as it supposed to be. We use \ref {http://github.c \2 {Modify the code} {} -First of all, configure your workspace. +\3 {First configure your workspace} + +Make sure you have Python 3.9 development tools. E.g., for Ubuntu, run \m {apt install python3.9-dev python3.9-venv}. + +Activate virtual environment. E.g. for fish shell, run \m {source venv/bin/activate.fish}. + +Install the project in editable mode: + +\code {pip install -e .} {shell} + +Install formatter, linter and test system\: \m {pip install black flake8 mypy pytest pytest-cov}. + +Be sure to enable Git hooks: + +\code {git config --local core.hooksPath data/githooks} {shell} + +If you are using PyCharm, you may want to set up user dictionary as well: \list - {Install formatter, linter and test system\: \m {pip install black flake8 pytest}.} - {Be sure to run \m {git config --local core.hooksPath data/githooks} to enable Git hooks.} + {\m {cp data/dictionary.xml .idea/dictionaries/.xml}} + {in \m {.idea/dictionaries/.xml} change \m {%USERNAME%} to your username,} + {restart PyCharm if it is launched.} \3 {Code style} {code-style} -We use \ref {http://github.com/psf/black} {Black} code formatter with maximum 80 characters line lenght for all Python files within the project. Reformat a file is as simple as \m {black -l 80 \formal {file name}}. +We use \ref {http://github.com/psf/black} {Black} code formatter with maximum 80 characters line length for all Python files within the project. Reformat a file is as simple as \m {black -l 80 \formal {file name}}. Reformat everything with \m {black -l 80 map_machine tests}. If you create new Python file, make sure you add \m {__author__ = "\formal {first name} \formal {second name}"} and \m {__email__ = "\formal {author e-mail}"} string variables. diff --git a/doc/emergency.png b/doc/emergency.png deleted file mode 100644 index ca2620a..0000000 Binary files a/doc/emergency.png and /dev/null differ diff --git a/doc/grid.png b/doc/grid.png deleted file mode 100644 index 267d8d2..0000000 Binary files a/doc/grid.png and /dev/null differ diff --git a/doc/grid.svg b/doc/grid.svg new file mode 100644 index 0000000..7494679 --- /dev/null +++ b/doc/grid.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/icons_emergency.svg b/doc/icons_emergency.svg new file mode 100644 index 0000000..fa32439 --- /dev/null +++ b/doc/icons_emergency.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/icons_japanese.svg b/doc/icons_japanese.svg new file mode 100644 index 0000000..5ab0e40 --- /dev/null +++ b/doc/icons_japanese.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/icons_power.svg b/doc/icons_power.svg new file mode 100644 index 0000000..878e1b0 --- /dev/null +++ b/doc/icons_power.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/install.moi b/doc/install.moi new file mode 100644 index 0000000..3338a7c --- /dev/null +++ b/doc/install.moi @@ -0,0 +1,23 @@ +\2 {Install} {install} + +Map Machine requires \ref {https://www.python.org} {Python} 3.9, \ref {https://pip.pypa.io/en/stable/installation/} {pip}, and two libraries\: + +\list + {\ref {https://www.cairographics.org/download/} {cairo 2D graphic library},} + {\ref {https://libgeos.org} {GEOS library}.} + +\2 {Installation examples} {installation-examples} + +\3 {Ubuntu} {ubuntu} + +\code { +apt install libcairo2-dev libgeos-dev +pip install git+https://github.com/enzet/map-machine +} {shell} + +\3 {macOS} {macos} + +\code { +brew install cairo geos +pip install git+https://github.com/enzet/map-machine +} {shell} \ No newline at end of file diff --git a/doc/japanese.png b/doc/japanese.png deleted file mode 100644 index 466479b..0000000 Binary files a/doc/japanese.png and /dev/null differ diff --git a/doc/lanes.png b/doc/lanes.png deleted file mode 100644 index 81688fe..0000000 Binary files a/doc/lanes.png and /dev/null differ diff --git a/doc/lanes.svg b/doc/lanes.svg new file mode 100644 index 0000000..83a92e7 --- /dev/null +++ b/doc/lanes.svg @@ -0,0 +1,2 @@ + +Urban Yoga SpaUrban Yoga SpaMattress FirmMattress FirmAce HardwareAce HardwareAlexandra'sAlexandra's20092009Securities BuildingSecurities BuildingWestlake CenterWestlake Center16241624TEKsystemsTEKsystems2244222222USPSUSPSOlive Way & 4th AveOlive Way & 4th Ave19011901190419041705170541541519271927190119011916191631431431031016011601191319131907190719051905Data: © OpenStreetMap contributorsData: © OpenStreetMap contributorsRendering: Map MachineRendering: Map Machine \ No newline at end of file diff --git a/doc/mast.svg b/doc/mast.svg new file mode 100644 index 0000000..ef28d8e --- /dev/null +++ b/doc/mast.svg @@ -0,0 +1,2 @@ + +tower:construction=*freestandinglatticeguyed_tubeguyed_latticetower:type=*communicationlightingmonitoringsiren \ No newline at end of file diff --git a/doc/power.png b/doc/power.png deleted file mode 100644 index 17cb1e0..0000000 Binary files a/doc/power.png and /dev/null differ diff --git a/doc/power.svg b/doc/power.svg new file mode 100644 index 0000000..61cfe08 --- /dev/null +++ b/doc/power.svg @@ -0,0 +1,2 @@ + +3535411411102102EnerconEnercon4411W11W22333312W12W11205205217_1217_12213W13W2062063322207207212212208208170170171171172172169169209209223W3W2W2W1W1W216_1216_1T-Mobile, Vodafone, O2T-Mobile, Vodafone, O2Wustermark Awn - AwbWustermark Awn - Awb2a2a \ No newline at end of file diff --git a/doc/power_tower_design.png b/doc/power_tower_design.png deleted file mode 100644 index da6ccdd..0000000 Binary files a/doc/power_tower_design.png and /dev/null differ diff --git a/doc/readme.moi b/doc/readme.moi index 65f9b7a..dc10f6a 100644 --- a/doc/readme.moi +++ b/doc/readme.moi @@ -1,49 +1,73 @@ \title {Map Machine} -\page_icon {out/icons_by_id/book.svg} \b {Map Machine} project consists of \list - {Python \ref {http://openstreetmap.org} {OpenStreetMap} renderer and tile generator (see \ref {#usage-example} {usage}, \ref {#map-generation} {renderer documentation}, \ref {#tile-generation} {tile generation}),} - {\ref {#icon-set} {Röntgen icon set}\: unique CC-BY 4.0 icons.} + {Python \ref {http://openstreetmap.org} {OpenStreetMap} renderer: + \list + {SVG \ref {#map-generation} {map generation},} + {SVG and PNG \ref {#tile-generation} {tile generation},}} + {\ref {#röntgen-icon-set} {Röntgen} icon set\: unique CC-BY 4.0 map icons.} -The idea behind the Map Machine project is to \b {show all the richness of the OpenStreetMap data}\: to have a possibility to \i {display any map feature} represented by OpenStreetMap data tags by means of colors, shapes, and icons. Map Machine is created for OpenStreetMap contributors\: to display all changes one made on the map even if they are small, and for users\: to dig down into the map and find every detail that was mapped. +The idea behind the Map Machine project is to \b {show all the richness of the OpenStreetMap data}\: to have a possibility to display any map feature represented by OpenStreetMap data tags by means of colors, shapes, and icons. Map Machine is created both for map contributors\: to display all changes one made on the map even if they are small, and for map users\: to dig down into the map and find every detail that was mapped. -Unlike standard OpenStreetMap layers, \b {Map Machine is a playground for experiments} where one can easily try to support proposed tags, tags with little or even single usage, deprecated tags. +Unlike standard OpenStreetMap layers, \b {Map Machine is a playground for experiments} where one can easily try to support any unsupported tag, proposed tagging scheme, tags with little or even single usage, deprecated ones that are still in use. -Map Machine is intended to be highly configurable, so it can generate precise but messy maps for OSM contributors as well as pretty and clean maps for OSM users, can use slow algorithms for some experimental features. +Map Machine is intended to be highly configurable, so it can generate precise but messy maps for OSM contributors as well as pretty and clean maps for OSM users. It can also use some slow algorithms for experimental features. + +See +\list + {\ref {#installation} {installation instructions},} + {\ref {#map-features} {map features},} + {\ref {#use-röntgen-as-josm-map-paint-style} {using Röntgen as JOSM style}.} \2 {Usage example} {usage-example} -\code {map-machine render -b 2.284,48.860,2.290,48.865} {bash} +\code {map-machine render -b=2.284,48.860,2.290,48.865} {bash} will automatically download OSM data and write output SVG map of the specified area to \m {out/map.svg}. See \ref {#map-generation} {Map generation}. -\code {map-machine tile -b 2.361,48.871,2.368,48.875} {bash} +\code {map-machine tile -b=2.361,48.871,2.368,48.875} {bash} -will automatically download OSM data and write output PNG tiles that cover the specified area to \m {out/tiles} directory. See \ref {#tile-generation} {Tile generation}. +will automatically download OSM data and write output PNG tiles that cover the specified area to the \m {out/tiles} directory. See \ref {#tile-generation} {Tile generation}. -\2 {Map features} {features} +\2 {Röntgen icon set} {röntgen-icon-set} -\3 {Isometric building shapes} {levels} +The central feature of the project is Röntgen icon set. It is a set of monochrome 14 × 14 px pixel-aligned icons specially created for Map Machine project. Unlike the Map Machine source code, which is under MIT license, all icons are under \ref {http://creativecommons.org/licenses/by/4.0/} {CC BY} license. So, with the appropriate credit icon set can be used outside the project. Some icons can be used as emoji symbols. -With \m {--buildings isometric} or \m {--buildings isometric-no-parts} (not set by default), buildings are drawn using isometric shapes for walls and shade in proportion to \osm {building:levels}, \osm {building:min_level}, \osm {height} and \osm {min_height} values. +All icons tend to support a common design style, which is heavily inspired by \ref {https://github.com/mapbox/maki} {Maki}, \ref {https://github.com/gmgeo/osmic} {Osmic}, and \ref {https://github.com/ideditor/temaki} {Temaki}. -\image {doc/buildings.png} {3D buildings} +\image {doc/grid.svg} {Icons} + +Feel free to request new icons via issues for whatever you want to see on the map. No matter how frequently the tag is used in OpenStreetMap since the final goal is to cover all tags. However, commonly used tags have priority, other things being equal. + +Generate icon grid and sets of individual icons with \m {\command {icons}}. It will update \m {doc/grid.svg} file, and create SVG files in \m {out/icons_by_id} directory where files are named using shape identifiers (e.g. \m {power_tower_portal_2_level.svg}) and in \m {icons_by_name} directory where files are named using shape names (e.g. \m {Röntgen portal two-level transmission tower.svg}). Files from the last directory are used in OpenStreetMap wiki (e.g. \ref {https://wiki.openstreetmap.org/wiki/File:R%C3%B6ntgen_portal_two-level_transmission_tower.svg} {\m {File:Röntgen_portal_two-level_transmission_tower.svg}}). + +\2 {Map features} {map-features} + +\3 {Extra icons} {extra-icons} + +Map Machine uses icons to visualize tags for nodes and areas. But unlike other renderers, Map Machine can use more than one icon to visualize an entity and use colors to visualize \osm {colour} value or other entity properties (like \osm {material} or \osm {genus}). + +\3 {Isometric building shapes} {isometric-building-shapes} + +With \m {--buildings isometric} or \m {--buildings isometric-no-parts} (not set by default), buildings are drawn using isometric shapes for walls and shade in proportion to \osm {building:levels}, \osm {building:min_level}, \osm {height}, and \osm {min_height} values. + +\image {doc/buildings.svg} {3D buildings} \3 {Road lanes} {road-lanes} -To determine road width Map Machine uses the \osm {width} tag value or estimates it based on the \osm {lanes} value. +To determine road width Map Machine uses the \osm {width} tag value or estimates it based on the \osm {lanes} value. If lane value is specified, it also draws lane separators. This map style is highly inspired by Christoph Hormann's post \ref {http://blog.imagico.de/navigating-the-maze-part-2/} {Navigating the Maze}. -\image {doc/lanes.png} {Road lanes} +\image {doc/lanes.svg} {Road lanes} \3 {Trees} {trees} -Visualization of tree leaf types (broadleaved or needleleaved) and genus or taxon by means of icon shapes and leaf cycles (deciduous or evergreen) by means of color. +Visualization of tree leaf types (broadleaved or needle-leaved) and genus or taxon by means of icon shapes and leaf cycles (deciduous or evergreen) by means of color. /* -Visualization of tree \icon {tree} leaf types (broadleaved \icon {tree_with_leaf} or needleleaved \icon {needleleaved_tree}) and genus or taxon by means of icon shapes and leaf cycles (unknown \color {#98AC64}, deciduous \color {#fcaf3e} or evergreen \color {#688C44}) by means of color. If diameter, circumference, and/or diameter_crown are specified, we draw crown and trunk as circles. We also have special icons for some genus and taxons\: birch (\i {Betula}) \icon {betula}, palm (\i {Arecaceae}) \icon {palm}, maple (\i {Acer}) \icon {tree}\icon {leaf_maple}. +Visualization of tree \icon {tree} leaf types (broadleaved \icon {tree_with_leaf} or needle-leaved \icon {needleleaved_tree}) and genus or taxon by means of icon shapes and leaf cycles (unknown \color {#98AC64}, deciduous \color {#fcaf3e} or evergreen \color {#688C44}) by means of color. If diameter, circumference, and/or diameter_crown are specified, we draw crown and trunk as circles. We also have special icons for some genus and taxons\: birch (\i {Betula}) \icon {betula}, palm (\i {Arecaceae}) \icon {palm}, maple (\i {Acer}) \icon {tree}\icon {leaf_maple}. \table {{\osm {natural=tree}} {\icon {tree}}} @@ -59,64 +83,63 @@ Visualization of tree \icon {tree} leaf types (broadleaved \icon {tree_with_leaf */ -\image {doc/trees.png} {Trees} +\image {doc/trees.svg} {Trees} -\3 {Viewpoint and camera direction} {direction} +\3 {Viewpoint and camera direction} {viewpoint-and-camera-direction} -Visualize \osm {direction} tag for \osm {tourism=viewpoint} and \osm {camera:direction} for \osm {man_made=surveillance}. +\osm {direction} tag values for \osm {tourism=viewpoint} and \osm {camera:direction} for \osm {man_made=surveillance} are rendered with sectors displaying the direction and angle (15º if angle is not specified) or the whole circle for panorama view. Radial gradient is used for surveillance and inverted radial gradient is used for viewpoints. -\image {doc/surveillance.png} {Surveillance} +\image {doc/surveillance.svg} {Surveillance} -\image {doc/viewpoints.png} {Viewpoints} +\image {doc/viewpoints.svg} {Viewpoints} \3 {Power tower design} {power-tower-design} Visualize \osm {design} values used with \osm {power=tower} tag. -\image {doc/power_tower_design.png} {Power tower design} -\image {doc/power.png} {Power tower design} +\image {doc/icons_power.svg} {Power tower design} + +\image {doc/power.svg} {Power tower design} \3 {Colors} {colors} -Map icons have \osm {colour} tag value if it is present, otherwise icons displayed with dark grey color by default, purple color for shop nodes, red color for emergency features, and special colors for natural features. Map Machine also takes into account \osm {building:colour}, \osm {roof:colour} and other \m {*:colour} tags. We also use \osm {colour} tag value to paint subway lines. +Map icons have \osm {colour} tag value if it is present, otherwise, icons are displayed with dark grey color by default, purple color for shop nodes, red color for emergency features, and special colors for natural features. Map Machine also takes into account \osm {building:colour}, \osm {roof:colour} and other \m {*:colour} tags, and uses \osm {colour} tag value to paint subway lines. -E.g. \osm {building:colour} visualization\: - -\image {doc/colors.png} {Building colors} +\image {doc/colors.svg} {Building colors} \3 {Emergency} {emergency} -\image {doc/emergency.png} {Emergency} +\image {doc/icons_emergency.svg} {Emergency} \3 {Japanese map symbols} {japanese-map-symbols} -There are \ref {https://en.wikipedia.org/wiki/List_of_Japanese_map_symbols} {special symbols} appearing on Japanese maps. +Japanese maps usually use \ref {https://en.wikipedia.org/wiki/List_of_Japanese_map_symbols} {special symbols} called \i {chizukigou} (地図記号) which are different from standard map symbols used in other countries. They can be enabled with \m {--country jp} option. -\image {doc/japanese.png} {Japanese map symbols} +\image {doc/icons_japanese.svg} {Japanese map symbols} -\2 {Icon set} {icon-set} +\3 {Shape combination} {shape-combination} -The central feature of the project is Röntgen icon set. It is a set of monochrome 14 × 14 px pixel-aligned icons. Unlike the Map Machine source code, which is under MIT license, all icons are under \ref {http://creativecommons.org/licenses/by/4.0/} {CC BY} license. So, with the appropriate credit icon set can be used outside the project. Some icons can be used as emoji symbols. +One of the key features of Map Machine is constructing icons from the several shapes. -All icons tend to support common design style, which is heavily inspired by \ref {https://github.com/mapbox/maki} {Maki}, \ref {https://github.com/gmgeo/osmic} {Osmic}, and \ref {https://github.com/ideditor/temaki} {Temaki}. +/* Some icons consist of just one shape, to construct others it may be necessary to combine two or more shapes. */ -Icons are used to visualize tags for nodes and areas. Unlike other renderers, Map Machine can use more than one icon to visualize an entity and use colors to visualize \osm {colour} value or other entity properties (like \osm {material} or \osm {genus}). +\4 {Masts} {masts} -\image {doc/grid.png} {Icons} +For \osm {man_made=mast} distinguish types (communication, lighting, monitoring, and siren) and construction (freestanding or lattice, and using of guys) are rendered by combining 7 unique icon shapes. -Feel free to request new icons via issues for whatever you want to see on the map. No matter how frequently the tag is used in OpenStreetMap since final goal is to cover all tags. However, common used tags have priority, other things being equal. +\image {doc/mast.svg} {Mast types} -Generate icon grid and sets of individual icons with \m {\command {icons}}. It will create \m {out/icon_grid.svg} file, and SVG files in \m {out/icons_by_id} directory where files are named using shape identifiers (e.g. \m {power_tower_portal_2_level.svg}) and in \m {icons_by_name} directory where files are named using shape names (e.g. \m {Röntgen portal two-level transmission tower.svg}). Files from the last directory are used in OpenStreetMap wiki (e.g. \ref {https://wiki.openstreetmap.org/wiki/File:R%C3%B6ntgen_portal_two-level_transmission_tower.svg} {\m {File:Röntgen_portal_two-level_transmission_tower.svg}}). +\4 {Volcanoes} {volcanoes} -\3 {Shape combination} {shape_combination} +For \osm {natural=volcano} status (active, dormant, extinct, or unspecified) and type (stratovolcano, shield, or scoria) are rendered by combining 7 unique icon shapes. -Map Machine constructs icons from the shapes extracted from the sketch SVG file. Some icons consists of just one shape, to construct other it may be necessary to combine two or more shapes. - -\image {doc/bus_stop.png} {Bus stop icon combination} +\image {doc/volcano.svg} {Volcano types} /* -\3 {Icon settings} {icon_settings} +\image {doc/bus_stop.png} {Bus stop icon combination} + +\3 {Icon settings} {icon-settings} \4 {Japanese map symbols} {japanese-map-symbols} @@ -154,17 +177,17 @@ Countries with right-to-left script direction\: \2 {Wireframe view} {wireframe-view} -\3 {Creation time mode} {time_mode} +\3 {Creation time mode} {creation-time-mode} Visualize element creation time with \m {--mode time}. -\image {doc/time.png} {Creation time mode} +\image {doc/time.svg} {Creation time mode} \3 {Author mode} {author_mode} Every way and node displayed with the random color picked for each author with \m {--mode author}. -\image {doc/author.png} {Author mode} +\image {doc/author.svg} {Author mode} \2 {Installation} {installation} @@ -172,28 +195,30 @@ Requirements\: Python 3.8/* or higher*/. \list {Install \ref {https://www.cairographics.org/download/} {cairo 2D graphic library},} - {install Python packages\:} + {install \ref {https://libgeos.org} {GEOS library},} + {install Python packages with the command\:} -\code {pip install -r requirements.txt -pip install .} {bash} +\code {pip install git+https://github.com/enzet/map-machine} {shell} + +For more detailed instructions, see \ref {doc/INSTALL.md} {instructions}. \2 {Map generation} {map-generation} -Command \m {render} is used to generates SVG map from OpenStreetMap data. You can run it using\: +Command \m {render} is used to generate SVG map from OpenStreetMap data. You can run it using\: \code {map-machine render \\ - -b \formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\ - -o \formal {output file name} \\ - -z \formal {OSM zoom level} \\ + -b=\formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\ + -o=\formal {output file name} \\ + -z=\formal {OSM zoom level} \\ \formal {other arguments}} {bash} -\3 {Example} {example-2} +\3 {Example} {example} \code {map-machine render \\ - --boundary-box 2.284,48.860,2.290,48.865 \\ - --output out/esplanade_du_trocadéro.svg} {bash} + --boundary-box=2.284,48.860,2.290,48.865 \\ + --output=out/esplanade_du_trocadéro.svg} {bash} -will download OSM data to \m {cache/2.284,48.860,2.290,48.865.osm} and write output SVG map of the specified area to \m {out/esplanade_du_trocadéro.svg}. +will download OSM data to \m {cache/2.284,48.860,2.290,48.865.osm} and write an output SVG map of the specified area to \m {out/esplanade_du_trocadéro.svg}. \3 {Arguments} {arguments} @@ -218,14 +243,14 @@ Specify tile coordinates\: or specify any geographical coordinates inside a tile\: \code {map-machine tile \\ - --coordinates \formal {latitude},\formal {longitude} \\ - --zoom \formal {OSM zoom levels}} {bash} + --coordinates=\formal {latitude},\formal {longitude} \\ + --zoom=\formal {OSM zoom levels}} {bash} Tile will be stored as SVG file \m {out/tiles/tile___.svg} and PNG file \m {out/tiles/tile___.svg}, where \m {x} and \m {y} are tile coordinates. \m {--zoom} option will be ignored if it is used with \m {--tile} option. Example\: -\code {map-machine tile -c 55.7510637,37.6270761 -z 18} {bash} +\code {map-machine tile -c=55.7510637,37.6270761 -z=18} {bash} will generate SVG file \m {out/tiles/tile_18_158471_81953.svg} and PNG file \m {out/tiles/tile_18_158471_81953.png}. @@ -234,14 +259,14 @@ will generate SVG file \m {out/tiles/tile_18_158471_81953.svg} and PNG file \m { Specify boundary box to get the minimal set of tiles that covers the area\: \code {map-machine tile \\ - --boundary-box \formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\ - --zoom \formal {OSM zoom levels}} {bash} + --boundary-box=\formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\ + --zoom=\formal {OSM zoom levels}} {bash} -Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files \m {out/tiles/tile___.svg} and PNG files \m {out/tiles/tile___.svg}, where \m {x} and \m {y} are tile coordinates. +The boundary box will be extended to the boundaries of the minimal tileset that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files \m {out/tiles/tile___.svg} and PNG files \m {out/tiles/tile___.svg}, where \m {x} and \m {y} are tile coordinates. Example\: -\code {map-machine tile -b 2.361,48.871,2.368,48.875} {bash} +\code {map-machine tile -b=2.361,48.871,2.368,48.875} {bash} will generate 36 PNG tiles at zoom level 18 from tile 18/132791/90164 all the way to 18/132796/90169 and two cached files \m {cache/2.360,48.869,2.370,48.877_18.svg} and \m {cache/2.360,48.869,2.370,48.877_18.png}. @@ -251,15 +276,15 @@ Command \m {server} is used to run tile server for slippy maps. \code {map-machine server} -Stop server interrupting process with \kbd {Ctrl} + \kbd {C}. +Stop server interrupting the process with \kbd {Ctrl} + \kbd {C}. \options {server} -\3 {Example} {example} +\3 {Example} {example-2} Create a minimal amount of tiles that cover specified boundary box for zoom levels 16, 17, 18, and 19\: -\code {map-machine tile -b 2.364,48.854,2.367,48.857 -z 16-19} {bash} +\code {map-machine tile -b=2.364,48.854,2.367,48.857 -z=16-19} {bash} Run tile server on 127.0.0.1\:8080\: @@ -286,7 +311,7 @@ HTML code\: \2 {Map options} {map-options} -Map configuration options used by \m {render} and \m {tile} commands: +Map configuration options used by \m {render} and \m {tile} commands\: \options {map} @@ -298,7 +323,7 @@ To create MapCSS with Map Machine style also for ways and relations, run \m {map \options {mapcss} -\3 {Use Map Machine as JOSM map paint style} +\3 {Use Röntgen as JOSM map paint style} {use-rntgen-as-josm-map-paint-style} \list {Run \m {\command {mapcss}}.} @@ -307,13 +332,13 @@ To create MapCSS with Map Machine style also for ways and relations, run \m {map {Active styles: press \kbd {+}.} {URL / File: set path to \m {out/map_machine_mapcss/map_machine.mapcss}.} -To enable / disable Map Machine map paint style go to \kbd {View} → \kbd {Map Paint Styles} → \kbd {Map Machine}. +To enable/disable Map Machine map paint style go to \kbd {View} → \kbd {Map Paint Styles} → \kbd {Map Machine}. -\4 {Example} +\4 {Example} {example-3} \image {doc/josm.png} {JOSM example} -Example of using Röntgen icons on top of Mapnik style in JOSM. Map Paint Styles look like\: +Example of using Röntgen icons on top of Mapnik style in JOSM. Map Paint Styles look like this\: \list {✓ Mapnik (true)} {✓ Map Machine} diff --git a/doc/surveillance.png b/doc/surveillance.png deleted file mode 100644 index 82f228e..0000000 Binary files a/doc/surveillance.png and /dev/null differ diff --git a/doc/surveillance.svg b/doc/surveillance.svg new file mode 100644 index 0000000..3d6eaf0 --- /dev/null +++ b/doc/surveillance.svg @@ -0,0 +1,2 @@ + +113344112-32-3Cine ChromatixCine ChromatixDeutsche TelekomDeutsche TelekomMo-Fr 22:00Mo-Fr 22:00Mo-Fr 12:15,15:30,17:00,18...Mo-Fr 12:15,15:30,17:00,18...11443.373.374.74.71.71.7333333LitfaßsäuleLitfaßsäuleLOOMLOOM2-32-3SteineckeSteineckeAufgang BAufgang BVDI/VDE-ITVDI/VDE-ITAufgang DAufgang DPrognosPrognosSpirit JogaSpirit Joga33848427 m27 m1111BerlinBerlin8484878787A87AData: © OpenStreetMap contributorsData: © OpenStreetMap contributorsRendering: Map MachineRendering: Map Machine \ No newline at end of file diff --git a/doc/time.png b/doc/time.png deleted file mode 100644 index d8f0faa..0000000 Binary files a/doc/time.png and /dev/null differ diff --git a/doc/time.svg b/doc/time.svg new file mode 100644 index 0000000..813cbee --- /dev/null +++ b/doc/time.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/trees.png b/doc/trees.png deleted file mode 100644 index 68643ef..0000000 Binary files a/doc/trees.png and /dev/null differ diff --git a/doc/trees.svg b/doc/trees.svg new file mode 100644 index 0000000..192ebb3 --- /dev/null +++ b/doc/trees.svg @@ -0,0 +1,2 @@ + +МедиацентрМедиацентрMedia CenterMedia CenterМедиацентрМедиацентрЛугЛугЛугЛугСтепьСтепьBirchBirchData: © OpenStreetMap contributorsData: © OpenStreetMap contributorsRendering: Map MachineRendering: Map Machine \ No newline at end of file diff --git a/doc/viewpoints.png b/doc/viewpoints.png deleted file mode 100644 index b857cb9..0000000 Binary files a/doc/viewpoints.png and /dev/null differ diff --git a/doc/viewpoints.svg b/doc/viewpoints.svg new file mode 100644 index 0000000..2dcb984 --- /dev/null +++ b/doc/viewpoints.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/volcano.svg b/doc/volcano.svg new file mode 100644 index 0000000..7f5a7f8 --- /dev/null +++ b/doc/volcano.svg @@ -0,0 +1,2 @@ + +volcano:type=*stratovolcanoshieldscoriavolcano:status=*activedormantextinct \ No newline at end of file diff --git a/map_machine/__init__.py b/map_machine/__init__.py index 3370ffc..962a55e 100644 --- a/map_machine/__init__.py +++ b/map_machine/__init__.py @@ -19,7 +19,7 @@ REQUIREMENTS = [ "numpy>=1.18.1", "Pillow>=8.2.0", "portolan>=1.0.1", - "pycairo", + "pycairo>=1.20.1", "pytest>=6.2.2", "PyYAML>=4.2b1", "setuptools>=51.0.0", diff --git a/map_machine/color.py b/map_machine/color.py index a656052..9dd5023 100644 --- a/map_machine/color.py +++ b/map_machine/color.py @@ -13,7 +13,7 @@ __email__ = "me@enzet.ru" def is_bright(color: Color) -> bool: """ - Check whether color bright enough to have black outline instead of white. + Check whether color is bright enough to have black outline instead of white. """ return ( 0.2126 * color.red + 0.7152 * color.green + 0.0722 * color.blue @@ -35,7 +35,7 @@ def get_gradient_color( scale: List[Color] = colors + [Color("black")] range_coefficient: float = ( - 0 if bounds.is_empty() else (value - bounds.min_) / bounds.delta() + 0.0 if bounds.is_empty() else (value - bounds.min_) / bounds.delta() ) # If value is out of range, set it to boundary value. range_coefficient = min(1.0, max(0.0, range_coefficient)) diff --git a/map_machine/constructor.py b/map_machine/constructor.py index 61c417e..e2aed2a 100644 --- a/map_machine/constructor.py +++ b/map_machine/constructor.py @@ -2,6 +2,7 @@ Construct Map Machine nodes and ways. """ import logging +import sys from datetime import datetime from hashlib import sha256 from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union @@ -9,18 +10,26 @@ from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union import numpy as np from colour import Color -from map_machine import ui from map_machine.color import get_gradient_color +from map_machine.feature.building import Building, BUILDING_SCALE +from map_machine.feature.crater import Crater +from map_machine.feature.direction import DirectionSector +from map_machine.feature.road import Road, Roads from map_machine.figure import ( - Building, - Crater, - DirectionSector, StyledFigure, - Tree, ) -from map_machine.road import Road, Roads -from map_machine.flinger import Flinger -from map_machine.icon import ( +from map_machine.feature.tree import Tree +from map_machine.geometry.flinger import Flinger +from map_machine.map_configuration import DrawingMode, MapConfiguration +from map_machine.osm.osm_reader import ( + OSMData, + OSMNode, + OSMRelation, + OSMWay, + parse_levels, + Tags, +) +from map_machine.pictogram.icon import ( DEFAULT_SMALL_SHAPE_ID, Icon, IconSet, @@ -28,18 +37,10 @@ from map_machine.icon import ( ShapeExtractor, ShapeSpecification, ) -from map_machine.map_configuration import DrawingMode, MapConfiguration -from map_machine.osm_reader import ( - OSMData, - OSMNode, - OSMRelation, - OSMWay, - parse_levels, -) -from map_machine.point import Point -from map_machine.scheme import DEFAULT_COLOR, LineStyle, RoadMatcher, Scheme -from map_machine.text import Label -from map_machine.ui import BuildingMode +from map_machine.pictogram.point import Point +from map_machine.scheme import LineStyle, RoadMatcher, Scheme +from map_machine.text import Label, TextConstructor +from map_machine.ui.cli import BuildingMode from map_machine.util import MinMax __author__ = "Sergey Vartanov" @@ -92,7 +93,9 @@ def get_time_color(time: Optional[datetime], boundaries: MinMax) -> Color: :param time: current element creation time :param boundaries: minimum and maximum element creation time on the map """ - return get_gradient_color(time, boundaries, TIME_COLOR_SCALE) + return get_gradient_color( + time if time else boundaries.max_, boundaries, TIME_COLOR_SCALE + ) def glue(ways: List[OSMWay]) -> List[List[OSMNode]]: @@ -153,9 +156,7 @@ def try_to_glue( class Constructor: - """ - Map Machine node and way constructor. - """ + """Map Machine node and way constructor.""" def __init__( self, @@ -170,6 +171,7 @@ class Constructor: self.scheme: Scheme = scheme self.extractor: ShapeExtractor = extractor self.configuration: MapConfiguration = configuration + self.text_constructor: TextConstructor = TextConstructor(self.scheme) if self.configuration.level == "all": self.check_level = lambda x: True @@ -190,7 +192,7 @@ class Constructor: self.craters: List[Crater] = [] self.direction_sectors: List[DirectionSector] = [] - self.heights: Set[float] = {2, 4} + self.heights: Set[float] = {0.25 / BUILDING_SCALE, 0.5 / BUILDING_SCALE} def add_building(self, building: Building) -> None: """Add building and update levels.""" @@ -206,18 +208,11 @@ class Constructor: def construct_ways(self) -> None: """Construct Map Machine ways.""" - for index, way_id in enumerate(self.osm_data.ways): - ui.progress_bar( - index, - len(self.osm_data.ways), - step=10, - text="Constructing ways", - ) + logging.info("Constructing ways...") + for way_id in self.osm_data.ways: way: OSMWay = self.osm_data.ways[way_id] self.construct_line(way, [], [way.nodes]) - ui.progress_bar(-1, len(self.osm_data.ways), text="Constructing ways") - def construct_line( self, line: Union[OSMWay, OSMRelation], @@ -225,7 +220,7 @@ class Constructor: outers: List[List[OSMNode]], ) -> None: """ - Way or relation construction. + Construct way or relation. :param line: OpenStreetMap way or relation :param inners: list of polygons that compose inner boundary @@ -233,23 +228,38 @@ class Constructor: """ assert len(outers) >= 1 + if len(outers[0]) == 0: + return + if not self.check_level(line.tags): return - center_point, center_coordinates = line_center(outers[0], self.flinger) + center_point, _ = line_center(outers[0], self.flinger) if self.configuration.is_wireframe(): color: Color if self.configuration.drawing_mode == DrawingMode.AUTHOR: - color = get_user_color(line.user, self.configuration.seed) - else: # self.mode == TIME_MODE + color = get_user_color( + line.user if line.user else "", self.configuration.seed + ) + elif self.configuration.drawing_mode == DrawingMode.TIME: color = get_time_color(line.timestamp, self.osm_data.time) + elif self.configuration.drawing_mode == DrawingMode.WHITE: + color = Color("#666666") + elif self.configuration.drawing_mode == DrawingMode.BLACK: + color = Color("#BBBBBB") + elif self.configuration.drawing_mode != DrawingMode.NORMAL: + logging.fatal( + f"Drawing mode {self.configuration.drawing_mode} is not " + f"supported." + ) + sys.exit(1) self.draw_special_mode(line, inners, outers, color) return if not line.tags: return - building_mode: str = self.configuration.building_mode + building_mode: BuildingMode = self.configuration.building_mode if "building" in line.tags or ( building_mode == BuildingMode.ISOMETRIC and "building:part" in line.tags @@ -260,9 +270,10 @@ class Constructor: road_matcher: RoadMatcher = self.scheme.get_road(line.tags) if road_matcher: - self.roads.append( - Road(line.tags, outers[0], road_matcher, self.flinger) + road: Road = Road( + line.tags, outers[0], road_matcher, self.flinger, self.scheme ) + self.roads.append(road) return processed: Set[str] = set() @@ -283,13 +294,16 @@ class Constructor: line_style.style ) new_style["stroke"] = recolor.hex - line_style = LineStyle(new_style, line_style.priority) + line_style = LineStyle( + new_style, line_style.parallel_offset, line_style.priority + ) self.figures.append( StyledFigure(line.tags, inners, outers, line_style) ) if not ( line.get_tag("area") == "yes" + or line.get_tag("type") == "multipolygon" or is_cycle(outers[0]) and line.get_tag("area") != "no" and self.scheme.is_area(line.tags) @@ -302,8 +316,10 @@ class Constructor: self.extractor, line.tags, processed, self.configuration ) if icon_set is not None: - labels: List[Label] = self.scheme.construct_text( - line.tags, "all", processed + labels: List[Label] = self.text_constructor.construct_text( + line.tags, + processed, + self.configuration.label_mode, ) point: Point = Point( icon_set, @@ -317,43 +333,45 @@ class Constructor: ) self.points.append(point) - if not line_styles: - if DEBUG: - style: Dict[str, Any] = { - "fill": "none", - "stroke": Color("red").hex, - "stroke-width": 1, - } - figure: StyledFigure = StyledFigure( - line.tags, inners, outers, LineStyle(style, 1000) - ) - self.figures.append(figure) + if line_styles: + return - processed: Set[str] = set() + self.add_point_for_line(center_point, inners, line, outers) - priority: int - icon_set: IconSet - icon_set, priority = self.scheme.get_icon( - self.extractor, + def add_point_for_line(self, center_point, inners, line, outers) -> None: + """Add icon at the center point of the way or relation.""" + if DEBUG: + style: Dict[str, Any] = { + "fill": "none", + "stroke": Color("red").hex, + "stroke-width": 1.0, + } + figure: StyledFigure = StyledFigure( + line.tags, inners, outers, LineStyle(style, 0.0, 1000.0) + ) + self.figures.append(figure) + + processed: set[str] = set() + priority: int + icon_set: IconSet + icon_set, priority = self.scheme.get_icon( + self.extractor, line.tags, processed, self.configuration + ) + if icon_set is not None: + labels: list[Label] = self.text_constructor.construct_text( + line.tags, processed, self.configuration.label_mode + ) + point: Point = Point( + icon_set, + labels, line.tags, processed, - self.configuration, + center_point, + is_for_node=False, + priority=priority, + add_tooltips=self.configuration.show_tooltips, ) - if icon_set is not None: - labels: List[Label] = self.scheme.construct_text( - line.tags, "all", processed - ) - point: Point = Point( - icon_set, - labels, - line.tags, - processed, - center_point, - is_for_node=False, - priority=priority, - add_tooltips=self.configuration.show_tooltips, - ) - self.points.append(point) + self.points.append(point) def draw_special_mode( self, @@ -400,21 +418,20 @@ class Constructor: def construct_nodes(self) -> None: """Draw nodes.""" + logging.info("Constructing nodes...") + sorted_node_ids: Iterator[int] = sorted( self.osm_data.nodes.keys(), key=lambda x: -self.osm_data.nodes[x].coordinates[0], ) - - for index, node_id in enumerate(sorted_node_ids): - ui.progress_bar( - index, len(self.osm_data.nodes), text="Constructing nodes" - ) + for node_id in sorted_node_ids: self.construct_node(self.osm_data.nodes[node_id]) - ui.progress_bar(-1, len(self.osm_data.nodes), text="Constructing nodes") def construct_node(self, node: OSMNode) -> None: """Draw one node.""" tags: Dict[str, str] = node.tags + if not tags: + return if not self.check_level(tags): return @@ -426,17 +443,21 @@ class Constructor: icon_set: IconSet draw_outline: bool = True - if self.configuration.is_wireframe(): - if not tags: - return - color: Color = DEFAULT_COLOR + if self.configuration.drawing_mode in ( + DrawingMode.AUTHOR, + DrawingMode.TIME, + ): + color: Color = self.scheme.get_color("default") if self.configuration.drawing_mode == DrawingMode.AUTHOR: color = get_user_color(node.user, self.configuration.seed) if self.configuration.drawing_mode == DrawingMode.TIME: color = get_time_color(node.timestamp, self.osm_data.time) dot: Shape = self.extractor.get_shape(DEFAULT_SMALL_SHAPE_ID) icon_set: IconSet = IconSet( - Icon([ShapeSpecification(dot, color)]), [], set() + Icon([ShapeSpecification(dot, color)]), + [], + Icon([ShapeSpecification(dot, color)]), + set(), ) point: Point = Point( icon_set, @@ -450,12 +471,38 @@ class Constructor: self.points.append(point) return + if self.configuration.drawing_mode in ( + DrawingMode.WHITE, + DrawingMode.BLACK, + ): + if self.configuration.drawing_mode == DrawingMode.WHITE: + color = Color("#CCCCCC") + if self.configuration.drawing_mode == DrawingMode.BLACK: + color = Color("#444444") + icon_set, priority = self.scheme.get_icon( + self.extractor, tags, processed, self.configuration + ) + icon_set.main_icon.recolor(color) + point: Point = Point( + icon_set, + [], + tags, + processed, + flung, + add_tooltips=self.configuration.show_tooltips, + ) + self.points.append(point) + return + icon_set, priority = self.scheme.get_icon( self.extractor, tags, processed, self.configuration ) if icon_set is None: return - labels: List[Label] = self.scheme.construct_text(tags, "all", processed) + + labels: List[Label] = self.text_constructor.construct_text( + tags, processed, self.configuration.label_mode + ) self.scheme.process_ignored(tags, processed) if node.get_tag("natural") == "tree" and ( @@ -483,7 +530,7 @@ class Constructor: self.points.append(point) -def check_level_number(tags: Dict[str, Any], level: float) -> bool: +def check_level_number(tags: Tags, level: float) -> bool: """Check if element described by tags is no the specified level.""" if "level" in tags: if level not in parse_levels(tags["level"]): @@ -493,13 +540,12 @@ def check_level_number(tags: Dict[str, Any], level: float) -> bool: return True -def check_level_overground(tags: Dict[str, Any]) -> bool: +def check_level_overground(tags: Tags) -> bool: """Check if element described by tags is overground.""" if "level" in tags: try: - levels: map = map(float, tags["level"].replace(",", ".").split(";")) - for level in levels: - if level < 0: + for level in map(float, tags["level"].replace(",", ".").split(";")): + if level < 0.0: return False except ValueError: pass @@ -507,4 +553,5 @@ def check_level_overground(tags: Dict[str, Any]) -> bool: return ( tags.get("location") != "underground" and tags.get("parking") != "underground" + and tags.get("tunnel") != "yes" ) diff --git a/map_machine/doc/__init__.py b/map_machine/doc/__init__.py new file mode 100644 index 0000000..12eaacc --- /dev/null +++ b/map_machine/doc/__init__.py @@ -0,0 +1,3 @@ +""" +Documentation utilities. +""" diff --git a/map_machine/doc/collections.py b/map_machine/doc/collections.py new file mode 100644 index 0000000..a892746 --- /dev/null +++ b/map_machine/doc/collections.py @@ -0,0 +1,335 @@ +""" +Special icon collections for documentation. +""" +import json +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Optional, List, Dict, Set + +import numpy as np +import svgwrite +from svgwrite import Drawing +from svgwrite.text import Text +from svgwrite.shapes import Line, Rect + +from map_machine.map_configuration import MapConfiguration +from map_machine.osm.osm_reader import Tags +from map_machine.pictogram.icon import ShapeExtractor, IconSet +from map_machine.scheme import Scheme +from map_machine.workspace import Workspace + +WORKSPACE: Workspace = Workspace(Path("temp")) + +SCHEME: Scheme = Scheme.from_file(WORKSPACE.DEFAULT_SCHEME_PATH) +EXTRACTOR: ShapeExtractor = ShapeExtractor( + WORKSPACE.ICONS_PATH, WORKSPACE.ICONS_CONFIG_PATH +) + + +@dataclass +class Collection: + """Icon collection.""" + + # Core tags + tags: Tags + + # Tag key to be used in rows + row_key: Optional[str] = None + + # List of tag values to be used in rows + row_values: List[str] = field(default_factory=list) + + # Tag key to be used in columns + column_key: Optional[str] = None + + # List of tag values to be used in columns + column_values: List[str] = field(default_factory=list) + + # List of tags to be used in rows + row_tags: List[Tags] = field(default_factory=list) + + @classmethod + def deserialize(cls, structure: Dict[str, Any]): + """Deserialize icon collection from structure.""" + row_key: Optional[str] = ( + structure["row_key"] if "row_key" in structure else None + ) + row_values: List[str] = ( + structure["row_values"] if "row_values" in structure else [] + ) + column_key: Optional[str] = ( + structure["column_key"] if "column_key" in structure else None + ) + column_values: List[str] = ( + structure["column_values"] if "column_values" in structure else [] + ) + row_tags: List[Tags] = ( + structure["row_tags"] if "row_tags" in structure else [] + ) + return cls( + structure["tags"], + row_key, + row_values, + column_key, + column_values, + row_tags, + ) + + +class SVGTable: + """SVG table with icon combinations.""" + + def __init__(self, collection: Collection, svg: svgwrite.Drawing): + self.collection: Collection = collection + self.svg: svgwrite.Drawing = svg + + self.border: np.ndarray = np.array((16.0, 16.0)) + self.step: float = 48.0 + self.icon_size: float = 32.0 + self.font_size: float = 10.0 + self.offset: float = 30.0 + self.half_step: np.ndarray = np.array( + (self.step / 2.0, self.step / 2.0) + ) + + fonts: List[str] = [ + "JetBrains Mono", + "Fira Code", + "Fira Mono", + "ui-monospace", + "SFMono-regular", + "SF Mono", + "Menlo", + "Consolas", + "Liberation Mono", + "monospace", + ] + self.font: str = ",".join(fonts) + self.font_width: float = self.font_size * 0.7 + + self.size: List[float] = [ + max( + max(map(len, self.collection.row_values)) * self.font_width, + len(self.collection.row_key) * self.font_width + + (self.offset if self.collection.column_values else 0), + 170.0, + ) + if self.collection.row_values + else 0.0, + max(map(len, self.collection.column_values)) * self.font_width + if self.collection.column_values + else 0.0, + ] + self.start_point: np.ndarray = ( + 2 * self.border + np.array(self.size) + self.half_step + ) + + def draw_table(self) -> None: + """Draw SVG table.""" + self.draw_rows() + self.draw_columns() + self.draw_delimiter() + self.draw_rectangle() + + for i, row_value in enumerate(self.collection.row_values): + for j, column_value in enumerate( + ( + self.collection.column_values + if self.collection.column_values + else [""] + ) + ): + current_tags: Tags = dict(self.collection.tags) | { + self.collection.row_key: row_value + } + if column_value: + current_tags |= {self.collection.column_key: column_value} + processed: Set[str] = set() + icon, _ = SCHEME.get_icon( + EXTRACTOR, current_tags, processed, MapConfiguration() + ) + processed = icon.processed + if not icon: + print("Icon was not constructed.") + + if ( + icon.main_icon + and not icon.main_icon.is_default() + and ( + not self.collection.column_key + or not column_value + or (self.collection.column_key in processed) + ) + and ( + not self.collection.row_key + or not row_value + or (self.collection.row_key in processed) + ) + ): + self.draw_icon(np.array((j, i)), icon) + else: + self.draw_cross(np.array((j, i))) + + width, height = self.get_size() + self.svg.update({"width": width, "height": height}) + + def draw_rows(self) -> None: + """Draw row texts.""" + point: np.ndarray = np.array(self.start_point) - np.array( + (self.step / 2.0 + self.border[0], 0.0) + ) + shift: np.ndarray = ( + -self.offset if self.collection.column_values else 0.9, + 2.0 - self.step / 2.0 - self.border[1], + ) + if self.collection.row_key: + self.draw_text( + f"{self.collection.row_key}=*", + point + np.array(shift), + anchor="end", + weight="bold", + ) + for row_value in self.collection.row_values: + if row_value: + self.draw_text( + row_value, point + np.array((0.0, 2.0)), anchor="end" + ) + point += np.array((0, self.step)) + + def draw_columns(self) -> None: + """Draw column texts.""" + point: np.ndarray = ( + self.start_point + - self.half_step + - self.border + + np.array((0.0, 2.0 - self.offset)) + ) + if self.collection.column_key: + self.draw_text( + f"{self.collection.column_key}=*", + point, + anchor="end", + weight="bold", + ) + + point = np.array(self.start_point) + for column_value in self.collection.column_values: + text_point: np.ndarray = point + np.array( + (2.0, -self.step / 2.0 - self.border[1]) + ) + self.draw_text(f"{column_value}", text_point, rotate=True) + point += np.array((self.step, 0.0)) + + def draw_delimiter(self) -> None: + """Draw line between column and row titles.""" + if self.collection.column_values: + line: Line = self.svg.line( + self.start_point - self.half_step - self.border, + self.start_point + - self.half_step + - self.border + - np.array((15, 15)), + stroke_width=0.5, + stroke="black", + ) + self.svg.add(line) + + def draw_rectangle(self, color: str = "#FEA") -> None: + """Draw rectangle beneath all cells.""" + rectangle: Rect = self.svg.rect( + self.start_point - self.half_step, + np.array( + ( + max(1, len(self.collection.column_values)), + len(self.collection.row_values), + ) + ) + * self.step, + fill=color, + ) + self.svg.add(rectangle) + + def draw_icon(self, position: np.ndarray, icon: IconSet) -> None: + """Draw icon in the table cell.""" + if not self.collection.column_values: + self.collection.column_values = [""] + point: np.ndarray = np.array(self.start_point) + position * self.step + icon.main_icon.draw(self.svg, point, scale=self.icon_size / 16.0) + + def draw_text( + self, + text: str, + point: np.ndarray, + anchor: str = "start", + weight: str = "normal", + rotate: bool = False, + ) -> None: + """Draw text on the table.""" + text: Text = self.svg.text( + text, + point, + font_family=self.font, + font_size=self.font_size, + text_anchor=anchor, + font_weight=weight, + ) + if rotate: + text.update({"transform": f"rotate(270,{point[0]},{point[1]})"}) + self.svg.add(text) + + def draw_cross(self, position: np.ndarray, size: float = 15) -> None: + """Draw cross in the cell.""" + point: np.ndarray = self.start_point + position * self.step + for vector in np.array((1, 1)), np.array((1, -1)): + line: Line = self.svg.line( + point - size * vector, + point + size * vector, + stroke_width=0.5, + stroke="black", + ) + self.svg.add(line) + + def get_size(self) -> np.ndarray: + """Get the whole picture size.""" + return ( + self.start_point + + np.array( + ( + max(1, len(self.collection.column_values)), + len(self.collection.row_values), + ) + ) + * self.step + - self.half_step + + self.border + ) + + +def draw_svg_tables(output_path: Path, html_file_path: Path) -> None: + """Draw SVG tables of icon collections.""" + + with Path("data/collections.json").open() as input_file: + collections: List[Dict[str, Any]] = json.load(input_file) + + with html_file_path.open("w+") as html_file: + for structure in collections: + if "id" not in structure: + continue + + path: Path = output_path / f"{structure['id']}.svg" + svg: Drawing = svgwrite.Drawing(path.name) + + collection: Collection = Collection.deserialize(structure) + + table: SVGTable = SVGTable(collection, svg) + table.draw_table() + + with path.open("w+") as output_file: + svg.write(output_file) + html_file.write( + f'\n' + ) + + +if __name__ == "__main__": + draw_svg_tables(Path("doc"), Path("result.html")) diff --git a/map_machine/doc/icons.py b/map_machine/doc/icons.py new file mode 100644 index 0000000..7ec9df8 --- /dev/null +++ b/map_machine/doc/icons.py @@ -0,0 +1,76 @@ +""" +Icon grids for documentation. +""" +from pathlib import Path +from typing import Iterable + +from colour import Color + +from map_machine.pictogram.icon import ( + Shape, + Icon, + ShapeSpecification, + ShapeExtractor, +) +from map_machine.pictogram.icon_collection import IconCollection +from map_machine.workspace import workspace + + +SKIP: bool = True + + +def draw_special_grid(all_shapes, function, path, color=None): + """Draw special icon grid to illustrate map feature.""" + icons = [ + Icon([ShapeSpecification(shape)]) + for shape in all_shapes + if function(shape) + ] + icons = sorted(icons) + + if color: + for icon in icons: + icon.recolor(color) + + IconCollection(icons).draw_grid(path, 8, scale=4.0) + + +def draw_special_grids(): + """Draw special icon grids.""" + extractor: ShapeExtractor = ShapeExtractor( + workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH + ) + all_shapes: Iterable[Shape] = extractor.shapes.values() + + draw_special_grid( + all_shapes, + lambda shape: shape.id_.startswith("power_tower") + or shape.id_.startswith("power_pole"), + Path("doc/icons_power.svg"), + ) + if SKIP: + draw_special_grid( + all_shapes, + lambda shape: shape.group == "root_space", + Path("doc/icons_space.svg"), + ) + draw_special_grid( + all_shapes, + lambda shape: shape.group == "root_street_playground", + Path("doc/icons_playground.svg"), + ) + draw_special_grid( + all_shapes, + lambda shape: "emergency" in shape.categories, + Path("doc/icons_emergency.svg"), + color=Color("#DD2222"), + ) + draw_special_grid( + all_shapes, + lambda shape: shape.id_.startswith("japan"), + Path("doc/icons_japanese.svg"), + ) + + +if __name__ == "__main__": + draw_special_grids() diff --git a/map_machine/moire_manager.py b/map_machine/doc/moire_manager.py similarity index 68% rename from map_machine/moire_manager.py rename to map_machine/doc/moire_manager.py index 50de341..ec67258 100644 --- a/map_machine/moire_manager.py +++ b/map_machine/doc/moire_manager.py @@ -9,9 +9,9 @@ from typing import Any, Dict, List, Union from moire.default import Default, DefaultHTML, DefaultMarkdown, DefaultWiki from moire.moire import Tag -from map_machine import ui -from map_machine.icon import ShapeExtractor -from map_machine.ui import COMMANDS +from map_machine.pictogram.icon import ShapeExtractor +from map_machine.ui import cli +from map_machine.ui.cli import COMMAND_LINES from map_machine.workspace import workspace __author__ = "Sergey Vartanov" @@ -51,12 +51,12 @@ class ArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs) -> None: self.arguments: List[Dict[str, Any]] = [] - super(ArgumentParser, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def add_argument(self, *args, **kwargs) -> None: """Just store argument with options.""" - super(ArgumentParser, self).add_argument(*args, **kwargs) - argument: Dict[str, Any] = {"arguments": [x for x in args]} + super().add_argument(*args, **kwargs) + argument: Dict[str, Any] = {"arguments": args} for key in kwargs: argument[key] = kwargs[key] @@ -75,8 +75,8 @@ class ArgumentParser(argparse.ArgumentParser): continue array: Code = [ - [Tag("no_wrap", [Tag("m", [x])]), ", "] - for x in option["arguments"] + [Tag("no_wrap", [Tag("m", [text])]), ", "] + for text in option["arguments"] ] cell: Code = [x for y in array for x in y][:-1] if "metavar" in option: @@ -116,74 +116,70 @@ class ArgumentParser(argparse.ArgumentParser): class MapMachineMoire(Default, ABC): - """ - Moire extension stub for Map Machine. - """ + """Moire extension stub for Map Machine.""" - def osm(self, args: Arguments) -> str: + def osm(self, arg: Arguments) -> str: """OSM tag key or key–value pair of tag.""" - spec: str = self.clear(args[0]) + spec: str = self.clear(arg[0]) if "=" in spec: key, tag = spec.split("=") return ( self.get_ref_(f"{PREFIX}Key:{key}", self.m([key])) - + "=" + + " = " + self.get_ref_(f"{PREFIX}Tag:{key}={tag}", self.m([tag])) ) - else: - return self.get_ref_(f"{PREFIX}Key:{spec}", self.m([spec])) - def color(self, args: Arguments) -> str: + return self.get_ref_(f"{PREFIX}Key:{spec}", self.m([spec])) + + def color(self, arg: Arguments) -> str: """Simple color sample.""" raise NotImplementedError("color") - def page_icon(self, args: Arguments) -> str: + def page_icon(self, arg: Arguments) -> str: """HTML page icon.""" return "" - def command(self, args: Arguments) -> str: + def command(self, arg: Arguments) -> str: """Bash command from integration tests.""" - return "map-machine " + " ".join(COMMANDS[self.clear(args[0])]) + return "map-machine " + " ".join(COMMAND_LINES[self.clear(arg[0])]) - def icon(self, args: Arguments) -> str: + def icon(self, arg: Arguments) -> str: """Image with Röntgen icon.""" raise NotImplementedError("icon") - def options(self, args: Arguments) -> str: + def options(self, arg: Arguments) -> str: """Table with option descriptions.""" parser: ArgumentParser = ArgumentParser() - command: str = self.clear(args[0]) + command: str = self.clear(arg[0]) if command == "render": - ui.add_render_arguments(parser) + cli.add_render_arguments(parser) elif command == "server": - ui.add_server_arguments(parser) + cli.add_server_arguments(parser) elif command == "tile": - ui.add_tile_arguments(parser) + cli.add_tile_arguments(parser) elif command == "map": - ui.add_map_arguments(parser) + cli.add_map_arguments(parser) elif command == "element": - ui.add_element_arguments(parser) + cli.add_element_arguments(parser) elif command == "mapcss": - ui.add_mapcss_arguments(parser) + cli.add_mapcss_arguments(parser) else: raise NotImplementedError( "no separate function for parser creation" ) return self.parse(parser.get_moire_help()) - def kbd(self, args: Arguments) -> str: + def kbd(self, arg: Arguments) -> str: """Keyboard key.""" - return self.m(args) + return self.m(arg) - def no_wrap(self, args: Arguments) -> str: + def no_wrap(self, arg: Arguments) -> str: """Do not wrap text at white spaces.""" - return self.parse(args[0]) + return self.parse(arg[0]) class MapMachineHTML(MapMachineMoire, DefaultHTML): - """ - Simple HTML. - """ + """Simple HTML.""" def __init__(self) -> None: super().__init__() @@ -196,48 +192,46 @@ class MapMachineHTML(MapMachineMoire, DefaultHTML): ["" + self.parse(td, in_block=True) + "" for td in arg[0]] ) content += f"{cell}" - for tr in arg[1:]: + for row in arg[1:]: cell: str = "".join( - ["" + self.parse(td, in_block=True) + "" for td in tr] + ["" + self.parse(td, in_block=True) + "" for td in row] ) content += f"{cell}" return f"{content}
" - def color(self, args: Arguments) -> str: + def color(self, arg: Arguments) -> str: """Simple color sample.""" return ( f'' + f'style="background-color: {self.clear(arg[0])};">' ) - def icon(self, args: Arguments) -> str: + def icon(self, arg: Arguments) -> str: """Image with Röntgen icon.""" - size: str = self.clear(args[1]) if len(args) > 1 else 16 + size: str = self.clear(arg[1]) if len(arg) > 1 else "16" return ( f'' + f'src="out/icons_by_id/{self.clear(arg[0])}.svg" />' ) - def kbd(self, args: Arguments) -> str: + def kbd(self, arg: Arguments) -> str: """Keyboard key.""" - return f"{self.clear(args[0])}" + return f"{self.clear(arg[0])}" - def page_icon(self, args: Arguments) -> str: + def page_icon(self, arg: Arguments) -> str: """HTML page icon.""" return ( - f'' ) - def no_wrap(self, args: Arguments) -> str: + def no_wrap(self, arg: Arguments) -> str: """Do not wrap text at white spaces.""" - return ( - f'{self.parse(args[0])}' - ) + return f'{self.parse(arg[0])}' - def formal(self, args: Arguments) -> str: + def formal(self, arg: Arguments) -> str: """Formal variable.""" - return f'{self.parse(args[0])}' + return f'{self.parse(arg[0])}' class MapMachineOSMWiki(MapMachineMoire, DefaultWiki): @@ -254,55 +248,51 @@ class MapMachineOSMWiki(MapMachineMoire, DefaultWiki): workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) - def osm(self, args: Arguments) -> str: + def osm(self, arg: Arguments) -> str: """OSM tag key or key–value pair of tag.""" - spec: str = self.clear(args[0]) + spec: str = self.clear(arg[0]) if "=" in spec: key, tag = spec.split("=") return f"{{{{Key|{key}|{tag}}}}}" - else: - return f"{{{{Tag|{spec}}}}}" - def color(self, args: Arguments) -> str: + return f"{{{{Tag|{spec}}}}}" + + def color(self, arg: Arguments) -> str: """Simple color sample.""" - return f"{{{{Color box|{self.clear(args[0])}}}}}" + return f"{{{{Color box|{self.clear(arg[0])}}}}}" - def icon(self, args: Arguments) -> str: + def icon(self, arg: Arguments) -> str: """Image with Röntgen icon.""" - size: str = self.clear(args[1]) if len(args) > 1 else 16 - shape_id: str = self.clear(args[0]) + size: str = self.clear(arg[1]) if len(arg) > 1 else "16" + shape_id: str = self.clear(arg[0]) name: str = self.extractor.get_shape(shape_id).name return f"[[File:Röntgen {name}.svg|{size}px]]" class MapMachineMarkdown(MapMachineMoire, DefaultMarkdown): - """ - GitHub flavored markdown. - """ + """GitHub flavored markdown.""" images = {} - def color(self, args: Arguments) -> str: + def color(self, arg: Arguments) -> str: """Simple color sample.""" - return self.clear(args[0]) + return self.clear(arg[0]) - def icon(self, args: Arguments) -> str: + def icon(self, arg: Arguments) -> str: """Image with Röntgen icon.""" - return f"[{self.clear(args[0])}]" + return f"[{self.clear(arg[0])}]" - def kbd(self, args: Arguments) -> str: + def kbd(self, arg: Arguments) -> str: """Keyboard key.""" - return f"{self.clear(args[0])}" + return f"{self.clear(arg[0])}" - def no_wrap(self, args: Arguments) -> str: + def no_wrap(self, arg: Arguments) -> str: """Do not wrap text at white spaces.""" - return ( - f'{self.parse(args[0])}' - ) + return f'{self.parse(arg[0])}' - def formal(self, args: Arguments) -> str: + def formal(self, arg: Arguments) -> str: """Formal variable.""" - return f"<{self.parse(args[0])}>" + return f"<{self.parse(arg[0])}>" def convert(input_path: Path, output_path: Path) -> None: diff --git a/map_machine/doc/preview.py b/map_machine/doc/preview.py new file mode 100755 index 0000000..2c5f5d7 --- /dev/null +++ b/map_machine/doc/preview.py @@ -0,0 +1,198 @@ +""" +Actions to perform before commit: generate PNG images for documentation. +""" +import logging +import sys +from pathlib import Path +from typing import Optional + +import numpy as np +import svgwrite + +from map_machine.geometry.boundary_box import BoundaryBox +from map_machine.constructor import Constructor +from map_machine.geometry.flinger import Flinger +from map_machine.pictogram.icon import ShapeExtractor +from map_machine.mapper import Map +from map_machine.map_configuration import ( + BuildingMode, + DrawingMode, + LabelMode, + MapConfiguration, +) +from map_machine.osm.osm_getter import get_osm +from map_machine.osm.osm_reader import OSMData +from map_machine.scheme import Scheme + +doc_path: Path = Path("doc") + +cache: Path = Path("cache") +cache.mkdir(exist_ok=True) + +SCHEME: Scheme = Scheme.from_file(Path("map_machine/scheme/default.yml")) +EXTRACTOR: ShapeExtractor = ShapeExtractor( + Path("map_machine/icons/icons.svg"), + Path("map_machine/icons/config.json"), +) + + +def draw( + input_file_name: Path, + output_file_name: Path, + boundary_box: BoundaryBox, + configuration: MapConfiguration = MapConfiguration(), +) -> None: + """Draw file.""" + osm_data: OSMData = OSMData() + osm_data.parse_osm_file(input_file_name) + flinger: Flinger = Flinger( + boundary_box, configuration.zoom_level, osm_data.equator_length + ) + constructor: Constructor = Constructor( + osm_data, flinger, SCHEME, EXTRACTOR, configuration + ) + constructor.construct() + + svg: svgwrite.Drawing = svgwrite.Drawing( + str(output_file_name), size=flinger.size + ) + map_: Map = Map(flinger, svg, SCHEME, configuration) + map_.draw(constructor) + + svg.write(output_file_name.open("w")) + + +def draw_around_point( + point: np.ndarray, + name: str, + configuration: MapConfiguration = MapConfiguration(), + size: np.ndarray = np.array((600, 400)), + get: Optional[BoundaryBox] = None, +) -> None: + """Draw around point.""" + input_path: Path = doc_path / f"{name}.svg" + + boundary_box: BoundaryBox = BoundaryBox.from_coordinates( + point, configuration.zoom_level, size[0], size[1] + ) + get_boundary_box = get if get else boundary_box + + get_osm(get_boundary_box, cache / f"{get_boundary_box.get_format()}.osm") + draw( + cache / f"{get_boundary_box.get_format()}.osm", + input_path, + boundary_box, + configuration, + ) + + +def main(id_: str) -> None: + """Entry point.""" + if id_ is None or id_ == "fitness": + draw_around_point( + np.array((55.75277, 37.40856)), + "fitness", + MapConfiguration(zoom_level=20.2), + np.array((300, 200)), + ) + + if id_ is None or id_ == "power": + draw_around_point( + np.array((52.5622, 12.94)), + "power", + configuration=MapConfiguration(zoom_level=15), + ) + + if id_ is None or id_ == "playground": + draw_around_point( + np.array((52.47388, 13.43826)), + "playground", + configuration=MapConfiguration(zoom_level=19), + ) + + # Playground: (59.91991/10.85535), (59.83627/10.83017), Oslo + # (52.47604/13.43701), (52.47388/13.43826)*, Berlin + + if id_ is None or id_ == "surveillance": + draw_around_point( + np.array((52.50892, 13.3244)), + "surveillance", + MapConfiguration( + zoom_level=18.5, + ignore_level_matching=True, + ), + ) + + if id_ is None or id_ == "viewpoints": + draw_around_point( + np.array((52.421, 13.101)), + "viewpoints", + MapConfiguration( + label_mode=LabelMode.NO, + zoom_level=15.7, + ignore_level_matching=True, + ), + ) + + if id_ is None or id_ == "buildings": + draw_around_point( + np.array((-26.19049, 28.05605)), + "buildings", + MapConfiguration(building_mode=BuildingMode.ISOMETRIC), + ) + + if id_ is None or id_ == "trees": + draw_around_point( + np.array((55.751, 37.628)), + "trees", + MapConfiguration( + label_mode=LabelMode(LabelMode.ALL), zoom_level=18.1 + ), + get=BoundaryBox(37.624, 55.749, 37.633, 55.753), + ) + + # if id_ is None or id_ == "golf": + # tiles = Tiles(np.array((52.5859, 13.4644)), 17, 2, 3) + # tiles.draw() + + if id_ is None or id_ == "time": + draw_around_point( + np.array((55.7655, 37.6055)), + "time", + MapConfiguration( + DrawingMode.TIME, + zoom_level=16.5, + ignore_level_matching=True, + ), + ) + + if id_ is None or id_ == "author": + draw_around_point( + np.array((55.7655, 37.6055)), + "author", + MapConfiguration( + DrawingMode.AUTHOR, + seed="a", + zoom_level=16.5, + ignore_level_matching=True, + ), + ) + + if id_ is None or id_ == "colors": + draw_around_point( + np.array((48.87422, 2.377)), + "colors", + configuration=MapConfiguration( + zoom_level=17.6, + building_mode=BuildingMode.ISOMETRIC, + ignore_level_matching=True, + ), + ) + + if id_ is None or id_ == "lanes": + draw_around_point(np.array((47.61224, -122.33866)), "lanes") + + +if __name__ == "__main__": + logging.basicConfig(format="%(levelname)s %(message)s", level=logging.DEBUG) + main(None if len(sys.argv) < 2 else sys.argv[1]) diff --git a/map_machine/taginfo.py b/map_machine/doc/taginfo.py similarity index 93% rename from map_machine/taginfo.py rename to map_machine/doc/taginfo.py index 277705e..8e6612c 100644 --- a/map_machine/taginfo.py +++ b/map_machine/doc/taginfo.py @@ -22,9 +22,7 @@ from map_machine.workspace import workspace class TaginfoProjectFile: - """ - JSON structure with OpenStreetMap tag usage. - """ + """JSON structure with OpenStreetMap tag usage.""" def __init__(self, path: Path, scheme: Scheme) -> None: self.path: Path = path @@ -56,8 +54,8 @@ class TaginfoProjectFile: key: str = list(matcher.tags.keys())[0] value: str = matcher.tags[key] ids: List[str] = [ - (x if isinstance(x, str) else x["shape"]) - for x in matcher.shapes + (shape if isinstance(shape, str) else shape["shape"]) + for shape in matcher.shapes ] icon_id: str = "___".join(ids) if value == "*": diff --git a/map_machine/doc/wiki.py b/map_machine/doc/wiki.py new file mode 100644 index 0000000..1a5ede4 --- /dev/null +++ b/map_machine/doc/wiki.py @@ -0,0 +1,224 @@ +""" +Automate OpenStreetMap wiki editing. +""" +import re +from pathlib import Path +from typing import Optional + +from map_machine.doc.collections import Collection +from map_machine.map_configuration import MapConfiguration +from map_machine.osm.osm_reader import Tags +from map_machine.pictogram.icon import Icon, ShapeExtractor +from map_machine.scheme import Scheme +from map_machine.workspace import Workspace + +WORKSPACE: Workspace = Workspace(Path("temp")) + +SCHEME: Scheme = Scheme.from_file(WORKSPACE.DEFAULT_SCHEME_PATH) +EXTRACTOR: ShapeExtractor = ShapeExtractor( + WORKSPACE.ICONS_PATH, WORKSPACE.ICONS_CONFIG_PATH +) + +HEADER_PATTERN: re.Pattern = re.compile("==?=?.*==?=?") +HEADER_2_PATTERN: re.Pattern = re.compile("== .* ==") +HEADER_PATTERNS: list[re.Pattern] = [ + re.compile("==\\s*Example.*=="), + re.compile("==\\s*See also\\s*=="), +] +RENDERING_HEADER_PATTERN: re.Pattern = re.compile("==\\s*Rendering.*==") +ROENTGEN_HEADER_PATTERN: re.Pattern = re.compile("===.*Röntgen.*===") + + +class WikiTable: + """SVG table with icon combinations.""" + + def __init__(self, collection: Collection, page_name: str): + self.collection: Collection = collection + self.page_name: str = page_name + + def generate_wiki_table(self) -> tuple[str, list[Icon]]: + """ + Generate Röntgen icon table for the OpenStreetMap wiki page. + """ + icons: list[Icon] = [] + text: str = '{| class="wikitable"\n' + + if self.collection.column_key is not None: + text += f"! {{{{Key|{self.collection.column_key}}}}}" + else: + text += "! Tag || Icon" + + if self.collection.row_tags: + text += "\n" + for current_tags in self.collection.row_tags: + text += "|-\n" + text += "| " + if current_tags: + for key, value in current_tags.items(): + if value == "*": + text += f"{{{{Key|{key}}}}}
" + else: + text += f"{{{{Tag|{key}|{value}}}}}
" + text = text[:-6] + text += "\n" + icon, _ = SCHEME.get_icon( + EXTRACTOR, + current_tags | self.collection.tags, + set(), + MapConfiguration(ignore_level_matching=True), + ) + icons.append(icon.main_icon) + text += ( + "| " + f"[[Image:Röntgen {icon.main_icon.get_name()}.svg|32px]]\n" + ) + text += "|}\n" + return text, icons + + if not self.collection.column_values: + self.collection.column_values = [""] + else: + make_vertical: bool = False + for column_value in self.collection.column_values: + if column_value and len(column_value) > 2: + make_vertical = True + for column_value in self.collection.column_values: + text += " ||" + if column_value: + tag: str = ( + f"{{{{TagValue|" + f"{self.collection.column_key}|{column_value}}}}}" + ) + text += " " + ( + f"{{{{vert header|{tag}}}}}" if make_vertical else tag + ) + text += "\n" + + for row_value in self.collection.row_values: + text += "|-\n" + if row_value: + text += f"| {{{{Tag|{self.collection.row_key}|{row_value}}}}}\n" + else: + text += "|\n" + for column_value in self.collection.column_values: + current_tags: Tags = dict(self.collection.tags) | { + self.collection.row_key: row_value + } + if column_value: + current_tags |= {self.collection.column_key: column_value} + icon, _ = SCHEME.get_icon(EXTRACTOR, current_tags, set()) + if not icon: + print("Icon was not constructed.") + text += ( + "| " + f"[[Image:Röntgen {icon.main_icon.get_name()}.svg|32px]]\n" + ) + icons.append(icon.main_icon) + + text += "|}\n" + + return text, icons + + +def generate_new_text( + old_text: str, + table: WikiTable, +) -> tuple[Optional[str], list[Icon]]: + """ + Generate Röntgen icon table for the OpenStreetMap wiki page. + + :param old_text: previous wiki page text + :param table: wiki table generator + :return: new wiki page text + """ + wiki_text: str + icons = [] + + if table.collection.row_key or table.collection.row_tags: + wiki_text, icons = table.generate_wiki_table() + else: + processed = set() + icon, _ = SCHEME.get_icon( + EXTRACTOR, table.collection.tags, processed, MapConfiguration() + ) + if not icon.main_icon.is_default(): + wiki_text = ( + f"[[Image:Röntgen {icon.main_icon.get_name()}.svg|32px]]\n" + ) + icons.append(icon.main_icon) + elif icon.extra_icons: + wiki_text = ( + f"Röntgen icon set has additional icon for the tag: " + f"[[Image:Röntgen {icon.extra_icons[0].get_name()}.svg|32px]]." + f"\n" + ) + icons.append(icon.extra_icons[0]) + else: + wiki_text = "" + + lines: list[str] = old_text.split("\n") + + # If rendering section already exists. + + start: Optional[int] = None + end: int = -1 + + for index, line in enumerate(lines): + if HEADER_2_PATTERN.match(line): + if start is not None: + end = index + break + if RENDERING_HEADER_PATTERN.match(line): + start = index + + if start is not None: + return ( + "\n".join(lines[: start + 2]) + + "\n=== [[Röntgen]] icons in [[Map Machine]] ===\n" + + f"\n{wiki_text}\n" + + "\n".join(lines[end:]) + ), icons + + # If Röntgen rendering section already exists. + + start: Optional[int] = None + end: int = -1 + + for index, line in enumerate(lines): + if HEADER_PATTERN.match(line): + if start is not None: + end = index + break + if ROENTGEN_HEADER_PATTERN.match(line): + start = index + + if start is not None: + return ( + "\n".join(lines[: start + 2]) + + f"\n{wiki_text}\n" + + "\n".join(lines[end:]) + ), icons + + # Otherwise. + + headers: list[Optional[int]] = [None, None] + + for index, line in enumerate(lines): + for i, pattern in enumerate(HEADER_PATTERNS): + if pattern.match(line): + headers[i] = index + + filtered = list(filter(lambda x: x is not None, headers)) + header: int + + if filtered: + header = filtered[0] + else: + lines += [""] + header = len(lines) + + return ( + "\n".join(lines[:header]) + + "\n== Rendering ==\n\n=== [[Röntgen]] icons in [[Map Machine]] " + "===\n\n" + wiki_text + "\n" + "\n".join(lines[header:]) + ), icons diff --git a/map_machine/drawing.py b/map_machine/drawing.py index 12202e6..f412345 100644 --- a/map_machine/drawing.py +++ b/map_machine/drawing.py @@ -20,16 +20,16 @@ __email__ = "me@enzet.ru" PathCommands = List[Union[float, str, np.ndarray]] +DEFAULT_FONT: str = "Helvetica" + @dataclass class Style: - """ - Drawing element style. - """ + """Drawing element style.""" fill: Optional[Color] = None stroke: Optional[Color] = None - width: float = 1 + width: float = 1.0 def update_svg_element(self, element: BaseElement) -> None: """Set style for SVG element.""" @@ -43,7 +43,7 @@ class Style: def draw_png_fill(self, context: Context) -> None: """Set style for context and draw fill.""" context.set_source_rgba( - self.fill.get_red(), self.fill.get_green(), self.fill.get_blue(), 1 + self.fill.get_red(), self.fill.get_green(), self.fill.get_blue() ) context.fill() @@ -53,16 +53,13 @@ class Style: self.stroke.get_red(), self.stroke.get_green(), self.stroke.get_blue(), - 1, ) context.set_line_width(self.width) context.stroke() class Drawing: - """ - Image. - """ + """Image.""" def __init__(self, file_path: Path, width: int, height: int) -> None: self.file_path: Path = file_path @@ -95,9 +92,7 @@ class Drawing: class SVGDrawing(Drawing): - """ - SVG image. - """ + """SVG image.""" def __init__(self, file_path: Path, width: int, height: int) -> None: super().__init__(file_path, width, height) @@ -145,9 +140,7 @@ class SVGDrawing(Drawing): class PNGDrawing(Drawing): - """ - PNG image. - """ + """PNG image.""" def __init__(self, file_path: Path, width: int, height: int) -> None: super().__init__(file_path, width, height) @@ -184,7 +177,7 @@ class PNGDrawing(Drawing): def _do_path(self, commands: PathCommands) -> None: """Draw path.""" - current: np.ndarray = np.array((0, 0)) + current: np.ndarray = np.array((0.0, 0.0)) start_point: Optional[np.ndarray] = None command: str = "M" is_absolute: bool = True @@ -239,14 +232,14 @@ class PNGDrawing(Drawing): point: np.ndarray if is_absolute: if command == "v": - point = np.array((0, commands[index])) + point = np.array((0.0, commands[index])) else: - point = np.array((commands[index], 0)) + point = np.array((commands[index], 0.0)) else: if command == "v": - point = current + np.array((0, commands[index])) + point = current + np.array((0.0, commands[index])) else: - point = current + np.array((commands[index], 0)) + point = current + np.array((commands[index], 0.0)) current = point self.context.line_to(point[0], point[1]) if start_point is None: @@ -304,3 +297,30 @@ def parse_path(path: str) -> PathCommands: index += 1 return result + + +def draw_text( + svg: svgwrite.Drawing, + text: str, + point: np.ndarray, + size: float, + fill: Color, + anchor: str = "middle", + stroke_linejoin: str = "round", + stroke_width: float = 1.0, + stroke: Optional[Color] = None, + opacity: float = 1.0, +): + text_element = svg.text( + text, + point, + font_size=size, + text_anchor=anchor, + font_family=DEFAULT_FONT, + fill=fill.hex, + stroke_linejoin=stroke_linejoin, + stroke_width=stroke_width, + stroke=stroke.hex if stroke else "none", + opacity=opacity, + ) + svg.add(text_element) diff --git a/map_machine/element.py b/map_machine/element.py index 8dbf97f..e0916dc 100644 --- a/map_machine/element.py +++ b/map_machine/element.py @@ -10,10 +10,11 @@ import numpy as np import svgwrite from svgwrite.path import Path as SVGPath -from map_machine.icon import ShapeExtractor -from map_machine.point import Point +from map_machine.map_configuration import LabelMode +from map_machine.pictogram.icon import ShapeExtractor +from map_machine.pictogram.point import Point from map_machine.scheme import LineStyle, Scheme -from map_machine.text import Label +from map_machine.text import Label, TextConstructor from map_machine.workspace import workspace __author__ = "Sergey Vartanov" @@ -34,29 +35,33 @@ def draw_element(options: argparse.Namespace) -> None: target = "area" tags_description = options.area - tags: Dict[str, str] = dict( - [x.split("=") for x in tags_description.split(",")] - ) - scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) + tags: Dict[str, str] = { + tag.split("=")[0]: tag.split("=")[1] + for tag in tags_description.split(",") + } + scheme: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) extractor: ShapeExtractor = ShapeExtractor( workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) processed: Set[str] = set() - icon, priority = scheme.get_icon(extractor, tags, processed) + icon, _ = scheme.get_icon(extractor, tags, processed) is_for_node: bool = target == "node" - labels: List[Label] = scheme.construct_text(tags, "all", processed) + text_constructor: TextConstructor = TextConstructor(scheme) + labels: List[Label] = text_constructor.construct_text( + tags, processed, LabelMode.ALL + ) point: Point = Point( icon, labels, tags, processed, - np.array((32, 32)), + np.array((32.0, 32.0)), is_for_node=is_for_node, draw_outline=is_for_node, ) - border: np.ndarray = np.array((16, 16)) + border: np.ndarray = np.array((16.0, 16.0)) size: np.ndarray = point.get_size() + border - point.point = np.array((size[0] / 2, 16 / 2 + border[1] / 2)) + point.point = np.array((size[0] / 2.0, 16.0 / 2.0 + border[1] / 2.0)) output_file_path: Path = workspace.output_path / "element.svg" svg: svgwrite.Drawing = svgwrite.Drawing( diff --git a/map_machine/feature/__init__.py b/map_machine/feature/__init__.py new file mode 100644 index 0000000..a34c92e --- /dev/null +++ b/map_machine/feature/__init__.py @@ -0,0 +1,3 @@ +""" +Specific map features: roads, directions, etc. +""" diff --git a/map_machine/feature/building.py b/map_machine/feature/building.py new file mode 100644 index 0000000..d2eb3ce --- /dev/null +++ b/map_machine/feature/building.py @@ -0,0 +1,215 @@ +""" +Buildings on the map. +""" +import numpy as np +from colour import Color +from svgwrite import Drawing +from svgwrite.container import Group +from svgwrite.path import Path +from typing import Dict, List + +from map_machine.drawing import PathCommands +from map_machine.figure import Figure +from map_machine.geometry.flinger import Flinger +from map_machine.geometry.vector import Segment +from map_machine.osm.osm_reader import OSMNode +from map_machine.scheme import Scheme + +BUILDING_MINIMAL_HEIGHT: float = 8.0 +BUILDING_SCALE: float = 0.33 +LEVEL_HEIGHT: float = 2.5 +SHADE_SCALE: float = 0.4 + + +class Building(Figure): + """Building on the map.""" + + def __init__( + self, + tags: Dict[str, str], + inners: List[List[OSMNode]], + outers: List[List[OSMNode]], + flinger: Flinger, + scheme: Scheme, + ) -> None: + super().__init__(tags, inners, outers) + + self.is_construction: bool = ( + tags.get("building") == "construction" + or tags.get("construction") == "yes" + ) + self.has_walls: bool = tags.get("building") != "roof" + + if self.is_construction: + self.fill: Color = scheme.get_color("building_construction_color") + self.stroke: Color = scheme.get_color( + "building_construction_border_color" + ) + else: + if color := tags.get("roof:colour"): + self.fill = scheme.get_color(color) + self.stroke: Color = Color(self.fill) + self.stroke.set_luminance(self.fill.get_luminance() * 0.85) + else: + self.fill: Color = scheme.get_color("building_color") + self.stroke: Color = scheme.get_color("building_border_color") + + self.parts: List[Segment] = [] + + for nodes in self.inners + self.outers: + for i in range(len(nodes) - 1): + flung_1: np.ndarray = flinger.fling(nodes[i].coordinates) + flung_2: np.ndarray = flinger.fling(nodes[i + 1].coordinates) + self.parts.append(Segment(flung_1, flung_2)) + + self.parts = sorted(self.parts) + + self.height: float = BUILDING_MINIMAL_HEIGHT + self.min_height: float = 0.0 + + self.wall_color: Color + if self.is_construction: + self.wall_color = scheme.get_color("wall_construction_color") + else: + self.wall_color = scheme.get_color("wall_color") + + if material := tags.get("building:material"): + if material in scheme.material_colors: + self.wall_color = Color(scheme.material_colors[material]) + + if color := tags.get("building:colour"): + self.wall_color = scheme.get_color(color) + + if color := tags.get("colour"): + self.wall_color = scheme.get_color(color) + + self.wall_bottom_color_1: Color = Color(self.wall_color) + self.wall_bottom_color_1.set_luminance( + self.wall_color.get_luminance() * 0.70 + ) + self.wall_bottom_color_2: Color = Color(self.wall_color) + self.wall_bottom_color_2.set_luminance( + self.wall_color.get_luminance() * 0.85 + ) + + if levels := self.get_float("building:levels"): + self.height = BUILDING_MINIMAL_HEIGHT + levels * LEVEL_HEIGHT + + if levels := self.get_float("building:min_level"): + self.min_height = BUILDING_MINIMAL_HEIGHT + levels * LEVEL_HEIGHT + + if height := self.get_length("height"): + self.height = BUILDING_MINIMAL_HEIGHT + height + + if height := self.get_length("min_height"): + self.min_height = BUILDING_MINIMAL_HEIGHT + height + + def draw(self, svg: Drawing, flinger: Flinger) -> None: + """Draw simple building shape.""" + path: Path = Path( + d=self.get_path(flinger), + stroke=self.stroke.hex, + fill=self.fill.hex, + stroke_linejoin="round", + ) + svg.add(path) + + def draw_shade(self, building_shade: Group, flinger: Flinger) -> None: + """Draw shade casted by the building.""" + scale: float = flinger.get_scale() * SHADE_SCALE + shift_1: np.ndarray = np.array((scale * self.min_height, 0.0)) + shift_2: np.ndarray = np.array((scale * self.height, 0.0)) + commands: str = self.get_path(flinger, shift_1) + path: Path = Path( + commands, fill="#000000", stroke="#000000", stroke_width=1.0 + ) + building_shade.add(path) + for nodes in self.inners + self.outers: + for i in range(len(nodes) - 1): + flung_1 = flinger.fling(nodes[i].coordinates) + flung_2 = flinger.fling(nodes[i + 1].coordinates) + command: PathCommands = [ + "M", + np.add(flung_1, shift_1), + "L", + np.add(flung_2, shift_1), + np.add(flung_2, shift_2), + np.add(flung_1, shift_2), + "Z", + ] + path: Path = Path( + command, fill="#000000", stroke="#000000", stroke_width=1.0 + ) + building_shade.add(path) + + def draw_walls( + self, svg: Drawing, height: float, previous_height: float, scale: float + ) -> None: + """Draw building walls.""" + if not self.has_walls: + return + + shift_1: np.ndarray = np.array( + (0.0, -previous_height * scale * BUILDING_SCALE) + ) + shift_2: np.ndarray = np.array((0.0, -height * scale * BUILDING_SCALE)) + + for segment in self.parts: + draw_walls(svg, self, segment, height, shift_1, shift_2) + + def draw_roof(self, svg: Drawing, flinger: Flinger, scale: float) -> None: + """Draw building roof.""" + path: Path = Path( + d=self.get_path( + flinger, np.array([0.0, -self.height * scale * BUILDING_SCALE]) + ), + stroke=self.stroke, + fill="none" if self.is_construction else self.fill.hex, + stroke_linejoin="round", + ) + svg.add(path) + + +def draw_walls(svg, building: Building, segment, height, shift_1, shift_2): + fill: str + if building.is_construction: + color_part: float = segment.angle * 0.2 + fill = Color( + rgb=( + building.wall_color.get_red() + color_part, + building.wall_color.get_green() + color_part, + building.wall_color.get_blue() + color_part, + ) + ).hex + elif height <= 0.25 / BUILDING_SCALE: + fill = building.wall_bottom_color_1.hex + elif height <= 0.5 / BUILDING_SCALE: + fill = building.wall_bottom_color_2.hex + else: + color_part: float = segment.angle * 0.2 - 0.1 + fill = Color( + rgb=( + max(min(building.wall_color.get_red() + color_part, 1), 0), + max(min(building.wall_color.get_green() + color_part, 1), 0), + max(min(building.wall_color.get_blue() + color_part, 1), 0), + ) + ).hex + + command = ( + "M", + segment.point_1 + shift_1, + "L", + segment.point_2 + shift_1, + segment.point_2 + shift_2, + segment.point_1 + shift_2, + segment.point_1 + shift_1, + "Z", + ) + path: Path = Path( + d=command, + fill=fill, + stroke=fill, + stroke_width=1, + stroke_linejoin="round", + ) + svg.add(path) diff --git a/map_machine/feature/crater.py b/map_machine/feature/crater.py new file mode 100644 index 0000000..ff3006c --- /dev/null +++ b/map_machine/feature/crater.py @@ -0,0 +1,47 @@ +""" +Crater on the map. +""" +import numpy as np +from colour import Color +from svgwrite import Drawing +from typing import Dict + +from map_machine.geometry.flinger import Flinger +from map_machine.osm.osm_reader import Tagged + + +class Crater(Tagged): + """Volcano or impact crater on the map.""" + + def __init__( + self, tags: Dict[str, str], coordinates: np.ndarray, point: np.ndarray + ) -> None: + super().__init__(tags) + self.coordinates: np.ndarray = coordinates + self.point: np.ndarray = point + + def draw(self, svg: Drawing, flinger: Flinger) -> None: + """Draw crater ridge.""" + scale: float = flinger.get_scale(self.coordinates) + assert "diameter" in self.tags + radius: float = float(self.tags["diameter"]) / 2.0 + radial_gradient = svg.radialGradient( + center=self.point + np.array((0.0, radius * scale / 7.0)), + r=radius * scale, + gradientUnits="userSpaceOnUse", + ) + color: Color = Color("#000000") + gradient = svg.defs.add(radial_gradient) + ( + gradient + .add_stop_color(0.0, color.hex, opacity=0.2) + .add_stop_color(0.7, color.hex, opacity=0.2) + .add_stop_color(1.0, color.hex, opacity=1.0) + ) # fmt: skip + circle = svg.circle( + self.point, + radius * scale, + fill=gradient.get_funciri(), + opacity=0.2, + ) + svg.add(circle) diff --git a/map_machine/direction.py b/map_machine/feature/direction.py similarity index 54% rename from map_machine/direction.py rename to map_machine/feature/direction.py index db73e35..7e7d40f 100644 --- a/map_machine/direction.py +++ b/map_machine/feature/direction.py @@ -1,19 +1,24 @@ """ Direction tag support. """ -from typing import Iterator, List, Optional +from typing import Iterator, List, Optional, Dict import numpy as np +from colour import Color from portolan import middle +from svgwrite import Drawing +from svgwrite.gradients import RadialGradient +from svgwrite.path import Path from map_machine.drawing import PathCommands +from map_machine.osm.osm_reader import Tagged __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -SHIFT: float = -np.pi / 2 -SMALLEST_ANGLE: float = np.pi / 15 -DEFAULT_ANGLE: float = np.pi / 30 +SHIFT: float = -np.pi / 2.0 +SMALLEST_ANGLE: float = np.pi / 15.0 +DEFAULT_ANGLE: float = np.pi / 30.0 def parse_vector(text: str) -> Optional[np.ndarray]: @@ -51,9 +56,7 @@ def rotation_matrix(angle: float) -> np.ndarray: class Sector: - """ - Sector described by two vectors. - """ + """Sector described by two vectors.""" def __init__(self, text: str, angle: Optional[float] = None) -> None: """ @@ -64,17 +67,17 @@ class Sector: self.end: Optional[np.ndarray] = None self.main_direction: Optional[np.ndarray] = None - if "-" in text: + if "-" in text and not text.startswith("-"): parts: List[str] = text.split("-") self.start = parse_vector(parts[0]) self.end = parse_vector(parts[1]) - self.main_direction = (self.start + self.end) / 2 + self.main_direction = (self.start + self.end) / 2.0 else: result_angle: float if angle is None: result_angle = DEFAULT_ANGLE else: - result_angle = max(SMALLEST_ANGLE, np.radians(angle) / 2) + result_angle = max(SMALLEST_ANGLE, np.radians(angle) / 2.0) vector: Optional[np.ndarray] = parse_vector(text) self.main_direction = vector @@ -107,21 +110,20 @@ class Sector: None otherwise. """ if self.main_direction is not None: - if np.allclose(self.main_direction[0], 0): + if np.allclose(self.main_direction[0], 0.0): return None - elif self.main_direction[0] > 0: + if self.main_direction[0] > 0.0: return True - else: - return False + return False + + return None def __str__(self) -> str: return f"{self.start}-{self.end}" class DirectionSet: - """ - Describes direction, set of directions. - """ + """Describes direction, set of directions.""" def __init__(self, text: str) -> None: """ @@ -152,9 +154,81 @@ class DirectionSet: :return: true if direction is right, false if direction is left, and None otherwise. """ - result: List[bool] = [x.is_right() for x in self.sectors] + result: List[bool] = [sector.is_right() for sector in self.sectors] if result == [True] * len(result): return True if result == [False] * len(result): return False return None + + +class DirectionSector(Tagged): + """Sector that represents direction.""" + + def __init__(self, tags: Dict[str, str], point: np.ndarray) -> None: + super().__init__(tags) + self.point: np.ndarray = point + + def draw(self, svg: Drawing, scheme) -> None: + """Draw gradient sector.""" + angle: Optional[float] = None + is_revert_gradient: bool = False + direction: str + direction_radius: float + direction_color: Color + + if self.get_tag("man_made") == "surveillance": + direction = self.get_tag("camera:direction") + if "camera:angle" in self.tags: + angle = float(self.get_tag("camera:angle")) + if "angle" in self.tags: + angle = float(self.get_tag("angle")) + direction_radius = 50.0 + direction_color = scheme.get_color("direction_camera_color") + elif self.get_tag("traffic_sign") == "stop": + direction = self.get_tag("direction") + direction_radius = 25.0 + direction_color = Color("red") + else: + direction = self.get_tag("direction") + direction_radius = 50.0 + direction_color = scheme.get_color("direction_view_color") + is_revert_gradient = True + + if not direction: + return + + point: np.ndarray = (self.point.astype(int)).astype(float) + + paths: Iterator[PathCommands] + if angle is not None: + paths = [Sector(direction, angle).draw(point, direction_radius)] + else: + paths = DirectionSet(direction).draw(point, direction_radius) + + for path in paths: + radial_gradient: RadialGradient = svg.radialGradient( + center=point, + r=direction_radius, + gradientUnits="userSpaceOnUse", + ) + gradient: RadialGradient = svg.defs.add(radial_gradient) + + if is_revert_gradient: + ( + gradient + .add_stop_color(0.0, direction_color.hex, opacity=0.0) + .add_stop_color(1.0, direction_color.hex, opacity=0.7) + ) # fmt: skip + else: + ( + gradient + .add_stop_color(0.0, direction_color.hex, opacity=0.4) + .add_stop_color(1.0, direction_color.hex, opacity=0.0) + ) # fmt: skip + + path_element: Path = svg.path( + d=["M", point] + path + ["L", point, "Z"], + fill=gradient.get_funciri(), + ) + svg.add(path_element) diff --git a/map_machine/road.py b/map_machine/feature/road.py similarity index 59% rename from map_machine/road.py rename to map_machine/feature/road.py index 522c8db..cd8236f 100644 --- a/map_machine/road.py +++ b/map_machine/feature/road.py @@ -1,38 +1,41 @@ """ WIP: road shape drawing. """ +import logging +from collections import defaultdict from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union import numpy as np import svgwrite from colour import Color from svgwrite import Drawing +from svgwrite.filters import Filter from svgwrite.path import Path from svgwrite.shapes import Circle from map_machine.drawing import PathCommands -from map_machine.flinger import Flinger -from map_machine.osm_reader import OSMNode, Tagged -from map_machine.scheme import RoadMatcher - -from map_machine.vector import ( +from map_machine.geometry.flinger import Flinger +from map_machine.geometry.vector import ( Line, Polyline, compute_angle, norm, turn_by_angle, ) +from map_machine.osm.osm_reader import OSMNode, Tagged +from map_machine.scheme import RoadMatcher, Scheme __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" +DEFAULT_LANE_WIDTH: float = 3.7 +USE_BLUR: bool = False + @dataclass class Lane: - """ - Road lane specification. - """ + """Road lane specification.""" width: Optional[float] = None # Width in meters is_forward: Optional[bool] = None # Whether lane is forward or backward @@ -54,9 +57,7 @@ class Lane: class RoadPart: - """ - Line part of the road. - """ + """Line part of the road.""" def __init__( self, @@ -73,15 +74,18 @@ class RoadPart: self.point_1: np.ndarray = point_1 self.point_2: np.ndarray = point_2 self.lanes: List[Lane] = lanes + + self.width: float if lanes: self.width = sum(map(lambda x: x.get_width(scale), lanes)) else: - self.width = 1 - self.left_offset: float = self.width / 2 - self.right_offset: float = self.width / 2 + self.width = 1.0 + + self.left_offset: float = self.width / 2.0 + self.right_offset: float = self.width / 2.0 self.turned: np.ndarray = norm( - turn_by_angle(self.point_2 - self.point_1, np.pi / 2) + turn_by_angle(self.point_2 - self.point_1, np.pi / 2.0) ) self.right_vector: np.ndarray = self.turned * self.right_offset self.left_vector: np.ndarray = -self.turned * self.left_offset @@ -120,7 +124,7 @@ class RoadPart: self.left_outer = self.left_connection self.point_middle = self.right_outer - self.right_vector - max_: float = 100 + max_: float = 100.0 if np.linalg.norm(self.point_middle - self.point_1) > max_: self.point_a = self.point_1 + max_ * norm( @@ -287,9 +291,8 @@ class Intersection: def __init__(self, parts: List[RoadPart]) -> None: self.parts: List[RoadPart] = sorted(parts, key=lambda x: x.get_angle()) - for index in range(len(self.parts)): + for index, part_1 in enumerate(self.parts): next_index: int = 0 if index == len(self.parts) - 1 else index + 1 - part_1: RoadPart = self.parts[index] part_2: RoadPart = self.parts[next_index] line_1: Line = Line( part_1.point_1 + part_1.right_vector, @@ -306,9 +309,8 @@ class Intersection: part_1.update() part_2.update() - for index in range(len(self.parts)): + for index, part_1 in enumerate(self.parts): next_index: int = 0 if index == len(self.parts) - 1 else index + 1 - part_1: RoadPart = self.parts[index] part_2: RoadPart = self.parts[next_index] part_1.update() part_2.update() @@ -362,9 +364,7 @@ class Intersection: class Road(Tagged): - """ - Road or track on the map. - """ + """Road or track on the map.""" def __init__( self, @@ -372,39 +372,47 @@ class Road(Tagged): nodes: List[OSMNode], matcher: RoadMatcher, flinger: Flinger, + scheme: Scheme, ) -> None: super().__init__(tags) self.nodes: List[OSMNode] = nodes self.matcher: RoadMatcher = matcher self.line: Polyline = Polyline( - [flinger.fling(x.coordinates) for x in self.nodes] + [flinger.fling(node.coordinates) for node in self.nodes] ) self.width: Optional[float] = matcher.default_width self.lanes: List[Lane] = [] + self.scale: float = flinger.get_scale(self.nodes[0].coordinates) + + self.is_area = scheme.is_area(tags) and nodes[0] == nodes[-1] + if "lanes" in tags: try: - self.width = int(tags["lanes"]) * 3.7 + self.width = int(tags["lanes"]) * DEFAULT_LANE_WIDTH self.lanes = [Lane()] * int(tags["lanes"]) except ValueError: pass if "width:lanes" in tags: - widths: List[float] = list( - map(float, tags["width:lanes"].split("|")) - ) - if len(widths) == len(self.lanes): - for index, lane in enumerate(self.lanes): - lane.width = widths[index] + try: + widths: List[float] = list( + map(float, tags["width:lanes"].split("|")) + ) + if len(widths) == len(self.lanes): + for index, lane in enumerate(self.lanes): + lane.width = widths[index] + except ValueError: + pass number: int if "lanes:forward" in tags: number = int(tags["lanes:forward"]) - [x.set_forward(True) for x in self.lanes[-number:]] + map(lambda x: x.set_forward(True), self.lanes[-number:]) if "lanes:backward" in tags: number = int(tags["lanes:backward"]) - [x.set_forward(False) for x in self.lanes[:number]] + map(lambda x: x.set_forward(False), self.lanes[:number]) if "width" in tags: try: @@ -412,106 +420,235 @@ class Road(Tagged): except ValueError: pass - self.layer: float = 0 + self.layer: float = 0.0 if "layer" in tags: self.layer = float(tags["layer"]) - def draw( - self, - svg: Drawing, - flinger: Flinger, - color: Color, - extra_width: float = 0, - ) -> None: - """Draw road as simple SVG path.""" + self.placement_offset: float = 0.0 + self.is_transition: bool = False + + if "placement" in tags: + value: str = tags["placement"] + if value == "transition": + self.is_transition = True + elif ":" in value and len(parts := value.split(":")) == 2: + place, lane_string = parts + lane_number: int = int(lane_string) - 1 + self.placement_offset = -self.width * self.scale / 2.0 + if lane_number > 0: + self.placement_offset += sum( + lane.get_width(self.scale) + for lane in self.lanes[:lane_number] + ) + elif lane_number < 0: + self.placement_offset += ( + DEFAULT_LANE_WIDTH * lane_number * self.scale + ) + + if place == "left_of": + pass + elif place == "middle_of": + self.placement_offset += ( + self.lanes[lane_number].get_width(self.scale) * 0.5 + ) + elif place == "right_of": + self.placement_offset += self.lanes[lane_number].get_width( + self.scale + ) + else: + logging.error(f"Unknown placement `{place}`.") + + def get_style( + self, is_border: bool, is_for_stroke: bool = False + ) -> Dict[str, Union[int, float, str]]: + """Get road SVG style.""" width: float if self.width is not None: width = self.width else: width = self.matcher.default_width - if extra_width and self.tags.get("bridge") == "yes": - color = Color("#666666") - if extra_width and self.tags.get("ford") == "yes": - color = Color("#88BBFF") - width += 2 - if extra_width and self.tags.get("embankment") == "yes": - color = Color("#666666") - width += 4 - scale: float = flinger.get_scale(self.nodes[0].coordinates) - path_commands: str = self.line.get_path() - path: Path = Path(d=path_commands) - style: Dict[str, Any] = { - "fill": "none", + + border_width: float + if is_border: + color = self.get_border_color() + border_width = 2.0 + else: + color = self.get_color() + border_width = 0.0 + + extra_width: float = 0.0 + if is_border: + if self.tags.get("bridge") == "yes": + extra_width = 0.5 + if self.tags.get("ford") == "yes": + extra_width = 2.0 + if self.tags.get("embankment") == "yes": + extra_width = 4.0 + + fill: str = "none" + if self.is_area: + fill = color.hex + + style: Dict[str, Union[int, float, str]] = { + "fill": fill, "stroke": color.hex, "stroke-linecap": "butt", "stroke-linejoin": "round", - "stroke-width": scale * width + extra_width, + "stroke-width": self.scale * width + extra_width + border_width, } - if extra_width and self.tags.get("embankment") == "yes": + if is_for_stroke: + style["stroke-width"] = 2.0 + extra_width + if is_border and self.tags.get("embankment") == "yes": style["stroke-dasharray"] = "1,3" - if extra_width and self.tags.get("tunnel") == "yes": - style["stroke-dasharray"] = "3,3" + if self.tags.get("tunnel") == "yes": + if is_border: + style["stroke-dasharray"] = "3,3" + + return style + + def get_filter(self, svg: Drawing, is_border: bool) -> Optional[Filter]: + """Get blurring filter.""" + if not USE_BLUR: + return None + + if is_border and self.tags.get("bridge") == "yes": + filter_ = svg.defs.add(svg.filter()) + filter_.feGaussianBlur(in_="SourceGraphic", stdDeviation=2) + return filter_ + + return None + + def draw(self, svg: Drawing, is_border: bool) -> None: + """Draw road as simple SVG path.""" + filter_: Filter = self.get_filter(svg, is_border) + + style: dict[str, Union[int, float, str]] = self.get_style(is_border) + path_commands: str = self.line.get_path(self.placement_offset) + path: Path + if filter_: + path = Path(d=path_commands, filter=filter_.get_funciri()) + else: + path = Path(d=path_commands) + path.update(style) svg.add(path) - def draw_lanes(self, svg: Drawing, flinger: Flinger, color: Color) -> None: + def get_color(self) -> Color: + """Get road main color.""" + color: Color = self.matcher.color + if self.tags.get("tunnel") == "yes": + color = Color(color, luminance=min(1.0, color.luminance + 0.2)) + return color + + def get_border_color(self) -> Color: + """Get road border color.""" + color: Color = self.matcher.border_color + if self.tags.get("bridge") == "yes": + color = Color("#666666") + if self.tags.get("ford") == "yes": + color = Color("#88BBFF") + if self.tags.get("embankment") == "yes": + color = Color("#666666") + return color + + def draw_lanes(self, svg: Drawing, color: Color) -> None: """Draw lane separators.""" - scale: float = flinger.get_scale(self.nodes[0].coordinates) if len(self.lanes) < 2: return + for index in range(1, len(self.lanes)): - parallel_offset: float = scale * ( - -self.width / 2 + index * self.width / len(self.lanes) + lane_offset: float = self.scale * ( + -self.width / 2.0 + index * self.width / len(self.lanes) + ) + path: Path = Path( + d=self.line.get_path(self.placement_offset + lane_offset) ) - path: Path = Path(d=self.line.get_path(parallel_offset)) style: Dict[str, Any] = { "fill": "none", "stroke": color.hex, "stroke-linejoin": "round", - "stroke-width": 1, + "stroke-width": 1.0, "opacity": 0.5, } path.update(style) svg.add(path) + def draw_caption(self, svg: Drawing) -> None: + """Draw road name along its path.""" + name: Optional[str] = self.tags.get("name") + if not name: + return + + path: Path = svg.path( + d=self.line.get_path(self.placement_offset + 3.0), fill="none" + ) + svg.add(path) + + text = svg.add(svg.text.Text("")) + text_path = svg.text.TextPath( + path=path, + text=name, + startOffset=None, + method="align", + spacing="exact", + font_family="Roboto", + font_size=10.0, + ) + text.add(text_path) + def get_curve_points( - road: Road, scale: float, center: np.ndarray, road_end: np.ndarray + road: Road, + center: np.ndarray, + road_end: np.ndarray, + placement_offset: float, + is_end: bool, ) -> List[np.ndarray]: """ :param road: road segment - :param scale: current zoom scale :param center: road intersection point :param road_end: end point of the road segment + :param placement_offset: offset based on placement tag value + :param is_end: whether the point represents road end """ - width: float = road.width / 2.0 * scale + width: float = road.width / 2.0 * road.scale - direction: np.ndarray = (road_end - center) / np.linalg.norm( - road_end - center + direction: np.ndarray = (center - road_end) / np.linalg.norm( + center - road_end + ) + if is_end: + direction = -direction + left: np.ndarray = turn_by_angle(direction, np.pi / 2.0) * ( + width + placement_offset + ) + right: np.ndarray = turn_by_angle(direction, -np.pi / 2.0) * ( + width - placement_offset ) - left: np.ndarray = turn_by_angle(direction, np.pi / 2.0) * width - right: np.ndarray = turn_by_angle(direction, -np.pi / 2.0) * width return [road_end + left, center + left, center + right, road_end + right] class Connector: - """ - Two roads connection. - """ + """Two roads connection.""" def __init__( self, connections: List[Tuple[Road, int]], flinger: Flinger, - scale: float, ) -> None: self.connections: List[Tuple[Road, int]] = connections - self.road_1: Road = connections[0][0] - self.index_1: int = connections[0][1] + self.road_1: Road + self.index_1: int + self.road_1, self.index_1 = connections[0] + self.priority = self.road_1.matcher.priority - self.layer: float = min(x[0].layer for x in connections) - self.scale: float = scale + self.min_layer: float = min( + connection[0].layer for connection in connections + ) + self.max_layer: float = max( + connection[0].layer for connection in connections + ) + self.scale: float = self.road_1.scale self.flinger: Flinger = flinger def draw(self, svg: Drawing) -> None: @@ -524,17 +661,14 @@ class Connector: class SimpleConnector(Connector): - """ - Simple connection between roads that don't change width. - """ + """Simple connection between roads that don't change width.""" def __init__( self, connections: List[Tuple[Road, int]], flinger: Flinger, - scale: float, ) -> None: - super().__init__(connections, flinger, scale) + super().__init__(connections, flinger) self.road_2: Road = connections[1][0] self.index_2: int = connections[1][1] @@ -546,8 +680,8 @@ class SimpleConnector(Connector): """Draw connection fill.""" circle: Circle = svg.circle( self.point, - self.road_1.width * self.scale / 2, - fill=self.road_1.matcher.color.hex, + self.road_1.width * self.scale / 2.0, + fill=self.road_1.get_color().hex, ) svg.add(circle) @@ -555,185 +689,213 @@ class SimpleConnector(Connector): """Draw connection outline.""" circle: Circle = svg.circle( self.point, - self.road_1.width * self.scale / 2 + 1, + self.road_1.width * self.scale / 2.0 + 1.0, fill=self.road_1.matcher.border_color.hex, ) svg.add(circle) class ComplexConnector(Connector): - """ - Connection between roads that change width. - """ + """Connection between two roads that change width.""" def __init__( self, connections: List[Tuple[Road, int]], flinger: Flinger, - scale: float, ) -> None: - super().__init__(connections, flinger, scale) + super().__init__(connections, flinger) self.road_2: Road = connections[1][0] self.index_2: int = connections[1][1] - length: float = abs(self.road_2.width - self.road_1.width) * scale + length: float = ( + abs(self.road_2.width - self.road_1.width) * self.road_1.scale + ) self.road_1.line.shorten(self.index_1, length) self.road_2.line.shorten(self.index_2, length) - node: OSMNode = self.road_1.nodes[self.index_1] - point: np.ndarray = flinger.fling(node.coordinates) + node_1: OSMNode = self.road_1.nodes[self.index_1] + point_1: np.ndarray = flinger.fling(node_1.coordinates) + node_2: OSMNode = self.road_2.nodes[self.index_2] + point_2: np.ndarray = flinger.fling(node_2.coordinates) + point = (point_1 + point_2) / 2.0 points_1: List[np.ndarray] = get_curve_points( - self.road_1, scale, point, self.road_1.line.points[self.index_1] + self.road_1, + point, + self.road_1.line.points[self.index_1], + self.road_1.placement_offset, + self.index_1 != 0, ) points_2: List[np.ndarray] = get_curve_points( - self.road_2, scale, point, self.road_2.line.points[self.index_2] + self.road_2, + point, + self.road_2.line.points[self.index_2], + self.road_2.placement_offset, + self.index_2 != 0, ) # fmt: off self.curve_1: PathCommands = [ - points_1[0], "C", points_1[1], points_2[2], points_2[3] + points_1[0], "C", points_1[1], points_2[1], points_2[0] ] self.curve_2: PathCommands = [ - points_2[0], "C", points_2[1], points_1[2], points_1[3] + points_2[3], "C", points_2[2], points_1[2], points_1[3] ] # fmt: on def draw(self, svg: Drawing) -> None: """Draw connection fill.""" - for road, index in [ - (self.road_1, self.index_1), - (self.road_2, self.index_2), - ]: - circle: Circle = svg.circle( - road.line.points[index], - road.width * self.scale / 2, - fill=road.matcher.color.hex, - ) - svg.add(circle) - path: Path = svg.path( d=["M"] + self.curve_1 + ["L"] + self.curve_2 + ["Z"], - fill=self.road_1.matcher.color.hex, + fill=self.road_1.get_color(), ) svg.add(path) def draw_border(self, svg: Drawing) -> None: """Draw connection outline.""" - path: Path = svg.path( - d=["M"] + self.curve_1 + ["L"] + self.curve_2 + ["Z"], - fill="none", - stroke=self.road_1.matcher.border_color.hex, - stroke_width=2, - ) + filter_: Filter = self.road_1.get_filter(svg, True) + + if filter_: + path: Path = svg.path( + d=["M"] + self.curve_1 + ["M"] + self.curve_2, + filter=filter_.get_funciri(), + ) + else: + path: Path = svg.path(d=["M"] + self.curve_1 + ["M"] + self.curve_2) + path.update(self.road_1.get_style(True, True)) svg.add(path) class SimpleIntersection(Connector): - """ - Connection between more than two roads. - """ - - def __init__( - self, - connections: List[Tuple[Road, int]], - flinger: Flinger, - scale: float, - ) -> None: - super().__init__(connections, flinger, scale) + """Connection between more than two roads.""" def draw(self, svg: Drawing) -> None: """Draw connection fill.""" - for road, index in self.connections: + for road, _ in sorted( + self.connections, key=lambda x: x[0].matcher.priority + ): node: OSMNode = self.road_1.nodes[self.index_1] point: np.ndarray = self.flinger.fling(node.coordinates) circle: Circle = svg.circle( - point, road.width * self.scale / 2, fill=road.matcher.color.hex + point, + road.width * self.scale / 2.0, + fill=road.matcher.color.hex, ) svg.add(circle) def draw_border(self, svg: Drawing) -> None: """Draw connection outline.""" - for road, index in self.connections: + for road, _ in self.connections: node: OSMNode = self.road_1.nodes[self.index_1] point: np.ndarray = self.flinger.fling(node.coordinates) circle: Circle = svg.circle( point, - road.width * self.scale / 2 + 1, + road.width * self.scale / 2.0 + 1.0, fill=road.matcher.border_color.hex, ) svg.add(circle) class Roads: - """ - Whole road structure. - """ + """Whole road structure.""" def __init__(self) -> None: self.roads: List[Road] = [] - self.connections: Dict[int, List[Tuple[Road, int]]] = {} + self.nodes: Dict[int, List[Tuple[Road, int]]] = {} def append(self, road: Road) -> None: """Add road and update connections.""" self.roads.append(road) - for index in road.nodes[0].id_, road.nodes[-1].id_: - if index not in self.connections: - self.connections[index] = [] - self.connections[road.nodes[0].id_].append((road, 0)) - self.connections[road.nodes[-1].id_].append((road, -1)) + for index, node in enumerate(road.nodes): + if node.id_ not in self.nodes: + self.nodes[node.id_] = [] + self.nodes[node.id_].append((road, index)) - def draw(self, svg: Drawing, flinger: Flinger) -> None: + def draw( + self, svg: Drawing, flinger: Flinger, draw_captions: bool = False + ) -> None: """Draw whole road system.""" if not self.roads: return - scale: float = flinger.get_scale(self.roads[0].nodes[0].coordinates) - layered_roads: Dict[float, List[Road]] = {} - layered_connectors: Dict[float, List[Connector]] = {} + layered_roads: Dict[float, List[Road]] = defaultdict(list) + layered_connectors: Dict[float, List[Connector]] = defaultdict(list) for road in self.roads: - if road.layer not in layered_roads: - layered_roads[road.layer] = [] - layered_roads[road.layer].append(road) + if not road.is_transition: + layered_roads[road.layer].append(road) + else: + connections = [] + for end in 0, -1: + connections.append( + [ + connection + for connection in self.nodes[road.nodes[end].id_] + if not connection[0].is_transition + ] + ) + if len(connections[0]) == 1 and len(connections[1]) == 1: + connector: Connector = ComplexConnector( + [connections[0][0], connections[1][0]], flinger + ) + layered_connectors[road.layer].append(connector) - for id_ in self.connections: - connected: List[Tuple[Road, int]] = self.connections[id_] + for connected in self.nodes.values(): connector: Connector - if len(self.connections[id_]) == 2: - road_1, _ = connected[0] - road_2, _ = connected[1] - if road_1.width == road_2.width: - connector = SimpleConnector(connected, flinger, scale) - else: - connector = ComplexConnector(connected, flinger, scale) - else: - connector = SimpleIntersection(connected, flinger, scale) + if len(connected) <= 1: + continue - if connector.layer not in layered_connectors: - layered_connectors[connector.layer] = [] - layered_connectors[connector.layer].append(connector) + if len(connected) == 2: + road_1, index_1 = connected[0] + road_2, index_2 = connected[1] + if ( + road_1.width == road_2.width + or index_1 not in [0, len(road_1.nodes) - 1] + or index_2 not in [0, len(road_2.nodes) - 1] + ): + connector = SimpleConnector(connected, flinger) + elif not road_1.is_transition and not road_2.is_transition: + connector = ComplexConnector(connected, flinger) + else: + continue + else: + # We can also use SimpleIntersection(connected, flinger, scale) + # here. + continue + + layered_connectors[connector.min_layer].append(connector) + layered_connectors[connector.max_layer].append(connector) for layer in sorted(layered_roads.keys()): roads: List[Road] = sorted( layered_roads[layer], key=lambda x: x.matcher.priority ) - connectors: List[Connector] - if layer in layered_connectors: - connectors = layered_connectors[layer] - else: - connectors = [] + connectors: List[Connector] = layered_connectors.get(layer) + + # Draw borders. for road in roads: - road.draw(svg, flinger, road.matcher.border_color, 2) - for connector in connectors: - connector.draw_border(svg) + road.draw(svg, True) + if connectors: + for connector in connectors: + if connector.min_layer == layer: + connector.draw_border(svg) - for connector in connectors: - connector.draw(svg) - for road in roads: - road.draw(svg, flinger, road.matcher.color) + # Draw inner parts. for road in roads: - road.draw_lanes(svg, flinger, road.matcher.border_color) + road.draw(svg, False) + if connectors: + for connector in connectors: + if connector.max_layer == layer: + connector.draw(svg) + + # Draw lane separators. + + for road in roads: + road.draw_lanes(svg, road.matcher.border_color) + + if draw_captions: + for road in self.roads: + road.draw_caption(svg) diff --git a/map_machine/feature/tree.py b/map_machine/feature/tree.py new file mode 100644 index 0000000..179a62e --- /dev/null +++ b/map_machine/feature/tree.py @@ -0,0 +1,36 @@ +import numpy as np +from colour import Color +from svgwrite import Drawing +from typing import Dict + +from map_machine.geometry.flinger import Flinger +from map_machine.osm.osm_reader import Tagged +from map_machine.scheme import Scheme + + +class Tree(Tagged): + """Tree on the map.""" + + def __init__( + self, tags: Dict[str, str], coordinates: np.ndarray, point: np.ndarray + ) -> None: + super().__init__(tags) + self.coordinates: np.ndarray = coordinates + self.point: np.ndarray = point + + def draw(self, svg: Drawing, flinger: Flinger, scheme: Scheme) -> None: + """Draw crown and trunk.""" + scale: float = flinger.get_scale(self.coordinates) + + radius: float + if diameter_crown := self.get_float("diameter_crown") is not None: + radius = diameter_crown / 2.0 + else: + radius = 2.0 + + color: Color = scheme.get_color("evergreen_color") + svg.add(svg.circle(self.point, radius * scale, fill=color, opacity=0.3)) + + if (circumference := self.get_float("circumference")) is not None: + radius: float = circumference / 2.0 / np.pi + svg.add(svg.circle(self.point, radius * scale, fill="#B89A74")) diff --git a/map_machine/figure.py b/map_machine/figure.py index 67d2c78..80555f9 100644 --- a/map_machine/figure.py +++ b/map_machine/figure.py @@ -2,32 +2,22 @@ Figures displayed on the map. """ from typing import Any, Dict, Iterator, List, Optional +from svgwrite import Drawing import numpy as np -from colour import Color -from svgwrite import Drawing -from svgwrite.container import Group -from svgwrite.path import Path -from map_machine.direction import DirectionSet, Sector -from map_machine.drawing import PathCommands -from map_machine.flinger import Flinger -from map_machine.osm_reader import OSMNode, Tagged -from map_machine.scheme import LineStyle, Scheme +from map_machine.geometry.flinger import Flinger +from map_machine.osm.osm_reader import OSMNode, Tagged +from map_machine.scheme import LineStyle __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -from map_machine.vector import Polyline - -BUILDING_HEIGHT_SCALE: float = 2.5 -BUILDING_MINIMAL_HEIGHT: float = 8.0 +from map_machine.geometry.vector import Polyline class Figure(Tagged): - """ - Some figure on the map: way or area. - """ + """Some figure on the map: way or area.""" def __init__( self, @@ -37,13 +27,17 @@ class Figure(Tagged): ) -> None: super().__init__(tags) - self.inners: List[List[OSMNode]] = list(map(make_clockwise, inners)) - self.outers: List[List[OSMNode]] = list( - map(make_counter_clockwise, outers) - ) + if inners and outers: + self.inners: List[List[OSMNode]] = list(map(make_clockwise, inners)) + self.outers: List[List[OSMNode]] = list( + map(make_counter_clockwise, outers) + ) + else: + self.inners = inners + self.outers = outers def get_path( - self, flinger: Flinger, offset: np.ndarray = np.array((0, 0)) + self, flinger: Flinger, offset: np.ndarray = np.array((0.0, 0.0)) ) -> str: """ Get SVG path commands. @@ -62,44 +56,6 @@ class Figure(Tagged): return path -class Building(Figure): - """ - Building on the map. - """ - - def __init__( - self, - tags: Dict[str, str], - inners: List[List[OSMNode]], - outers: List[List[OSMNode]], - flinger: Flinger, - scheme: Scheme, - ) -> None: - super().__init__(tags, inners, outers) - - style: Dict[str, Any] = { - "fill": scheme.get_color("building_color").hex, - "stroke": scheme.get_color("building_border_color").hex, - } - self.line_style: LineStyle = LineStyle(style) - self.parts: List[Segment] = [] - - for nodes in self.inners + self.outers: - for i in range(len(nodes) - 1): - flung_1: np.ndarray = flinger.fling(nodes[i].coordinates) - flung_2: np.ndarray = flinger.fling(nodes[i + 1].coordinates) - self.parts.append(Segment(flung_1, flung_2)) - - self.parts = sorted(self.parts) - - self.height: float = BUILDING_MINIMAL_HEIGHT - self.min_height: float = 0.0 - - levels: Optional[str] = self.get_float("building:levels") - if levels: - self.height = float(levels) * BUILDING_HEIGHT_SCALE - - levels: Optional[str] = self.get_float("building:min_level") if levels: self.min_height = float(levels) * BUILDING_HEIGHT_SCALE @@ -118,83 +74,9 @@ class Building(Figure): path.update({"stroke-linejoin": "round"}) svg.add(path) - def draw_shade(self, building_shade: Group, flinger: Flinger) -> None: - """Draw shade casted by the building.""" - scale: float = flinger.get_scale() / 3.0 - shift_1: np.ndarray = np.array((scale * self.min_height, 0)) - shift_2: np.ndarray = np.array((scale * self.height, 0)) - commands: str = self.get_path(flinger, shift_1) - path: Path = Path( - d=commands, fill="#000000", stroke="#000000", stroke_width=1 - ) - building_shade.add(path) - for nodes in self.inners + self.outers: - for i in range(len(nodes) - 1): - flung_1 = flinger.fling(nodes[i].coordinates) - flung_2 = flinger.fling(nodes[i + 1].coordinates) - command: PathCommands = [ - "M", - np.add(flung_1, shift_1), - "L", - np.add(flung_2, shift_1), - np.add(flung_2, shift_2), - np.add(flung_1, shift_2), - "Z", - ] - path: Path = Path( - command, fill="#000000", stroke="#000000", stroke_width=1 - ) - building_shade.add(path) - - def draw_walls( - self, svg: Drawing, height: float, previous_height: float, scale: float - ) -> None: - """Draw building walls.""" - shift_1: np.ndarray = np.array((0, -previous_height * scale)) - shift_2: np.ndarray = np.array((0, -height * scale)) - for segment in self.parts: - fill: Color - if height == 2: - fill = Color("#AAAAAA") - elif height == 4: - fill = Color("#C3C3C3") - else: - color_part: float = 0.8 + segment.angle * 0.2 - fill = Color(rgb=(color_part, color_part, color_part)) - - command = ( - "M", - segment.point_1 + shift_1, - "L", - segment.point_2 + shift_1, - segment.point_2 + shift_2, - segment.point_1 + shift_2, - segment.point_1 + shift_1, - "Z", - ) - path: Path = svg.path( - d=command, - fill=fill.hex, - stroke=fill.hex, - stroke_width=1, - stroke_linejoin="round", - ) - svg.add(path) - - def draw_roof(self, svg: Drawing, flinger: Flinger, scale: float) -> None: - """Draw building roof.""" - path: Path = Path( - d=self.get_path(flinger, np.array([0, -self.height * scale])) - ) - path.update(self.line_style.style) - path.update({"stroke-linejoin": "round"}) - svg.add(path) - class StyledFigure(Figure): - """ - Figure with stroke and fill style. - """ + """Figure with stroke and fill style.""" def __init__( self, @@ -206,164 +88,32 @@ class StyledFigure(Figure): super().__init__(tags, inners, outers) self.line_style: LineStyle = line_style + def get_path( + self, + flinger: Flinger, + offset: np.ndarray = np.array((0.0, 0.0)), + ) -> str: + """ + Get SVG path commands. -class Crater(Tagged): - """ - Volcano or impact crater on the map. - """ + :param flinger: converter for geo coordinates + :param offset: offset vector + """ + path: str = "" - def __init__( - self, tags: Dict[str, str], coordinates: np.ndarray, point: np.ndarray - ) -> None: - super().__init__(tags) - self.coordinates: np.ndarray = coordinates - self.point: np.ndarray = point - - def draw(self, svg: Drawing, flinger: Flinger) -> None: - """Draw crater ridge.""" - scale: float = flinger.get_scale(self.coordinates) - assert "diameter" in self.tags - radius: float = float(self.tags["diameter"]) / 2.0 - radial_gradient = svg.radialGradient( - center=self.point + np.array((0, radius * scale / 7)), - r=radius * scale, - gradientUnits="userSpaceOnUse", - ) - color: Color = Color("#000000") - gradient = svg.defs.add(radial_gradient) - ( - gradient - .add_stop_color(0, color.hex, opacity=0.2) - .add_stop_color(0.7, color.hex, opacity=0.2) - .add_stop_color(1, color.hex, opacity=1) - ) # fmt: skip - circle = svg.circle( - self.point, - radius * scale, - fill=gradient.get_paint_server(), - opacity=0.2, - ) - svg.add(circle) - - -class Tree(Tagged): - """ - Tree on the map. - """ - - def __init__( - self, tags: Dict[str, str], coordinates: np.ndarray, point: np.ndarray - ) -> None: - super().__init__(tags) - self.coordinates: np.ndarray = coordinates - self.point: np.ndarray = point - - def draw(self, svg: Drawing, flinger: Flinger, scheme: Scheme) -> None: - """Draw crown and trunk.""" - scale: float = flinger.get_scale(self.coordinates) - radius: float - if "diameter_crown" in self.tags: - radius = float(self.tags["diameter_crown"]) / 2.0 - else: - radius = 2.0 - color: Color = scheme.get_color("evergreen_color") - svg.add(svg.circle(self.point, radius * scale, fill=color, opacity=0.3)) - - if "circumference" in self.tags: - radius: float = float(self.tags["circumference"]) / 2.0 / np.pi - svg.add(svg.circle(self.point, radius * scale, fill="#B89A74")) - - -class DirectionSector(Tagged): - """ - Sector that represents direction. - """ - - def __init__(self, tags: Dict[str, str], point: np.ndarray) -> None: - super().__init__(tags) - self.point: np.ndarray = point - - def draw(self, svg: Drawing, scheme: Scheme) -> None: - """Draw gradient sector.""" - angle: Optional[float] = None - is_revert_gradient: bool = False - direction: str - direction_radius: float - direction_color: Color - - if self.get_tag("man_made") == "surveillance": - direction = self.get_tag("camera:direction") - if "camera:angle" in self.tags: - angle = float(self.get_tag("camera:angle")) - if "angle" in self.tags: - angle = float(self.get_tag("angle")) - direction_radius = 50 - direction_color = scheme.get_color("direction_camera_color") - elif self.get_tag("traffic_sign") == "stop": - direction = self.get_tag("direction") - direction_radius = 25 - direction_color = Color("red") - else: - direction = self.get_tag("direction") - direction_radius = 50 - direction_color = scheme.get_color("direction_view_color") - is_revert_gradient = True - - if not direction: - return - - point: np.ndarray = (self.point.astype(int)).astype(float) - - paths: Iterator[PathCommands] - if angle is not None: - paths = [Sector(direction, angle).draw(point, direction_radius)] - else: - paths = DirectionSet(direction).draw(point, direction_radius) - - for path in paths: - radial_gradient = svg.radialGradient( - center=point, - r=direction_radius, - gradientUnits="userSpaceOnUse", + for outer_nodes in self.outers: + commands: str = get_path( + outer_nodes, offset, flinger, self.line_style.parallel_offset ) - gradient = svg.defs.add(radial_gradient) - if is_revert_gradient: - ( - gradient - .add_stop_color(0, direction_color.hex, opacity=0) - .add_stop_color(1, direction_color.hex, opacity=0.7) - ) # fmt: skip - else: - ( - gradient - .add_stop_color(0, direction_color.hex, opacity=0.4) - .add_stop_color(1, direction_color.hex, opacity=0) - ) # fmt: skip - path_element: Path = svg.path( - d=["M", point] + path + ["L", point, "Z"], - fill=gradient.get_paint_server(), + path += f"{commands} " + + for inner_nodes in self.inners: + commands: str = get_path( + inner_nodes, offset, flinger, self.line_style.parallel_offset ) - svg.add(path_element) + path += f"{commands} " - -class Segment: - """ - Line segment. - """ - - def __init__(self, point_1: np.ndarray, point_2: np.ndarray) -> None: - self.point_1: np.ndarray = point_1 - self.point_2: np.ndarray = point_2 - - difference: np.ndarray = point_2 - point_1 - vector: np.ndarray = difference / np.linalg.norm(difference) - self.angle: float = np.arccos(np.dot(vector, np.array((0, 1)))) / np.pi - - def __lt__(self, other: "Segment") -> bool: - return ( - ((self.point_1 + self.point_2) / 2)[1] - < ((other.point_1 + other.point_2) / 2)[1] - ) # fmt: skip + return path def is_clockwise(polygon: List[OSMNode]) -> bool: @@ -372,13 +122,13 @@ def is_clockwise(polygon: List[OSMNode]) -> bool: :param polygon: list of OpenStreetMap nodes """ - count: float = 0 + count: float = 0.0 for index, node in enumerate(polygon): next_index: int = 0 if index == len(polygon) - 1 else index + 1 count += (polygon[next_index].coordinates[0] - node.coordinates[0]) * ( polygon[next_index].coordinates[1] + node.coordinates[1] ) - return count >= 0 + return count >= 0.0 def make_clockwise(polygon: List[OSMNode]) -> List[OSMNode]: @@ -399,8 +149,13 @@ def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]: return polygon if not is_clockwise(polygon) else list(reversed(polygon)) -def get_path(nodes: List[OSMNode], shift: np.ndarray, flinger: Flinger) -> str: +def get_path( + nodes: List[OSMNode], + shift: np.ndarray, + flinger: Flinger, + parallel_offset: float = 0.0, +) -> str: """Construct SVG path commands from nodes.""" return Polyline( - [flinger.fling(x.coordinates) + shift for x in nodes] - ).get_path() + [flinger.fling(node.coordinates) + shift for node in nodes] + ).get_path(parallel_offset) diff --git a/map_machine/geometry/__init__.py b/map_machine/geometry/__init__.py new file mode 100644 index 0000000..3b1a03f --- /dev/null +++ b/map_machine/geometry/__init__.py @@ -0,0 +1,3 @@ +""" +Map geometry: dealing with coordinates, projections. +""" diff --git a/map_machine/boundary_box.py b/map_machine/geometry/boundary_box.py similarity index 73% rename from map_machine/boundary_box.py rename to map_machine/geometry/boundary_box.py index ca490a7..e35706d 100644 --- a/map_machine/boundary_box.py +++ b/map_machine/geometry/boundary_box.py @@ -17,9 +17,7 @@ LONGITUDE_MAX_DIFFERENCE: float = 0.5 @dataclass class BoundaryBox: - """ - Rectangle that limit space on the map. - """ + """Rectangle that limit space on the map.""" left: float # Minimum longitude. bottom: float # Minimum latitude. @@ -97,15 +95,15 @@ class BoundaryBox: n: float = 2.0 ** (zoom_level + 8.0) x: int = int((coordinates[1] + 180.0) / 360.0 * n) - left: float = (x - width / 2) / n * 360.0 - 180.0 - right: float = (x + width / 2) / n * 360.0 - 180.0 + left: float = (x - width / 2.0) / n * 360.0 - 180.0 + right: float = (x + width / 2.0) / n * 360.0 - 180.0 y: int = (1.0 - np.arcsinh(np.tan(lat_rad)) / np.pi) / 2.0 * n bottom_radians = np.arctan( - np.sinh((1.0 - (y + height / 2) * 2.0 / n) * np.pi) + np.sinh((1.0 - (y + height / 2.0) * 2.0 / n) * np.pi) ) top_radians = np.arctan( - np.sinh((1.0 - (y - height / 2) * 2.0 / n) * np.pi) + np.sinh((1.0 - (y - height / 2.0) * 2.0 / n) * np.pi) ) return cls( @@ -123,27 +121,27 @@ class BoundaryBox: """Get maximum coordinates.""" return np.array((self.top, self.right)) - def get_left_top(self) -> (np.ndarray, np.ndarray): + def get_left_top(self) -> np.ndarray: """Get left top corner of the boundary box.""" - return self.top, self.left + return np.array((self.top, self.left)) - def get_right_bottom(self) -> (np.ndarray, np.ndarray): + def get_right_bottom(self) -> np.ndarray: """Get right bottom corner of the boundary box.""" - return self.bottom, self.right + return np.array((self.bottom, self.right)) def round(self) -> "BoundaryBox": """Round boundary box.""" - self.left = round(self.left * 1000) / 1000 - 0.001 - self.bottom = round(self.bottom * 1000) / 1000 - 0.001 - self.right = round(self.right * 1000) / 1000 + 0.001 - self.top = round(self.top * 1000) / 1000 + 0.001 + self.left = round(self.left * 1000.0) / 1000.0 - 0.001 + self.bottom = round(self.bottom * 1000.0) / 1000.0 - 0.001 + self.right = round(self.right * 1000.0) / 1000.0 + 0.001 + self.top = round(self.top * 1000.0) / 1000.0 + 0.001 return self def center(self) -> np.ndarray: """Return center point of boundary box.""" return np.array( - ((self.left + self.right) / 2, (self.top + self.bottom) / 2) + ((self.top + self.bottom) / 2.0, (self.left + self.right) / 2.0) ) def get_format(self) -> str: @@ -152,16 +150,23 @@ class BoundaryBox: ,,,. Coordinates are rounded to three digits after comma. """ - left: float = np.floor(self.left * 1000) / 1000 - bottom: float = np.floor(self.bottom * 1000) / 1000 - right: float = np.ceil(self.right * 1000) / 1000 - top: float = np.ceil(self.top * 1000) / 1000 + left: float = np.floor(self.left * 1000.0) / 1000.0 + bottom: float = np.floor(self.bottom * 1000.0) / 1000.0 + right: float = np.ceil(self.right * 1000.0) / 1000.0 + top: float = np.ceil(self.top * 1000.0) / 1000.0 return f"{left:.3f},{bottom:.3f},{right:.3f},{top:.3f}" + def update(self, coordinates: np.ndarray) -> None: + """Make the boundary box cover coordinates.""" + self.left = min(self.left, coordinates[1]) + self.bottom = min(self.bottom, coordinates[0]) + self.right = max(self.right, coordinates[1]) + self.top = max(self.top, coordinates[0]) + def combine(self, other: "BoundaryBox") -> None: """Combine with another boundary box.""" self.left = min(self.left, other.left) - self.right = min(self.right, other.right) self.bottom = min(self.bottom, other.bottom) - self.top = min(self.top, other.top) + self.right = max(self.right, other.right) + self.top = max(self.top, other.top) diff --git a/map_machine/flinger.py b/map_machine/geometry/flinger.py similarity index 86% rename from map_machine/flinger.py rename to map_machine/geometry/flinger.py index 95c1063..5f0065c 100644 --- a/map_machine/flinger.py +++ b/map_machine/geometry/flinger.py @@ -5,7 +5,7 @@ from typing import Optional import numpy as np -from map_machine.boundary_box import BoundaryBox +from map_machine.geometry.boundary_box import BoundaryBox __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -20,7 +20,9 @@ def pseudo_mercator(coordinates: np.ndarray) -> np.ndarray: :return: position on the plane in the form of (x, y) """ y: float = ( - 180 / np.pi * np.log(np.tan(np.pi / 4 + coordinates[0] * np.pi / 360)) + 180.0 + / np.pi + * np.log(np.tan(np.pi / 4.0 + coordinates[0] * np.pi / 360.0)) ) return np.array((coordinates[1], y)) @@ -36,13 +38,11 @@ def osm_zoom_level_to_pixels_per_meter( function allows any non-negative float value :param equator_length: celestial body equator length in meters """ - return 2 ** zoom_level / equator_length * 256 + return 2.0**zoom_level / equator_length * 256.0 class Flinger: - """ - Convert geo coordinates into SVG position points. - """ + """Convert geo coordinates into SVG position points.""" def __init__( self, @@ -56,7 +56,7 @@ class Flinger: :param equator_length: celestial body equator length in meters """ self.geo_boundaries: BoundaryBox = geo_boundaries - self.ratio: float = 2 ** zoom_level * 256 / 360 + self.ratio: float = 2.0**zoom_level * 256.0 / 360.0 self.size: np.ndarray = self.ratio * ( pseudo_mercator(self.geo_boundaries.max_()) - pseudo_mercator(self.geo_boundaries.min_()) @@ -92,5 +92,5 @@ class Flinger: # Get pixels per meter ratio for the center of the boundary box. coordinates = self.geo_boundaries.center() - scale_factor: float = 1 / np.cos(coordinates[0] / 180 * np.pi) + scale_factor: float = abs(1.0 / np.cos(coordinates[0] / 180.0 * np.pi)) return self.pixels_per_meter * scale_factor diff --git a/map_machine/geometry/vector.py b/map_machine/geometry/vector.py new file mode 100644 index 0000000..6f8728d --- /dev/null +++ b/map_machine/geometry/vector.py @@ -0,0 +1,174 @@ +""" +Vector utility. +""" +import numpy as np + +__author__ = "Sergey Vartanov" +__email__ = "me@enzet.ru" + +from typing import List +from shapely.geometry import LineString + + +def compute_angle(vector: np.ndarray) -> float: + """ + For the given vector compute an angle between it and (1, 0) vector. The + result is in [0, 2π]. + """ + if vector[0] == 0.0: + if vector[1] > 0.0: + return np.pi / 2.0 + return np.pi + np.pi / 2.0 + if vector[0] < 0.0: + return np.arctan(vector[1] / vector[0]) + np.pi + if vector[1] < 0.0: + return np.arctan(vector[1] / vector[0]) + 2.0 * np.pi + return np.arctan(vector[1] / vector[0]) + + +def turn_by_angle(vector: np.ndarray, angle: float) -> np.ndarray: + """Turn vector by an angle.""" + return np.array( + ( + vector[0] * np.cos(angle) - vector[1] * np.sin(angle), + vector[0] * np.sin(angle) + vector[1] * np.cos(angle), + ) + ) + + +def norm(vector: np.ndarray) -> np.ndarray: + """Compute vector with the same direction and length 1.""" + return vector / np.linalg.norm(vector) + + +class Polyline: + """List of connected points.""" + + def __init__(self, points: List[np.ndarray]) -> None: + self.points: List[np.ndarray] = points + + def get_path(self, parallel_offset: float = 0.0) -> str: + """Construct SVG path commands.""" + points: List[np.ndarray] + if np.allclose(parallel_offset, 0.0): + points = self.points + else: + try: + points = ( + LineString(self.points) + .parallel_offset(parallel_offset) + .coords + if parallel_offset + else self.points + ) + except (ValueError, NotImplementedError): + points = self.points + + return ( + "M " + + " L ".join(f"{point[0]},{point[1]}" for point in points) + + (" Z" if np.allclose(points[0], points[-1]) else "") + ) + + def shorten(self, index: int, length: float) -> None: + """Make shorten part specified with index.""" + index_2: int = 1 if index == 0 else -2 + diff: np.ndarray = self.points[index_2] - self.points[index] + self.points[index] = ( + self.points[index] + diff / np.linalg.norm(diff) * length + ) + + +class Line: + """Infinity line: Ax + By + C = 0.""" + + def __init__(self, start: np.ndarray, end: np.ndarray) -> None: + # if start.near(end): + # util.error("cannot create line by one point") + self.a: float = start[1] - end[1] + self.b: float = end[0] - start[0] + self.c: float = start[0] * end[1] - end[0] * start[1] + + def parallel_shift(self, shift: np.ndarray) -> None: + """ + Shift current vector according with shift. + + :param shift: shift vector + """ + self.c -= self.a * shift[0] + self.b * shift[1] + + def is_parallel(self, other: "Line") -> bool: + """If lines are parallel or equal.""" + return np.allclose(other.a * self.b - self.a * other.b, 0.0) + + def get_intersection_point(self, other: "Line") -> np.ndarray: + """Get point of intersection current line with other.""" + if other.a * self.b - self.a * other.b == 0.0: + return np.array((0.0, 0.0)) + + x: float = -(self.b * other.c - other.b * self.c) / ( + other.a * self.b - self.a * other.b + ) + y: float = -(self.a * other.c - other.a * self.c) / ( + other.b * self.a - self.b * other.a + ) + return np.array((x, y)) + + def __repr__(self) -> str: + return f"{self.a} * x + {self.b} * y + {self.c} == 0" + + +class Segment: + """Closed line segment.""" + + def __init__(self, point_1: np.ndarray, point_2: np.ndarray) -> None: + self.point_1: np.ndarray = point_1 + self.point_2: np.ndarray = point_2 + + self.y = ((self.point_1 + self.point_2) / 2.0)[1] + + difference: np.ndarray = point_2 - point_1 + vector: np.ndarray = difference / np.linalg.norm(difference) + if vector[0] > 0: + vector = -vector + self.angle: float = ( + np.arccos(np.dot(vector, np.array((0.0, 1.0)))) / np.pi + ) + + def __repr__(self): + return f"{self.point_1} -- {self.point_2}" + + def __lt__(self, other: "Segment") -> bool: + return self.y < other.y + + def intersection(self, other: "Segment"): + divisor = (self.point_1[0] - self.point_2[0]) * ( + other.point_1[1] - other.point_2[1] + ) - (self.point_1[1] - self.point_2[1]) * ( + other.point_1[0] - other.point_2[0] + ) + if not divisor: + return None + + t: float = ( + (self.point_1[0] - other.point_1[0]) + * (other.point_1[1] - other.point_2[1]) + - (self.point_1[1] - other.point_1[1]) + * (other.point_1[0] - other.point_2[0]) + ) / divisor + + u: float = ( + (self.point_1[0] - other.point_1[0]) + * (self.point_1[1] - self.point_2[1]) + - (self.point_1[1] - other.point_1[1]) + * (self.point_1[0] - self.point_2[0]) + ) / divisor + + if 0 <= t <= 1 and 0 <= u <= 1: + print(t) + return [ + self.point_1[0] + t * (self.point_2[0] - self.point_1[0]), + self.point_1[1] + t * (self.point_2[1] - self.point_1[1]), + ] + else: + return None diff --git a/map_machine/icons/LICENSE b/map_machine/icons/LICENSE index 112ace9..652d243 100644 --- a/map_machine/icons/LICENSE +++ b/map_machine/icons/LICENSE @@ -1,7 +1,7 @@ -Rontgen icons (c) by Sergey Vartanov +Röntgen icons (c) by Sergey Vartanov -Rontgen icons are licensed under a Creative Commons Attribution 4.0 +Röntgen icons are licensed under a Creative Commons Attribution 4.0 International License. -You should have received a copy of the license along with this work. If not, +You should have received a copy of the license along with this work. If not, see . diff --git a/map_machine/icons/config.json b/map_machine/icons/config.json index 3231951..24bdd85 100644 --- a/map_machine/icons/config.json +++ b/map_machine/icons/config.json @@ -1,913 +1,560 @@ { - "amusement_ride": { - "emoji": "🎠" - }, - "anchor": { - "emoji": "⚓", - "name": "anchor" - }, - "apartments": {}, - "apple": { - "emoji": [ - "🍎", - "🍏" - ], - "name": "apple" - }, - "arrow_down": { - "is_part": true - }, - "arrow_left": { - "is_part": true - }, - "arrow_right": { - "is_part": true - }, - "arrow_up": { - "is_part": true - }, - "at_in_square": { - "emoji": "@", - "name": "@ in square" - }, - "atm": {}, - "awning": { - "is_part": true - }, - "bag": {}, - "baptist": {}, - "bbq": { - "name": "BBQ" - }, - "beach": {}, - "bed": { - "emoji": "🛌" - }, - "beer_mug": { - "emoji": "🍺", - "name": "beer mug" - }, - "bench": { - "name": "bench" - }, - "bench_backrest": { - "name": "bench with backrest" - }, - "bench_no_backrest": { - "name": "bench without backrest" - }, - "bench_with_shelter": {}, - "betula": { - "group": "plant", - "name": "betula" - }, - "bicycle": { - "emoji": "🚲", - "name": "bicycle" - }, - "bicycle_share": { - "name": "bicycle with share sign" - }, - "billboard": { - "name": "billboard" - }, - "binoculars": { - "name": "binocular" - }, - "binoculars_on_pole": { - "name": "binoculars on pole" - }, - "bleachers": {}, - "block": {}, - "bollard": {}, - "book": { - "emoji": "📕", - "name": "book" - }, - "books": { - "emoji": "📚", - "name": "books" - }, - "booster_landing": { - "name": "landing booster" - }, - "bottle": {}, - "bottle_and_wine_glass": {}, - "bottom_right_horizontal_line": { - "is_part": true - }, - "bowling_ball": {}, - "bricks": {}, - "briefcase": {}, - "buffer_stop": { - "name": "buffer stop" - }, - "building": {}, - "bump": {}, - "buoy": {}, - "burger": { - "emoji": "🍔", - "name": "burger" - }, - "bus": { - "emoji": "🚌", - "name": "bus" - }, - "bus_stop": { - "emoji": "🚏" - }, - "bus_stop_bench": { - "is_part": true - }, - "bus_stop_shelter": { - "is_part": true - }, - "bus_stop_sign": {}, - "buses": { - "name": "two buses" - }, - "bush": {}, - "cactus": {}, - "camp": { - "name": "camp" - }, - "cannon": { - "directed": "right", - "name": "cannon" - }, - "car": { - "name": "car" - }, - "car_on_ferry": {}, - "car_wash": { - "name": "car_wash" - }, - "caravan": { - "name": "caravan" - }, - "card_and_dice": { - "name": "card and dice" - }, - "cave": {}, - "cctv": { - "directed": "right" - }, - "charging_station": {}, - "chimney": { - "name": "chimney" - }, - "christmas_tree": { - "emoji": "🎄", - "name": "Christmas tree" - }, - "circle_11": { - "name": "circle 11 px" - }, - "circle_9": { - "emoji": "🕳", - "name": "circle 9 px" - }, - "circle_empty": {}, - "city_gate": { - "name": "city gate" - }, - "city_limit_sign": {}, - "cliff": {}, - "clock": { - "emoji": "⌚", - "name": "clock" - }, - "clockwise": {}, - "cocktail_glass": { - "name": "cocktail glass" - }, - "cocktail_glass_with_straw": { - "name": "cocktail glass with straw" - }, - "coffee_cup": { - "emoji": "☕", - "name": "coffee cup" - }, - "comb_and_scissors": { - "name": "comb and scissors" - }, - "contrclockwise": {}, - "crater": { - "name": "crater" - }, - "credit_card": { - "emoji": "💳", - "name": "payment card" - }, - "cross": { - "emoji": "✝️" - }, - "crossing": {}, - "cupcake": { - "emoji": "🧁" - }, - "default": {}, - "default_small": {}, - "defibrillator": {}, - "descent_stage": { - "name": "descent stage" - }, - "dharmachakra": {}, - "diamond": { - "emoji": "💎" - }, - "digit_0": { - "is_part": true - }, - "digit_1": { - "is_part": true - }, - "digit_2": { - "is_part": true - }, - "digit_3": { - "is_part": true - }, - "digit_4": { - "is_part": true - }, - "digit_5": { - "is_part": true - }, - "digit_6": { - "is_part": true - }, - "digit_7": { - "is_part": true - }, - "digit_8": { - "is_part": true - }, - "digit_9": { - "is_part": true - }, - "digital_clock": {}, - "dog": { - "emoji": "🐕" - }, - "dog_and_cross": { - "name": "dog and cross" - }, - "door_with_keyhole": { - "name": "door with keyhole" - }, - "drinking_water": { - "emoji": "🚰" - }, - "dumbbell": { - "name": "dumbbell" - }, - "ear_botany": {}, - "ear_botany_2": {}, - "electricity": { - "emoji": "⚡" - }, - "elevator": { - "name": "elevator" - }, - "entrance": { - "name": "door" - }, - "envelope": { - "emoji": "✉️", - "name": "envelope" - }, - "exchange": { - "name": "exchange" - }, - "exit": {}, - "film": { - "emoji": "🎞️", - "name": "film" - }, - "fire_extinguisher": { - "emoji": "🧯" - }, - "fire_hydrant": {}, - "firepit": { - "name": "firepit" - }, - "fireplace": {}, - "fishing_angle": { - "name": "fishing angle" - }, - "flagpole": { - "emoji": "🏴" - }, - "food_court": { - "name": "food court" - }, - "foot": { - "emoji": "👣" - }, - "ford": {}, - "fountain": { - "emoji": "⛲", - "name": "fountain" - }, - "fountain_bubbler": { - "name": "bubbler fountain" - }, - "fountain_cascade": { - "name": "cascade fountain" - }, - "fountain_roman_wolf": { - "name": "Roman wolf fountain" - }, - "fountain_toret": { - "name": "toret fountain" - }, - "frame": { - "name": "picture frame" - }, - "fuel_station": { - "emoji": "⛽️" - }, - "garages": {}, - "gate": {}, - "gift": { - "emoji": "🎁" - }, - "glider": {}, - "golf_club_and_ball": {}, - "golf_pin": {}, - "golf_tee": {}, - "government": {}, - "grapes": {}, - "grapes_2": {}, - "grapes_3": {}, - "greek_cross": { - "name": "greek cross" - }, - "greek_cross_in_box": { - "name": "inverted Greek cross in box" - }, - "guidepost": { - "directed": "right" - }, - "guys": {}, - "h": { - "emoji": "H", - "name": "H letter" - }, - "hifi": { - "name": "Hi-Fi" - }, - "historic": {}, - "hopscotch": { - "name": "hopscotch" - }, - "horizontal_bar": { - "name": "high horizontal bar" - }, - "horizontal_ladder": { - "name": "horizontal ladder" - }, - "human_on_ferry": {}, - "ice_cream": { - "emoji": "🍨" - }, - "ice_cream_2": {}, - "information": { - "name": "i letter" - }, - "information_board": {}, - "japan_castle": { - "name": "Japanese map symbol for castle" - }, - "japan_court": { - "name": "Japanese map symbol for court house or building" - }, - "japan_elementary_school": {}, - "japan_fire_station": { - "name": "Japanese map symbol for fire station" - }, - "japan_forest_service": { - "name": "Japanese map symbol for forest service office" - }, - "japan_historic": { - "name": "Japanese map symbol for place of historic, cultural, or scenic interest" - }, - "japan_koban": { - "name": "Japanese map symbol for kōban" - }, - "japan_post": { - "name": "Japanese map symbol for post office" - }, - "japan_shinto_shrine": { - "name": "Japanese map symbol for shinto shrine" - }, - "japan_tv_tower": { - "name": "Japanese map symbol for TV tower" - }, - "japan_well": { - "name": "Japanese map symbol for oil or gas well" - }, - "jewish": { - "emoji": "✡️" - }, - "judgement": {}, - "key": { - "is_part": true, - "name": "key" - }, - "kiosk": {}, - "knives": { - "name": "knives" - }, - "lander": { - "name": "lander" - }, - "lattice": {}, - "lattice_guyed": {}, - "leaf_maple": { - "group": "plant" - }, - "life_ring": {}, - "lift_gate": {}, - "light_left": { - "is_part": true - }, - "light_right": { - "is_part": true - }, - "lock": {}, - "lock_unlocked": {}, - "lock_with_keyhole": { - "emoji": "🔒" - }, - "low_horizontal_bars": { - "name": "low horizontal bars" - }, - "lowered_kerb": {}, - "lunokhod": { - "name": "lunokhod" - }, - "maglev": {}, - "main_entrance": {}, - "manhole_drain": { - "name": "drain manhole cover" - }, - "massage": {}, - "mausoleum": { - "name": "mausoleum" - }, - "medicine_bottle": { - "name": "medicine bottle" - }, - "memorial": {}, - "microphone": { - "emoji": "🎤", - "name": "microphone" - }, - "milk": {}, - "money": {}, - "monorail": {}, - "muslim": { - "emoji": "☪️" - }, - "needleleaved_tree": { - "emoji": "🌲", - "name": "needleleaved tree" - }, - "no_door": {}, - "no_foot": {}, - "no_traffic_signals": {}, - "no_wheelchair": {}, - "noexit": { - "name": "letter T" - }, - "oat": {}, - "oat_2": {}, - "onion_roof_shape": { - "name": "onion roof shape" - }, - "orbiter": { - "name": "orbiter" - }, - "orthodox": { - "emoji": "☦️" - }, - "p": { - "emoji": "P", - "name": "letter P" - }, - "p_small": { - "name": "small letter P" - }, - "pac_man": { - "name": "Pac-Man" - }, - "pagoda": { - "name": "pagoda" - }, - "palm": { - "emoji": "🌴" - }, - "pan": {}, - "peach": {}, - "pear": { - "emoji": "🍐", - "name": "pear" - }, - "pergola": { - "is_part": true - }, - "phone": { - "name": "phone" - }, - "photo_camera": { - "emoji": "📷" - }, - "picture": {}, - "pipeline": {}, - "plane": { - "emoji": "✈️", - "name": "plane" - }, - "plaque": { - "name": "plaque with inscription" - }, - "platform": { - "is_part": true - }, - "pole": {}, - "pole_dancer": { - "name": "pole dancer" - }, - "pole_lamp": { - "directed": "right", - "name": "pole lamp" - }, - "power_generator": {}, - "power_pole_1_level": { - "name": "one-level transmission pole" - }, - "power_pole_2_level": { - "name": "two-level transmission pole" - }, - "power_pole_3_level": { - "name": "three-level transmission pole" - }, - "power_pole_4_level": { - "name": "four-level transmission pole" - }, - "power_pole_asymmetric": { - "name": "asymmetric transmission pole" - }, - "power_pole_asymmetric_armless": { - "name": "asymmetric armless transmission pole" - }, - "power_pole_delta": { - "name": "delta transmission pole" - }, - "power_pole_flag": { - "name": "flag transmission pole" - }, - "power_pole_triangle": { - "name": "triangle transmission pole" - }, - "power_pole_triangle_armless": { - "name": "armless triangle transmission pole" - }, - "power_tower_1_level": { - "name": "one-level transmission tower" - }, - "power_tower_2_level": { - "name": "two-level transmission tower" - }, - "power_tower_3_level": { - "name": "three-level transmission tower" - }, - "power_tower_4_level": { - "name": "four-level transmission tower" - }, - "power_tower_asymmetric": { - "name": "asymmetric transmission tower" - }, - "power_tower_barrel": { - "name": "barrel transmission tower" - }, - "power_tower_delta": { - "name": "delta transmission tower" - }, - "power_tower_delta_2_level": { - "name": "delta two-level transmission tower" - }, - "power_tower_delta_3_level": { - "name": "delta three-level transmission tower" - }, - "power_tower_donau": { - "name": "donau transmission tower" - }, - "power_tower_flag": { - "name": "flag transmission tower" - }, - "power_tower_guyed_h_frame": { - "name": "guyed h-frame transmission tower" - }, - "power_tower_h_frame": { - "name": "h-frame transmission tower" - }, - "power_tower_h_frame_2_level": { - "name": "h-frame two-level transmission tower" - }, - "power_tower_portal": { - "name": "portal transmission tower" - }, - "power_tower_portal_2_level": { - "name": "portal two-level transmission tower" - }, - "power_tower_portal_3_level": { - "name": "portal three-level transmission tower" - }, - "power_tower_triangle": { - "name": "triangle transmission tower" - }, - "power_tower_x_frame": { - "name": "x-frame transmission tower" - }, - "power_tower_y_frame": { - "name": "y-frame transmission tower" - }, - "prison": {}, - "probe": { - "name": "probe" - }, - "rape": {}, - "rape_2": {}, - "rectangle_vertical_rounded": { - "name": "vertical rounded rectangle" - }, - "rectangle_vertical_rounded_crossed": { - "name": "crossed vertical rounded rectangle" - }, - "recycling_container": { - "name": "recycling container" - }, - "restaurant": { - "emoji": "🍴" - }, - "rings": { - "name": "gymnastic rings" - }, - "rocket_flying": { - "emoji": "🚀", - "name": "flying rocket" - }, - "rocket_on_launch_pad": { - "name": "rocket on launch pad" - }, - "roof": { - "is_part": true - }, - "roof_and_walls": { - "is_part": true - }, - "roundabout": {}, - "russian_orthodox": {}, - "sauna": {}, - "seesaw": { - "name": "seesaw" - }, - "sheets": { - "name": "two sheets" - }, - "shelter": { - "name": "shelter" - }, - "shield_volcano": { - "name": "shield volcano" - }, - "shoe": { - "name": "shoe" - }, - "shop_convenience": {}, - "shower": { - "name": "shower" - }, - "sit_up": { - "name": "incline bench" - }, - "skateboard": {}, - "slide": { - "directed": "right", - "name": "slide" - }, - "slide_and_water": {}, - "solar_panel": {}, - "sos_phone": { - "name": "phone with SOS" - }, - "speed_limit_mph": { - "name": "speed limit box for mph" - }, - "staircase": { - "name": "door with stairs" - }, - "statue": { - "name": "statue" - }, - "steak_and_fork": { - "name": "steak and fork" - }, - "stone": { - "emoji": "🪨" - }, - "stone_with_inscription": {}, - "stop": { - "emoji": [ - "🛑", - "⛔️" - ] - }, - "stratovolcano": { - "name": "stratovolcano" - }, - "street_cabinet": {}, - "street_lamp": {}, - "sunflower": {}, - "supermarket_cart": {}, - "support_column": { - "is_part": true - }, - "support_pole": { - "is_part": true - }, - "support_wall": { - "is_part": true - }, - "survey_point": {}, - "suspension_railway": { - "name": "suspension railway" - }, - "swimming_area": {}, - "t_shirt": { - "name": "t-shirt" - }, - "t_shirt_and_scissors": { - "name": "t-shirt and scissors" - }, - "table": {}, - "table_and_two_chairs": { - "name": "table and two chairs" - }, - "tactile_paving": {}, - "taxi": {}, - "telephone": { - "emoji": "☎️" - }, - "telescope_gamma": { - "name": "gamma telescope" - }, - "telescope_radio": { - "name": "radio telescope" - }, - "theatre": {}, - "third_stage": { - "name": "rocket third stage" - }, - "ticket": { - "emoji": [ - "🎫", - "🎟" - ] - }, - "toll_booth": { - "name": "toll booth" - }, - "tomb": { - "emoji": "🪦" - }, - "tooth": { - "name": "tooth" - }, - "torch": { - "name": "torch" - }, - "toucan_crossing": {}, - "tower": {}, - "tower_communication": { - "name": "communication tower" - }, - "tower_cooling": { - "name": "cooling tower" - }, - "tower_defensive": { - "name": "defensive tower" - }, - "townhall": { - "name": "townhall" - }, - "toy_horse": {}, - "traffic_cushion": {}, - "traffic_signals": { - "emoji": "🚦" - }, - "traffic_table": {}, - "train": { - "emoji": "🚆", - "name": "train" - }, - "tram": { - "emoji": "🚊", - "name": "tram" - }, - "transformer": { - "name": "transformer" - }, - "tree": { - "emoji": "🌳", - "name": "tree" - }, - "tree_with_leaf": { - "name": "tree with leaf" - }, - "triangle_down_hollow": {}, - "triangle_small": { - "emoji": "⛰️", - "name": "small triangle" - }, - "trolleybus": { - "name": "trolleybus" - }, - "tube": {}, - "tube_guyed": {}, - "turnstile": { - "name": "turnstile" - }, - "tv": {}, - "two_beds": {}, - "two_people_together": {}, - "umbrella": { - "is_part": true - }, - "urban_tree_pot": { - "is_part": true - }, - "vanity_mirror": { - "name": "vanity mirror" - }, - "vending_angle": {}, - "vending_bottle": {}, - "vending_candles": {}, - "vending_chemist": {}, - "vending_drop": {}, - "vending_excrement_bag": { - "name": "excrement bag vending" - }, - "vending_machine": {}, - "vending_p": { - "name": "vending machine with letter P" - }, - "vending_tickets": {}, - "ventilation": {}, - "volcanic_cone": { - "name": "volcanic cone" - }, - "wall_bars": { - "name": "wall bars" - }, - "waste_basket": { - "name": "waste basket" - }, - "waste_disposal": {}, - "watches": {}, - "waterfall": { - "directed": "right", - "name": "waterfall" - }, - "wave_left": { - "is_part": true - }, - "wave_right": { - "is_part": true - }, - "waving_flag": {}, - "wheelchair": { - "emoji": "🦽" - }, - "wind_turbine": {}, - "wlan": {}, - "woman_and_man": { - "emoji": "🚻", - "name": "woman and man" - }, - "wood": { - "emoji": "🪵" - }, - "wretch_and_hammer": { - "name": "wretch and hammer" - }, - "x": { - "emoji": "❌" - }, - "y": { - "name": "letter Y" + "architecture": { + "building_size": { + "apartments_1_story": {"name": "one-story apartments building", "categories": ["building"]}, + "apartments_1_story_gabled_roof": {"name": "one-story apartments building with gabled roof", "categories": ["building"]}, + "apartments_1_story_skillion_roof": {"name": "one-story apartments building with skillion roof", "categories": ["building"]}, + "apartments_2_story": {"name": "two-story apartments building", "categories": ["building"]}, + "apartments_2_story_gabled_roof": {"name": "two-story apartments building with gabled roof", "categories": ["building"]}, + "apartments_2_story_skillion_roof": {"name": "two-story apartments building with skillion roof", "categories": ["building"]}, + "apartments_3_story": {"name": "three-story apartments building", "categories": ["building"]}, + "apartments_3_story_gabled_roof": {"name": "three-story apartments building with gabled roof", "categories": ["building"]}, + "apartments_3_story_skillion_roof": {"name": "three-story apartments building with skillion roof", "categories": ["building"]}, + "apartments_4_story": {"name": "four-story apartments building", "categories": ["building"]}, + "apartments_4_story_gabled_roof": {"name": "four-story apartments building with gabled roof", "categories": ["building"]}, + "apartments_4_story_skillion_roof": {"name": "four-story apartments building with skillion roof", "categories": ["building"]}, + "apartments_5_story": {"name": "five-story apartments building", "categories": ["building"]}, + "apartments_5_story_gabled_roof": {"name": "five-story apartments building with gabled roof", "categories": ["building"]}, + "apartments_5_story_skillion_roof": {"name": "five-story apartments building with skillion roof", "categories": ["building"]}, + "building": {"name": "house", "categories": ["building"]}, + "building_construction": {"name": "building construction", "categories": ["building"]}, + "building_container": {"name": "container building", "categories": ["building"]}, + "houseboat": {"name": "houseboat", "categories": ["building"]}, + "fort": {"name": "fort", "categories": ["building"]}, + "mausoleum": {"name": "mausoleum", "categories": ["building"]}, + "minaret": {"name": "minaret", "categories": ["building"]}, + "pagoda": {"name": "pagoda", "categories": ["building"]}, + "townhall": {"name": "town hall", "categories": ["building"]} + }, + "small": { + "pillar": {"name": "pillar"}, + "wayside_shrine": {"name": "wayside shrine"} + } + }, + "life": { + "plant": { + "fruit": { + "apple": {"emoji": ["🍎", "🍏"], "name": "apple", "categories": ["natural"]}, + "peach": {"categories": ["natural"], "name": "peach", "emoji": "🍑"}, + "pear": {"emoji": "🍐", "name": "pear", "categories": ["natural"]} + }, + "tree": { + "betula": {"name": "birch", "categories": ["natural"]}, + "christmas_tree": {"emoji": "🎄", "name": "Christmas tree", "categories": ["natural"]}, + "needleleaved_tree": {"emoji": "🌲", "name": "needleleaved tree", "categories": ["natural"]}, + "palm": {"emoji": "🌴", "categories": ["natural"], "name": "palm"}, + "tree": {"emoji": "🌳", "name": "tree", "categories": ["natural"]}, + "tree_with_leaf": {"name": "tree with leaf", "categories": ["natural"]} + }, + "leaf": { + "leaf_maple": {"categories": ["natural"], "name": "maple leaf", "emoji": "🍁"} + }, + "bush": {"categories": ["natural"], "name": "bush"}, + "cactus": {"categories": ["natural"], "name": "cactus", "emoji": "🌵"}, + "ear_botany": {"categories": ["natural"], "name": "ear (botany)"}, + "ear_botany_2": {"categories": ["natural"], "name": "ear (botany, 2)"}, + "flower_in_pot": {"name": "flower in pot"}, + "grapes": {"categories": ["natural"], "name": "three grapes"}, + "grapes_2": {"categories": ["natural"], "name": "eight grapes"}, + "grapes_3": {"categories": ["natural"], "name": "six grapes"}, + "oat": {"categories": ["natural"], "name": "oat"}, + "oat_2": {"categories": ["natural"], "name": "oat (2)"}, + "rape": {"categories": ["natural"], "name": "rape"}, + "rape_2": {"categories": ["natural"], "name": "rape (2)"}, + "sunflower": {"categories": ["natural"], "name": "sunflower"} + }, + "animal": { + "dog": {"emoji": "🐕", "name": "dog"}, + "dog_and_cross": {"name": "dog and cross"} + } + }, + "symbol": { + "abstract": { + "arrow_down": {"is_part": true, "name": "downwards arrow"}, + "arrow_left": {"is_part": true, "name": "leftwards arrow"}, + "arrow_right": {"is_part": true, "name": "rightwards arrow"}, + "arrow_right_short": {"is_part": true, "name": "short rightwards arrow"}, + "arrow_up": {"is_part": true, "name": "upwards arrow"}, + "at_in_square": {"emoji": "@", "name": "@ in square", "categories": ["letter"]}, + "circle_3": {"name": "circle 3 px"}, + "circle_4": {"name": "circle 4 px"}, + "circle_9": {"emoji": "🕳", "name": "circle 9 px"}, + "circle_11": {"name": "circle 11 px"}, + "circle_empty": {"name": "empty circle"}, + "clockwise": {"name": "clockwise arrow"}, + "counterclockwise": {"name": "counterclockwise arrow"}, + "default": {"name": "small square"}, + "default_small": {"name": "tiny square"}, + "electricity": {"emoji": "⚡", "name": "lightning"}, + "glider": {"name": "glider"}, + "light_left": {"is_part": true, "name": "leftward light"}, + "light_right": {"is_part": true, "name": "rightward light"}, + "sharing": {"name": "clockwise arrows"}, + "rectangle_vertical_rounded": {"name": "vertical rounded rectangle"}, + "rectangle_vertical_rounded_crossed": {"name": "crossed vertical rounded rectangle"}, + "triangle_small": {"emoji": "⛰️", "name": "small triangle"}, + "wave_left": {"is_part": true, "name": "leftward waves"}, + "wave_right": {"is_part": true, "name": "rightward waves"}, + "wlan": {"name": "wireless LAN"} + }, + "japan": { + "japan_castle": {"name": "Japanese map symbol for castle"}, + "japan_court": {"name": "Japanese map symbol for court house or building"}, + "japan_elementary_school": {"name": "Japanese map symbol for elementary school"}, + "japan_fire_station": {"name": "Japanese map symbol for fire station"}, + "japan_forest_service": {"name": "Japanese map symbol for forest service office"}, + "japan_historic": {"name": "Japanese map symbol for place of historic, cultural, or scenic interest"}, + "japan_koban": {"name": "Japanese map symbol for kōban"}, + "japan_police_station": {"name": "Japanese map symbol for police station"}, + "japan_public_health_center": {"name": "Japanese map symbol for public health center"}, + "japan_post": {"name": "Japanese map symbol for post office"}, + "japan_shinto_shrine": {"name": "Japanese map symbol for shinto shrine"}, + "japan_tv_tower": {"name": "Japanese map symbol for TV tower"}, + "japan_weather_station": {"name": "Japanese map symbol for meteorological observatory"}, + "japan_well": {"name": "Japanese map symbol for oil or gas well"} + }, + "letter": { + "digit_0": {"is_part": true, "categories": ["number"], "name": "digit 0"}, + "digit_1": {"is_part": true, "categories": ["number"], "name": "digit 1"}, + "digit_2": {"is_part": true, "categories": ["number"], "name": "digit 2"}, + "digit_3": {"is_part": true, "categories": ["number"], "name": "digit 3"}, + "digit_4": {"is_part": true, "categories": ["number"], "name": "digit 4"}, + "digit_5": {"is_part": true, "categories": ["number"], "name": "digit 5"}, + "digit_6": {"is_part": true, "categories": ["number"], "name": "digit 6"}, + "digit_7": {"is_part": true, "categories": ["number"], "name": "digit 7"}, + "digit_8": {"is_part": true, "categories": ["number"], "name": "digit 8"}, + "digit_9": {"is_part": true, "categories": ["number"], "name": "digit 9"}, + "dollar": {"name": "dollar sign"}, + "free": {"name": "free"}, + "h": {"emoji": "H", "name": "letter H", "categories": ["letter"]}, + "historic": {"name": "looped square"}, + "i": {"name": "letter i", "categories": ["letter"]}, + "i_in_square": {"name": "letter i in square"}, + "p": {"emoji": "P", "name": "letter P", "categories": ["letter"]}, + "p_small": {"name": "small letter P", "categories": ["letter"]}, + "pound": {"name": "pound sign"}, + "t": {"name": "letter T", "categories": ["letter"]}, + "x": {"emoji": "❌", "name": "letter X", "categories": ["letter"]}, + "x_4": {"emoji": "❌", "name": "X sign 4 px", "categories": ["letter"]}, + "x_5": {"emoji": "❌", "name": "X sign 5 px", "categories": ["letter"]}, + "y": {"name": "letter Y", "categories": ["letter"]} + }, + "religious": { + "baptist": {"name": "baptist symbol"}, + "crescent": {"emoji": "☪️", "name": "crescent"}, + "latin_cross": {"emoji": "✝️", "name": "Latin cross"}, + "cross_and_horizontal_bar": {"name": "cross and horizontal bar️"}, + "dharmachakra": {"name": "dharmachakra"}, + "greek_cross": {"name": "greek cross"}, + "greek_cross_in_box": {"name": "inverted Greek cross in box"}, + "orthodox": {"emoji": "☦️", "name": "orthodox cross"}, + "russian_orthodox_cross": {"name": "Russian Orthodox cross", "emoji": "☦"}, + "star_of_david": {"emoji": "✡️", "name": "Star of David"} + } + }, + "barrier": { + "chain_barrier": {"name": "chain barrier", "categories": ["barrier"]}, + "entrance": {"name": "door", "categories": ["barrier"]}, + "exit": {"name": "door with arrow", "categories": ["barrier"]}, + "door_with_keyhole": {"name": "door with keyhole", "categories": ["barrier"]}, + "garage_door": {"name": "garage door", "categories": ["barrier"]}, + "gate": {"name": "gate", "categories": ["barrier"]}, + "maze": {"name": "maze", "categories": ["barrier"]} + }, + "transport_items": { + "traffic_barrier": { + "block": {"categories": ["highway"], "name": "block"}, + "bollard": {"categories": ["highway"], "name": "bollard"}, + "bump": {"name": "speed bump", "categories": ["highway"]}, + "czech_hedgehog": {"name": "Czech hedgehog", "categories": ["barrier"]}, + "dip": {"name": "dip", "categories": ["highway"]}, + "double_dip": {"name": "double dip", "categories": ["highway"]}, + "dragons_teeth": {"name": "dragon's teeth", "categories": ["barrier"]}, + "hump": {"categories": ["highway"], "name": "speed hump"}, + "mini_bumps": {"name": "speed bumps", "categories": ["highway"]}, + "rumble_strip": {"name": "rumble strips", "categories": ["highway"]}, + "traffic_cushion": {"categories": ["highway"], "name": "speed cushion"}, + "traffic_table": {"categories": ["highway"], "name": "speed table"} + }, + "traffic_sigh": { + "bus_stop": {"emoji": "🚏", "categories": ["public transport"], "name": "big bus stop sign"}, + "bus_stop_sign": {"categories": ["public transport", "highway"], "name": "small bus stop sign"}, + "city_limit_sign": {"name": "city limit sign"}, + "no_traffic_signals": {"name": "crossed out traffic signals"}, + "speed_limit_mph": {"name": "speed limit box for mph"}, + "stop": {"emoji": ["🛑", "⛔️"], "categories": ["highway"], "name": "stop sign"}, + "traffic_signals": {"emoji": "🚦", "name": "traffic signals"} + }, + "bus_stop_bench": {"is_part": true, "name": "bus stop bench"}, + "bus_stop_shelter": {"is_part": true, "name": "bus stop shelter"}, + "charging_station": {"name": "charging station"} + }, + "sea": { + "anchor": {"emoji": "⚓", "name": "anchor"}, + "buoy": {"name": "buoy"} + }, + "material": { + "bricks": {"name": "brick wall"}, + "wood": {"emoji": "🪵", "name": "log"} + }, + "natural": { + "cave": {"categories": ["natural"], "name": "cave"}, + "cliff": {"categories": ["natural"], "name": "cliff"}, + "crater": {"categories": ["natural"], "name": "crater"}, + "ford": {"categories": ["natural"], "name": "ford"}, + "lava": {"name": "lava"}, + "saddle": {"name": "saddle"}, + "shield_volcano": {"name": "shield volcano", "categories": ["natural"], "emoji": "🌋"}, + "smoke": {"name": "smoke"}, + "smoke_2": {"name": "clouds"}, + "stone": {"emoji": "🪨", "categories": ["natural"], "name": "stone"}, + "stone_with_inscription": {"categories": ["natural"], "name": "stone with inscription"}, + "stratovolcano": {"name": "stratovolcano", "categories": ["natural"], "emoji": "🌋"}, + "volcanic_cone": {"name": "volcanic cone", "categories": ["natural"]}, + "waterfall": {"directed": "right", "name": "waterfall", "categories": ["natural"]} + }, + "space": { + "booster_landing": {"name": "landing booster", "categories": ["aeroway"]}, + "descent_stage": {"name": "descent stage"}, + "lander": {"name": "lander"}, + "lunokhod": {"name": "lunokhod"}, + "orbiter": {"name": "orbiter", "emoji": "🛰"}, + "probe": {"name": "probe"}, + "rocket_flying": {"emoji": "🚀", "name": "flying rocket", "categories": ["aeroway"]}, + "rocket_on_launch_pad": {"name": "rocket on launch pad", "categories": ["aeroway"]}, + "third_stage": {"name": "rocket third stage"} + }, + "kitchen": { + "amphora": {"name": "amphora"}, + "bbq": {"name": "BBQ"}, + "beer_mug": {"emoji": "🍺", "name": "beer mug"}, + "bottle": {"name": "bottle"}, + "bottle_and_wine_glass": {"name": "wine bottle and wine glass"}, + "cocktail_glass": {"name": "cocktail glass", "emoji": "🍸"}, + "cocktail_glass_with_straw": {"name": "cocktail glass with straw", "emoji": "🍸"}, + "coffee_cup": {"emoji": "☕", "name": "coffee cup"}, + "knives": {"name": "knives"}, + "pan": {"name": "pan"}, + "steak_and_fork": {"name": "steak and fork"} + }, + "instrument": { + "wrench": {"name": "wrench"} + }, + "food": { + "aseptic_carton": {"name": "aseptic carton"}, + "burger": {"emoji": "🍔", "name": "burger"}, + "cupcake": {"emoji": "🧁", "name": "cupcake"}, + "ice_cream": {"emoji": "🍨", "name": "ice cream"}, + "ice_cream_2": {"name": "ice cream cone"} + }, + "street": { + "playground": { + "hopscotch": {"name": "hopscotch"}, + "roundabout": {"name": "roundabout"}, + "sandpit": {"name": "sandpit", "categories": ["amenity"]}, + "seesaw": {"name": "seesaw", "categories": ["amenity"]}, + "slide": {"directed": "right", "name": "slide", "categories": ["amenity"], "emoji": "🛝"}, + "slide_and_water": {"categories": ["amenity"], "name": "water slide"}, + "street_cabinet": {"name": "street cabinet"}, + "street_lamp": {"name": "street lamp"}, + "toy_horse": {"name": "rocking horse"} + }, + "advertising_column": {"name": "advertising column", "categories": ["amenity"]}, + "bench": {"name": "bench", "categories": ["amenity"]}, + "bench_backrest": {"name": "bench with backrest", "categories": ["amenity"]}, + "bench_no_backrest": {"name": "bench without backrest", "categories": ["amenity"]}, + "bench_with_inscription": {"name": "bench with inscription", "categories": ["amenity"]}, + "bench_with_statue": {"name": "bench with statue", "categories": ["amenity"]}, + "bench_with_shelter": {"name": "bench with shelter", "categories": ["amenity"]}, + "bicycle_parking_wall_loops": {"name": "bicycle parking wall loops"}, + "bicycle_parking_rack": {"name": "bicycle parking rack"}, + "bicycle_parking_stand": {"name": "bicycle parking stand"}, + "billboard": {"name": "billboard", "categories": ["amenity"]}, + "fountain": {"emoji": "⛲", "name": "fountain", "categories": ["amenity"]}, + "fountain_bubbler": {"name": "bubbler fountain", "categories": ["amenity"]}, + "fountain_cascade": {"name": "cascade fountain", "categories": ["amenity"]}, + "fountain_roman_wolf": {"name": "Roman wolf fountain", "categories": ["amenity"]}, + "fountain_toret": {"name": "toret fountain", "categories": ["amenity"]}, + "table": {"name": "street table"}, + "table_and_two_chairs": {"name": "table and two chairs"} + }, + "indoor": { + "furniture": { + "bed": {"emoji": "🛌", "name": "bed"}, + "bed_and_roof": {"name": "bed and roof"}, + "bed_with_floor_and_ceiling": {"name": "bed with floor and ceiling"}, + "drawer": {"name": "drawer"}, + "two_beds": {"name": "two beds"} + }, + "curtains": {"name": "curtains"}, + "elevator": {"name": "elevator"}, + "fireplace": {"name": "fireplace"}, + "shower_head": {"name": "shower head"}, + "washing_machine": {"name": "washing machine"} + }, + "technique": { + "crane": {"name": "crane"}, + "crane_gantry": {"name": "gantry crane"}, + "crane_portal": {"name": "portal crane"}, + "crane_travel_lift": {"name": "travel lift"} + }, + "hand_items": { + "bag": {"name": "bag"}, + "book": {"emoji": "📕", "name": "book"}, + "books": {"emoji": "📚", "name": "books"} + }, + "other": { + "amusement_ride": {"emoji": "🎠", "name": "amusement ride"}, + "atm": {"name": "bill acceptor"}, + "awning": {"is_part": true, "name": "awning"}, + "beach": {"name": "umbrella and water"}, + "binoculars": {"name": "binocular"}, + "binoculars_on_pole": {"name": "binoculars on pole"}, + "bleachers": {"name": "bleachers"}, + "bottom_right_horizontal_line": {"is_part": true, "name": "bottom right horizontal line"}, + "briefcase": {"name": "briefcase"}, + "buffer_stop": {"name": "buffer stop"}, + "camp": {"name": "camp"}, + "cannon": {"directed": "right", "name": "cannon"}, + "card_and_dice": {"name": "card and dice"}, + "chimney": {"name": "chimney"}, + "clock": {"emoji": "⌚", "name": "clock"}, + "comb_and_scissors": {"name": "comb and scissors"}, + "credit_card": {"emoji": "💳", "name": "payment card"}, + "crossing": {"name": "zebra crossing"}, + "defibrillator": {"categories": ["emergency"], "name": "heart and lightning"}, + "diamond": {"emoji": "💎", "name": "diamond"}, + "digital_clock": {"name": "digital clock"}, + "drinking_water": {"emoji": "🚰", "name": "tap and glass"}, + "dumbbell": {"name": "dumbbell"}, + "envelope": {"emoji": "✉️", "name": "envelope"}, + "exchange": {"name": "exchange"}, + "film": {"emoji": "🎞️", "name": "film"}, + "fire_extinguisher": {"emoji": "🧯", "categories": ["emergency"], "name": "fire extinguisher"}, + "fire_hydrant": {"categories": ["emergency"], "name": "fire hydrant"}, + "fire_pit": {"name": "fire pit"}, + "fishing_angle": {"name": "fishing angle"}, + "flagpole": {"emoji": "🏴", "name": "flagpole"}, + "food_court": {"name": "food court"}, + "foot": {"emoji": "👣", "name": "footprint"}, + "frame": {"name": "picture frame"}, + "fuel_station": {"emoji": "⛽️", "name": "fuel station"}, + "garages": {"name": "car under roof"}, + "gavel": {"name": "gavel"}, + "gift": {"emoji": "🎁", "name": "gift box"}, + "globe": {"name": "globe"}, + "government": {"name": "building with dome and flag"}, + "guidepost": {"directed": "right", "name": "guidepost"}, + "guys": {"name": "guys"}, + "hi_fi": {"name": "Hi-Fi"}, + "human_on_ferry": {"name": "human over water"}, + "hunting_stand": {"name": "hunting stand"}, + "kerb": {"name": "kerb"}, + "key": {"is_part": true, "name": "key"}, + "kiosk": {"name": "kiosk"}, + "life_ring": {"categories": ["emergency"], "emoji": "🛟", "name": "life ring"}, + "lift_gate": {"name": "lift gate"}, + "lock": {"emoji": "🔒", "name": "closed lock"}, + "lock_unlocked": {"name": "opened lock"}, + "lock_with_keyhole": {"emoji": "🔒", "name": "closed lock with keyhole"}, + "lowered_kerb": {"name": "lowered kerb"}, + "main_entrance": {"name": "main entrance"}, + "manhole_drain": {"name": "drain manhole cover"}, + "marketplace": {"name": "marketplace"}, + "medicine_bottle": {"name": "medicine bottle"}, + "microphone": {"emoji": "🎤", "name": "microphone"}, + "milestone": {"name": "milestone"}, + "money": {"name": "banknotes"}, + "no_door": {"name": "doorway"}, + "no_foot": {"name": "crossed out footprint"}, + "no_wheelchair": {"name": "wheelchair with X sign"}, + "onion_roof_shape": {"name": "onion roof shape"}, + "pac_man": {"name": "Pac-Man"}, + "pergola": {"is_part": true, "name": "pergola"}, + "picture": {"name": "picture in frame"}, + "pipeline": {"name": "pipeline with valve"}, + "platform": {"is_part": true, "name": "platform"}, + "pole": {"name": "pole"}, + "power_generator": {"name": "power generator"}, + "prison": {"name": "bars"}, + "restaurant": {"emoji": "🍴", "name": "fork and knife"}, + "roof": {"is_part": true, "name": "roof"}, + "roof_and_walls": {"is_part": true, "name": "roof and wall"}, + "sheets": {"name": "two sheets"}, + "shelter": {"name": "shelter"}, + "shop_convenience": {"name": "convenience store"}, + "shower": {"name": "shower"}, + "side_mirror": {"name": "side mirror"}, + "signal": {"name": "railway signal"}, + "solar_panel": {"name": "solar panel"}, + "sos_phone": {"name": "phone with SOS", "categories": ["emergency"]}, + "stained_glass": {"name": "stained glass"}, + "staircase": {"name": "door with stairs"}, + "statue_exhibit": {"name": "indoor statue"}, + "supermarket_cart": {"name": "supermarket cart"}, + "support_column": {"is_part": true, "name": "support column"}, + "support_pole": {"is_part": true, "name": "support pole"}, + "support_wall": {"is_part": true, "name": "support wall"}, + "survey_point": {"name": "survey point"}, + "suspension_railway": {"name": "suspension railway", "emoji": "🚟"}, + "swimming_area": {"name": "pool ladder"}, + "tactile_paving": {"name": "tactile paving"}, + "ticket": {"emoji": ["🎫", "🎟"], "name": "ticket"}, + "toll_booth": {"name": "toll booth"}, + "tomb": {"emoji": "🪦", "name": "tomb"}, + "torch": {"name": "torch"}, + "toucan_crossing": {"name": "toucan crossing"}, + "transformer": {"name": "transformer"}, + "triangle_down_hollow": {"name": "hollow upside down triangle"}, + "turning_loop": {"name": "turning loop"}, + "turnstile": {"name": "turnstile"}, + "umbrella": {"is_part": true, "name": "umbrella"}, + "urban_tree_pot": {"is_part": true, "name": "tree pot"}, + "vanity_mirror": {"name": "vanity mirror"}, + "ventilation": {"name": "ventilation shaft"}, + "waving_flag": {"name": "waving flag"}, + "wretch_and_hammer": {"name": "wretch and hammer"} + }, + "body_part": { + "tooth": {"name": "tooth", "emoji": "🦷"} + }, + "clothes": { + "glasses": {"name": "glasses"}, + "shoe": {"name": "shoe"}, + "t_shirt": {"name": "T-shirt"}, + "t_shirt_and_scissors": {"name": "T-shirt and scissors"}, + "watches": {"name": "hand watch"} + }, + "sport": { + "bowling_ball": {"categories": ["sport"], "name": "bowling ball"}, + "golf_club_and_ball": {"categories": ["sport"], "name": "golf club and ball"}, + "golf_pin": {"categories": ["sport"], "name": "golf pin"}, + "golf_tee": {"categories": ["sport"], "name": "golf tee"}, + "horizontal_bar": {"name": "high horizontal bar", "categories": ["sport"]}, + "horizontal_ladder": {"name": "horizontal ladder", "categories": ["sport"]}, + "low_horizontal_bars": {"name": "low horizontal bars", "categories": ["sport"]}, + "rings": {"name": "gymnastic rings", "categories": ["sport"]}, + "sit_up": {"name": "incline bench", "categories": ["sport"]}, + "wall_bars": {"name": "wall bars", "categories": ["sport"]} + }, + "recycling": { + "recycling_container": {"name": "recycling container with wheel"}, + "waste_basket": {"name": "waste basket"}, + "waste_disposal": {"name": "recycling container"} + }, + "electronic_device": { + "cctv": {"directed": "right", "name": "wall CCTV camera"}, + "cctv_dome_wall": {"directed": "right", "name": "wall dome CCTV camera"}, + "cctv_dome_ceiling": {"directed": "right", "name": "ceiling dome CCTV camera"}, + "phone": {"name": "mobile phone"}, + "photo_camera": {"emoji": "📷", "name": "photo camera"}, + "telephone": {"emoji": "☎️", "name": "telephone"}, + "tv": {"name": "monitor"} + }, + "car_part": { + "tyre": {"name": "tyre"} + }, + "human": { + "massage": {"name": "massage"}, + "pole_dancer": {"name": "pole dancer"}, + "sauna": {"name": "sauna"}, + "two_people_together": {"emoji": "🧑‍🤝‍🧑", "name": "two people together"}, + "woman_and_man": {"emoji": "🚻", "name": "woman and man"} + }, + "tower": { + "city_gate": {"name": "city gate"}, + "diving_platform": {"name": "diving platform"}, + "diving_2_platforms": {"name": "diving tower with 2 platforms"}, + "diving_3_platforms": {"name": "diving tower with 3 platforms"}, + "diving_4_platforms": {"name": "diving tower with 4 platforms"}, + "lattice": {"name": "lattice mast"}, + "lattice_guyed": {"name": "guyed lattice mast"}, + "observatory": {"name": "observatory"}, + "pole_lamp": {"directed": "right", "name": "pole lamp"}, + "power_pole_1_level": {"name": "one-level transmission pole", "categories": ["power"]}, + "power_pole_2_level": {"name": "two-level transmission pole", "categories": ["power"]}, + "power_pole_3_level": {"name": "three-level transmission pole", "categories": ["power"]}, + "power_pole_4_level": {"name": "four-level transmission pole", "categories": ["power"]}, + "power_pole_asymmetric": {"name": "asymmetric transmission pole", "categories": ["power"]}, + "power_pole_asymmetric_armless": {"name": "asymmetric armless transmission pole", "categories": ["power"]}, + "power_pole_delta": {"name": "delta transmission pole", "categories": ["power"]}, + "power_pole_flag": {"name": "flag transmission pole", "categories": ["power"]}, + "power_pole_triangle": {"name": "triangle transmission pole", "categories": ["power"]}, + "power_pole_triangle_armless": {"name": "armless triangle transmission pole", "categories": ["power"]}, + "power_tower_1_level": {"name": "one-level transmission tower", "categories": ["power"]}, + "power_tower_2_level": {"name": "two-level transmission tower", "categories": ["power"]}, + "power_tower_3_level": {"name": "three-level transmission tower", "categories": ["power"]}, + "power_tower_4_level": {"name": "four-level transmission tower", "categories": ["power"]}, + "power_tower_asymmetric": {"name": "asymmetric transmission tower", "categories": ["power"]}, + "power_tower_barrel": {"name": "barrel transmission tower", "categories": ["power"]}, + "power_tower_delta": {"name": "delta transmission tower", "categories": ["power"]}, + "power_tower_delta_2_level": {"name": "delta two-level transmission tower", "categories": ["power"]}, + "power_tower_delta_3_level": {"name": "delta three-level transmission tower", "categories": ["power"]}, + "power_tower_donau": {"name": "donau transmission tower", "categories": ["power"]}, + "power_tower_donau_inverse": {"name": "inverse donau transmission tower", "categories": ["power"]}, + "power_tower_flag": {"name": "flag transmission tower", "categories": ["power"]}, + "power_tower_guyed_h_frame": {"name": "guyed h-frame transmission tower", "categories": ["power"]}, + "power_tower_h_frame": {"name": "h-frame transmission tower", "categories": ["power"]}, + "power_tower_h_frame_2_level": {"name": "h-frame two-level transmission tower", "categories": ["power"]}, + "power_tower_portal": {"name": "portal transmission tower", "categories": ["power"]}, + "power_tower_portal_2_level": {"name": "portal two-level transmission tower", "categories": ["power"]}, + "power_tower_portal_3_level": {"name": "portal three-level transmission tower", "categories": ["power"]}, + "power_tower_triangle": {"name": "triangle transmission tower", "categories": ["power"]}, + "power_tower_x_frame": {"name": "x-frame transmission tower", "categories": ["power"]}, + "power_tower_y_frame": {"name": "y-frame transmission tower", "categories": ["power"]}, + "stupa": {"name": "stupa"}, + "telescope_gamma": {"name": "gamma telescope"}, + "telescope_radio": {"name": "radio telescope"}, + "tower": {"name": "tower"}, + "tower_communication": {"name": "communication tower"}, + "tower_cooling": {"name": "cooling tower"}, + "tower_defensive": {"name": "defensive tower"}, + "tower_observation": {"name": "observation tower"}, + "tube": {"name": "mast"}, + "tube_guyed": {"name": "guyed mast"}, + "wind_turbine": {"name": "wind turbine"} + }, + "transport": { + "small": { + "bicycle": {"emoji": "🚲", "name": "bicycle"}, + "skateboard": {"emoji": "🛹", "name": "skateboard"}, + "wheelchair": {"emoji": "🦽", "name": "wheelchair"} + }, + "bus": {"emoji": "🚌", "name": "bus", "categories": ["public transport"]}, + "buses": {"name": "two buses", "categories": ["public transport"]}, + "car": {"name": "car"}, + "car_on_ferry": {"name": "car over water"}, + "caravan": {"name": "caravan"}, + "maglev": {"name": "maglev", "categories": ["public transport"]}, + "monorail": {"categories": ["public transport"], "name": "monorail"}, + "plane": {"emoji": "✈️", "name": "plane", "categories": ["aeroway", "public transport"]}, + "taxi": {"categories": ["public transport"], "name": "taxi sign"}, + "train": {"emoji": "🚆", "name": "train", "categories": ["public transport"]}, + "tram": {"emoji": "🚊", "name": "tram", "categories": ["public transport"]}, + "trolleybus": {"name": "trolleybus", "categories": ["public transport"]} + }, + "vending": { + "vending_angle": {"name": "vending machine with angle"}, + "vending_bottle": {"name": "vending machine with bottle"}, + "vending_candles": {"name": "vending machine with candle"}, + "vending_chemist": {"name": "vending machine with Greek cross"}, + "vending_drop": {"name": "vending machine with drop"}, + "vending_excrement_bag": {"name": "excrement bag vending"}, + "vending_machine": {"name": "vending machine"}, + "vending_p": {"name": "vending machine with letter P"}, + "vending_tickets": {"name": "ticket vending machine"} + }, + "historic": { + "memorial": {"categories": ["historic"], "name": "memorial"}, + "monument": {"name": "monument"}, + "obelisk": {"name": "obelisk", "categories": ["historic"]}, + "plaque": {"name": "plaque with inscription", "categories": ["historic"]}, + "statue": {"name": "statue", "categories": ["historic"]} + }, + "parts": { + "dish_antenna_left": {"is_part": true, "name": "leftward dish antenna"}, + "dish_antenna_right": {"is_part": true, "name": "rightward dish antenna"}, + "siren_left": {"is_part": true, "name": "leftward siren"}, + "siren_right": {"is_part": true, "name": "rightward siren"} } -} \ No newline at end of file +} diff --git a/map_machine/icons/icons.svg b/map_machine/icons/icons.svg index 305ba8b..1469ef8 100644 --- a/map_machine/icons/icons.svg +++ b/map_machine/icons/icons.svg @@ -1,156 +1,159 @@ + height="800" + width="800" + inkscape:export-filename="/Users/enzet/program/map-machine/map_machine/icons/icons.png" + inkscape:export-xdpi="960" + inkscape:export-ydpi="960" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + showborder="true" + inkscape:showpageshadow="false" + height="800px"> + empopacity="0.50196078" + spacingy="0.5" + spacingx="0.5" /> image/svg+xml - @@ -561,9 +560,240 @@ inkscape:label="main" id="layer1" style="display:inline"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + travel lift + + + gantry crane + + + + + + + + @@ -601,7 +831,7 @@ @@ -615,25 +845,27 @@ sodipodi:type="arc" d="m 332.38909,234.38909 a 5.5,5.5 0 0 1 -7.77818,0 5.5,5.5 0 0 1 0,-7.77818 L 328.5,230.5 Z" id="circle7188" - style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> + inkscape:label="#rect9680" + inkscape:connector-curvature="0" /> @@ -641,22 +873,22 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path13103" - d="m 362.5,74.5 1,3" + d="m 362.5,73.5 1,3" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -681,11 +913,11 @@ sodipodi:nodetypes="cssssssc" inkscape:connector-curvature="0" id="path22149" - d="m 46,107.5 c 0,0.277 -0.223,0.5 -0.5,0.5 l -7,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-1 c 0,-0.277 0.225785,-0.53917 0.5,-0.5 l 7,1 c 0.27422,0.0392 0.5,0.223 0.5,0.5" - style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:0.2" /> + d="m 126,587.5 c 0,0.277 -0.223,0.5 -0.5,0.5 h -7 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -1 c 0,-0.277 0.22578,-0.53917 0.5,-0.5 l 7,1 c 0.27422,0.0392 0.5,0.223 0.5,0.5" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -737,7 +969,7 @@ @@ -769,7 +1001,7 @@ y="337.51932" rx="0.5" ry="0.45580599" - transform="matrix(0.96592583,-0.25881905,0.25881905,0.96592583,0,0)" /> + transform="rotate(-15)" /> @@ -810,9 +1042,9 @@ inkscape:connector-curvature="0" id="path9613" d="m 155,513.5 -2,2" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" /> @@ -838,7 +1070,7 @@ d="M 546,8 V 5 l 1,-1 h 7 2 l -3,3 v 2 h -6 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + d="m 534.5,233 v 2 3" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> @@ -997,21 +1229,21 @@ sodipodi:nodetypes="ccsc" inkscape:connector-curvature="0" id="path5183" - d="m 542.5,232.5 c 0,-0.78519 0,-0.72909 0,-1 0,-0.55228 -0.44772,-1 -1,-1 l -1,0" - style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;opacity:0.2" /> + d="m 542.5,232.5 c 0,-0.78519 0,-0.72909 0,-1 0,-0.55228 -0.44772,-1 -1,-1 h -1" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;marker:none;enable-background:accumulate" /> + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;marker:none;enable-background:accumulate" /> @@ -1049,16 +1281,16 @@ sodipodi:nodetypes="czccsc" inkscape:connector-curvature="0" id="path4358" - d="m 17,75 c 0,0 1,0 2,-1 1,-1 2,-1 3,0 1,-1 3,-1 4,0 1,-1 2,-1 3,0 0.18896,0.18896 2,1 2,1" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" /> + d="m 17,73 c 0,0 1,0 2,-1 1,-1 2,-1 3,0 1,-1 3,-1 4,0 1,-1 2,-1 3,0 0.18896,0.18896 2,1 2,1" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="M 3.5,164 5,162.5 h 7 l 1.5,1.5" + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;marker:none;enable-background:accumulate" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" /> @@ -1159,7 +1391,7 @@ style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -1167,7 +1399,7 @@ sodipodi:nodetypes="ssss" inkscape:connector-curvature="0" id="path4360" - d="m 19,74 c -2,-1 0,-5 5,-5 5,0 7,4 5,5 -2,1 -8,1 -10,0 z" + d="m 19,72 c -2,-1 0,-5 5,-5 5,0 7,4 5,5 -2,1 -8,1 -10,0 z" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> greek cross + id="title5984">Greek cross - - + y="391" /> coffee cup @@ -1301,8 +1521,8 @@ + d="m 146.5,397.5 h 4 v -9 c -3,0 -1,2 -4,2 z" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> @@ -1323,65 +1543,65 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path3127" - d="m 148.5,389.5 0,7.5" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" /> + d="M 148.5,389.5 V 397" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 154.5,389.5 v 7.5 0" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> train @@ -1404,21 +1624,21 @@ + d="m 264,370 -2,2 v 7 h 4 v -7 z m -4,10 -1,1 v 1 h 10 v -1 l -1,-1 z" + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.94;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;enable-background:accumulate" /> statue @@ -1477,13 +1697,13 @@ inkscape:connector-curvature="0" inkscape:label="#rect4373" id="tomb" - d="m 294,372 c -1.108,0 -2,0.892 -2,2 l 0,3 0,2 0,2 2,0 3,0 2,0 0,-2 0,-2 0,-3 c 0,-1.108 -0.892,-2 -2,-2 z m -4,10 0,1 11,0 0,-1 z" + d="m 294,372 c -1.108,0 -2,0.892 -2,2 v 3 2 2 h 2 3 2 v -2 -2 -3 c 0,-1.108 -0.892,-2 -2,-2 z m -4,10 v 1 h 11 v -1 z" style="fill:#000000;fill-opacity:1;stroke:none" /> plaque with inscription @@ -1491,7 +1711,7 @@ turnstile @@ -1503,7 +1723,7 @@ width="2" height="7.9999995" x="113" - y="389" + y="388" ry="0.49999997" /> + d="m 244.5,82 c -0.277,0 -0.5,0.223 -0.5,0.5 v 5 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.82843,0 1.5,0.67157 1.5,1.5 v 2.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 91 c 0,-0.82843 0.67157,-1.5 1.5,-1.5 0.277,0 0.5,-0.223 0.5,-0.5 v -1.5 -5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 -0.277,0 -0.5,0.223 -0.5,0.5 V 87 h -1 v -4.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 -0.277,0 -0.5,0.223 -0.5,0.5 V 87 h -1 v -4.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 6.5,0 c -0.554,0 -1,0.446 -1,1 v 7 c 0,0.554 0.446,1 1,1 v 2.5 c 0,0.277 0.23166,0.5 0.5,0.5 0.26834,0 0.5,-0.223 0.5,-0.5 V 90 84.5 83 c 0,-0.554 -0.446,-1 -1,-1 z" + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;stroke:none;stroke-width:0.3;marker:none;enable-background:accumulate" /> + d="m 107,370 v 1 c 0,0.554 -0.446,1 -1,1 h -1 c -0.54314,0 -0.98298,-0.42989 -1,-0.96875 -0.63691,0.0664 -1.10058,0.25683 -1.40625,0.5625 C 102.2116,371.9759 102,372.58333 102,373.5 v 9.5 h -1 v -9.5 c 0,-1.08333 0.2884,-1.9759 0.90625,-2.59375 C 102.5241,370.2884 103.41667,370 104.5,370 h 0.5 1 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;marker:none;enable-background:accumulate"> pole lamp + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 647.5,327 c 0,0 0,0 0,0 0,-0.82843 0.67157,-1.5 1.5,-1.5 0.82843,0 1.5,0.67157 1.5,1.5 v 1" + style="opacity:0.2;fill:none;stroke:#000000" /> @@ -1788,10 +2008,10 @@ ry="0.75" y="516" x="132" - height="6" + height="5" width="8" id="rect4307" - style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none" /> + style="fill:none;stroke:#000000;stroke-width:0.0999996;stroke-miterlimit:4;stroke-dasharray:none" /> - - - - - - - - bus - - - trolleybus - tree @@ -1983,11 +2131,11 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path4462" - d="m 24,207 0,-9" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" /> + d="m 24,207 v -9" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" /> @@ -1995,17 +2143,17 @@ inkscape:connector-curvature="0" id="path4499" d="m 27,202 c -2,0 -3,1 -3,3" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" sodipodi:nodetypes="cc" /> @@ -2013,23 +2161,23 @@ inkscape:connector-curvature="0" id="path4505" d="m 24,200 c 0,-2 -1,-3 -3,-3" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" sodipodi:nodetypes="cc" /> @@ -2037,41 +2185,41 @@ inkscape:connector-curvature="0" id="path4515" d="m 21,194.5 c 2,0 3,1 3,3" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" sodipodi:nodetypes="cc" /> + d="m 7.5,207 v -3 l -2,-4 -4,-3" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 7.5,207 v -3 l 2,-4 4,-3 v 0 0" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 5.5,200 v -5" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 9.5,200 v -5" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> <path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path3460" - d="m 66,549 3,0 c 0,-2 5,-2 5,0 l 3,0 c 0,-4 -11,-4 -11,0 z" + d="m 66,549 h 3 c 0,-2 5,-2 5,0 h 3 c 0,-4 -11,-4 -11,0 z" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <path style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 66,550 c 0,0.554 0.446,1 1,1 l 1,0 c 0.554,0 1,-0.446 1,-1 z" + d="m 66,550 c 0,0.554 0.446,1 1,1 h 1 c 0.554,0 1,-0.446 1,-1 z" id="path4264" inkscape:connector-curvature="0" /> <path inkscape:connector-curvature="0" id="path4266" - d="m 74,550 c 0,0.554 0.446,1 1,1 l 1,0 c 0.554,0 1,-0.446 1,-1 z" + d="m 74,550 c 0,0.554 0.446,1 1,1 h 1 c 0.554,0 1,-0.446 1,-1 z" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <path inkscape:label="#path3178" inkscape:connector-curvature="0" id="needleleaved_tree" - d="m 72,211 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 4,0 0,3 2,0 0,-3 4,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" + d="m 72,211 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 4 v 3 h 2 v -3 h 4 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" style="fill:#000000;stroke:none" sodipodi:nodetypes="cccccccccccccccc"> <title @@ -2214,7 +2362,7 @@ <path inkscape:connector-curvature="0" id="path4418" - d="m 728,482 0,13" + d="m 728,482 v 13" style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" @@ -2268,17 +2416,11 @@ inkscape:connector-curvature="0" inkscape:label="#rect4444" id="clock" - d="m 277,371 c -0.554,0 -1,0.446 -1,1 l 0,7 c 0,0.554 0.446,1 1,1 l 7,0 c 0.554,0 1,-0.446 1,-1 l 0,-7 c 0,-0.554 -0.446,-1 -1,-1 z m 3.5,1 c 1.933,0 3.5,1.567 3.5,3.5 0,1.933 -1.567,3.5 -3.5,3.5 -1.933,0 -3.5,-1.567 -3.5,-3.5 0,-1.933 1.567,-3.5 3.5,-3.5 z m 0,1 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,2 c 0,0.277 0.223,0.5 0.5,0.5 l 2,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -1.5,0 0,-1.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" - style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;stroke:none;stroke-width:0.30000001;marker:none;enable-background:accumulate"> + d="m 277,371 c -0.554,0 -1,0.446 -1,1 v 7 c 0,0.554 0.446,1 1,1 h 7 c 0.554,0 1,-0.446 1,-1 v -7 c 0,-0.554 -0.446,-1 -1,-1 z m 3.5,1 c 1.933,0 3.5,1.567 3.5,3.5 0,1.933 -1.567,3.5 -3.5,3.5 -1.933,0 -3.5,-1.567 -3.5,-3.5 0,-1.933 1.567,-3.5 3.5,-3.5 z m 0,1 c -0.277,0 -0.5,0.223 -0.5,0.5 v 2 c 0,0.277 0.223,0.5 0.5,0.5 h 2 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 H 281 v -1.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;stroke:none;stroke-width:0.3;marker:none;enable-background:accumulate"> <title id="title5988">clock - + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -2489,12 +2631,12 @@ sodipodi:cy="69.5" sodipodi:cx="599.5" id="path4457" - style="fill:none;stroke:#000000;stroke-linecap:round;opacity:0.2" + style="opacity:0.2;fill:none;stroke:#000000;stroke-linecap:round" sodipodi:type="arc" sodipodi:arc-type="arc" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" /> @@ -2530,7 +2672,7 @@ inkscape:connector-curvature="0" id="path4468" d="m 598,77 h 1.5" - style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.2" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" /> t-shirt - - - - - - - - - + style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> beer mug @@ -2803,7 +2882,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect4546" id="bench_backrest" - d="m 51.5,373 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,0.5 c -0.554,0 -1,0.446 -1,1 l 0,1 c 0,0.554 0.446,1 1,1 l 0,1 -0.5,0 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 l 0.5,0 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 8,0 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 0.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -0.5,0 0,-1 c 0.554,0 1,-0.446 1,-1 l 0,-1 c 0,-0.554 -0.446,-1 -1,-1 l 0,-0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 -0.277,0 -0.5,0.223 -0.5,0.5 l 0,0.5 -8,0 0,-0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 0.5,4 8,0 0,1 -8,0 z" + d="m 339.5,467 c -0.277,0 -0.5,0.223 -0.5,0.5 v 0.5 c -0.554,0 -1,0.446 -1,1 v 1 c 0,0.554 0.446,1 1,1 v 1 h -0.5 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 h 0.5 v 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 474 h 8 v 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 474 h 0.5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 H 349 v -1 c 0.554,0 1,-0.446 1,-1 v -1 c 0,-0.554 -0.446,-1 -1,-1 v -0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 -0.277,0 -0.5,0.223 -0.5,0.5 v 0.5 h -8 v -0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 0.5,4 h 8 v 1 h -8 z" style="fill:#000000;stroke:none"> bench with backrest @@ -2812,7 +2891,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect4554" id="bench_no_backrest" - d="m 34.5,378 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 l 0.5,0 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 8,0 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 0.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" + d="m 322.5,470 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 h 0.5 v 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 472 h 8 v 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 472 h 0.5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" style="fill:#000000;stroke:none"> bench without backrest @@ -2830,7 +2909,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect4564" id="bus_stop" - d="m 5,530 c -0.554,0 -1,0.52311 -1,1.15625 l 0,5.6875 C 4,537.47689 4.446,538 5,538 l 2,0 0,3 -1.5,0 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 4,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -1.5,0 0,-3 2,0 c 0.554,0 1,-0.52311 1,-1.15625 l 0,-5.6875 C 11,530.52311 10.554,530 10,530 Z m 1,1 3,0 c 0.554,0 1,0.5575 1,1.25 l 0,2.5 C 10,535.4425 9.554,536 9,536 l -3,0 c -0.554,0 -1,-0.5575 -1,-1.25 l 0,-2.5 C 5,531.5575 5.446,531 6,531 Z m 0.5,1 C 6.223,532 6,532.223 6,532.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 l 2,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 C 9,532.223 8.777,532 8.5,532 Z" + d="m 5,530 c -0.554,0 -1,0.52311 -1,1.15625 v 5.6875 C 4,537.47689 4.446,538 5,538 h 2 v 3 H 5.5 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 4 C 9.777,542 10,541.777 10,541.5 10,541.223 9.777,541 9.5,541 H 8 v -3 h 2 c 0.554,0 1,-0.52311 1,-1.15625 v -5.6875 C 11,530.52311 10.554,530 10,530 Z m 1,1 h 3 c 0.554,0 1,0.5575 1,1.25 v 2.5 C 10,535.4425 9.554,536 9,536 H 6 c -0.554,0 -1,-0.5575 -1,-1.25 v -2.5 C 5,531.5575 5.446,531 6,531 Z m 0.5,1 C 6.223,532 6,532.223 6,532.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 h 2 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1 C 9,532.223 8.777,532 8.5,532 Z" style="fill:#000000;stroke:none" /> - bench @@ -3179,7 +3251,7 @@ inkscape:connector-curvature="0" inkscape:label="#path4386" id="bench_with_shelter" - d="m 23.875,369.5 a 0.50005,0.50005 0 0 0 -0.0312,0.0312 l -5.5,2 a 0.50005,0.50005 0 1 0 0.3125,0.9375 L 24,370.53125 l 5.34375,1.9375 a 0.50005,0.50005 0 1 0 0.3125,-0.9375 l -5.5,-2 a 0.50005,0.50005 0 0 0 -0.21875,-0.0312 0.50005,0.50005 0 0 0 -0.0625,0 z M 19.5,377 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,0.5 -0.5,0 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 l 0.5,0 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 8,0 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 0.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -0.5,0 0,-0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 -0.277,0 -0.5,0.223 -0.5,0.5 l 0,0.5 -8,0 0,-0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" + d="m 311.875,465.5 a 0.50005,0.50005 0 0 0 -0.0312,0.0312 l -5.5,2 a 0.50005,0.50005 0 1 0 0.3125,0.9375 l 5.3437,-1.93745 5.34375,1.9375 a 0.50005,0.50005 0 1 0 0.3125,-0.9375 l -5.5,-2 a 0.50005,0.50005 0 0 0 -0.21875,-0.0312 0.50005,0.50005 0 0 0 -0.0625,0 z M 307.5,473 c -0.277,0 -0.5,0.223 -0.5,0.5 v 0.5 h -0.5 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 h 0.5 v 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 476 h 8 v 1.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 476 h 0.5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 H 317 v -0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 -0.277,0 -0.5,0.223 -0.5,0.5 v 0.5 h -8 v -0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" style="fill:#000000;stroke:none" /> @@ -3289,20 +3361,20 @@ + d="m 162,389 v 6 h 5 v -8 c -3,0 -2,2 -5,2 z m 6,-2 v 8 h 5 v -6 c -3,0 -2,-2 -5,-2 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" /> + d="m 392,370 c -2.75,0 -6,1 -6,3 h 3 c 0,-2 6,-2 6,0 h 3 c 0,-2 -3.25,-3 -6,-3 z m -1.09375,3 c -0.38136,0.0415 -0.72609,0.32238 -0.84375,0.6875 l -0.875,2.59375 -1.625,0.8125 C 387.22529,377.25783 386.99739,377.625 387,378 v 1 2 c 5e-5,0.52358 0.47642,0.99995 1,1 h 8 c 0.52358,-5e-5 0.99995,-0.47642 1,-1 v -2 -1 c 0.003,-0.375 -0.22529,-0.74217 -0.5625,-0.90625 l -1.625,-0.8125 -0.875,-2.59375 C 393.80828,373.29468 393.4135,373.00518 393,373 h -2 c -0.0312,-10e-4 -0.0625,-10e-4 -0.0937,0 z M 386,374 c 0,0.554 0.446,1 1,1 h 1 c 0.554,0 1,-0.446 1,-1 z m 9,0 c 0,0.554 0.446,1 1,1 h 1 c 0.554,0 1,-0.446 1,-1 z m -3,2 c 1.10457,0 2,0.89543 2,2 0,1.10457 -0.89543,2 -2,2 -1.10457,0 -2,-0.89543 -2,-2 0,-1.10457 0.89543,-2 2,-2 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;enable-background:accumulate" /> door @@ -3388,7 +3460,7 @@ width="6" height="10" x="21" - y="387" + y="386" rx="0.5" ry="0.5" /> + d="m 21.5,397.5 h 3 v -3 h 3" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + d="m 36.5,398.5 h 2 v -2 h 2 v -2 h 2" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> door with stairs @@ -3471,7 +3543,7 @@ inkscape:connector-curvature="0" inkscape:label="#path4508" id="main_entrance" - d="m 54.5,401 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.27614 -0.22386,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 2,0 3,0 2,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -1,0 c -0.27614,0 -0.5,-0.22386 -0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m -1,3 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,9 c 0,0.277 0.223,0.5 0.5,0.5 l 5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-9 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 1,4 2,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1.5,0 0,0.5 c 0,0.277 -0.223,0.5 -0.5,0.5 -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-1 c 0,-0.277 0.223,-0.5 0.5,-0.5 z" + d="m 54.5,401 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.27614 -0.22386,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 2 3 2 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 h -1 c -0.27614,0 -0.5,-0.22386 -0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m -1,3 c -0.277,0 -0.5,0.223 -0.5,0.5 v 9 c 0,0.277 0.223,0.5 0.5,0.5 h 5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -9 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 1,4 h 2 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 H 55 v 0.5 c 0,0.277 -0.223,0.5 -0.5,0.5 -0.277,0 -0.5,-0.223 -0.5,-0.5 v -1 c 0,-0.277 0.223,-0.5 0.5,-0.5 z" style="fill:#000000;stroke:none" /> + d="m 86.5,360.5 h 3 l 1.5,-5 h -6 z" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> @@ -3615,24 +3687,24 @@ inkscape:connector-curvature="0" inkscape:label="#rect4842" id="street_lamp" - d="m 86.40625,369 c -0.0682,0.0176 -0.13253,0.0498 -0.1875,0.0937 l -3,2 C 82.88033,371.33654 83.08358,371.99147 83.5,372 l 1.125,0 1.40625,4.65625 c 0.0646,0.19641 0.262,0.34116 0.46875,0.34375 l 0.5,0 0,5.5 c 0,0.277 0.223,0.5 0.5,0.5 l 1,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-5.5 0.5,0 c 0.20675,-0.003 0.40414,-0.14734 0.46875,-0.34375 L 91.375,372 92.5,372 c 0.41642,-0.008 0.61967,-0.66346 0.28125,-0.90625 l -3,-2 c -0.0815,-0.0585 -0.18094,-0.0917 -0.28125,-0.0937 l -3,0 c -0.0312,-0.003 -0.0626,-0.003 -0.0937,0 z m -0.75,3 4.6875,0 -1.21875,4 -0.625,0 -1,0 -0.625,0 z" + d="m 86.40625,369 c -0.0682,0.0176 -0.13253,0.0498 -0.1875,0.0937 l -3,2 C 82.88033,371.33654 83.08358,371.99147 83.5,372 h 1.125 l 1.40625,4.65625 c 0.0646,0.19641 0.262,0.34116 0.46875,0.34375 H 87 v 5.5 c 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 V 377 h 0.5 c 0.20675,-0.003 0.40414,-0.14734 0.46875,-0.34375 L 91.375,372 H 92.5 c 0.41642,-0.008 0.61967,-0.66346 0.28125,-0.90625 l -3,-2 c -0.0815,-0.0585 -0.18094,-0.0917 -0.28125,-0.0937 h -3 c -0.0312,-0.003 -0.0626,-0.003 -0.0937,0 z m -0.75,3 h 4.6875 l -1.21875,4 h -0.625 -1 -0.625 z" style="fill:#000000;stroke:none" /> + d="m 105,69 4,-4" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> cocktail glass with straw @@ -3651,9 +3723,9 @@ inkscape:label="#path4874" sodipodi:nodetypes="ccccccc" inkscape:connector-curvature="0" - id="electricity" - d="m 57,562 -5,7 4,0 -1,5 5,-7 -4,0 z" - style="fill:#000000;stroke:none" /> + id="path4874" + d="m 57,562 -5,7 h 4 l -1,5 5,-7 h -4 z" + style="fill:#0000ff;stroke:none" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" /> + d="m 210.34375,180 c -0.0741,0.0356 -0.13911,0.0898 -0.1875,0.15625 l -1,1 c -0.25698,0.15718 -0.30568,0.56716 -0.0927,0.78017 0.21301,0.21301 0.62299,0.16431 0.78017,-0.0927 L 210.6875,181 h 10.625 l 0.84375,0.84375 c 0.15718,0.25698 0.56716,0.30568 0.78017,0.0927 0.21301,-0.21301 0.16431,-0.62299 -0.0927,-0.78017 l -1,-1 C 221.75512,180.06238 221.62896,180.00509 221.5,180 h -11 c -0.0312,-0.003 -0.0626,-0.003 -0.0937,0 -0.0208,-0.001 -0.0417,-0.001 -0.0625,0 z m 4.56245,3 c -0.19329,0.0192 -0.38045,0.0963 -0.53125,0.21875 L 212.15625,185 H 211 c -0.52358,5e-5 -0.99995,0.47642 -1,1 v 1 c 5e-5,0.52358 0.47642,0.99995 1,1 0,-1.108 0.892,-2 2,-2 1.108,0 2,0.892 2,2 h 2 c 0,-1.108 0.892,-2 2,-2 1.108,0 2,0.892 2,2 0.52358,-5e-5 0.99995,-0.47642 1,-1 v -1 c -5e-5,-0.52358 -0.47642,-0.99995 -1,-1 h -0.5625 l -1.71875,-1.71875 C 218.52897,183.09755 218.26406,182.99388 218,183 h -3 c -0.0312,-0.001 -0.0625,-0.001 -0.0937,0 z M 213,187 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 0,-0.554 -0.446,-1 -1,-1 z m 6,0 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 0,-0.554 -0.446,-1 -1,-1 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;enable-background:accumulate" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> - @@ -3982,7 +4049,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect3847" id="betula" - d="m 151,209 c -0.554,0 -1,0.446 -1,1 -0.554,0 -1,0.446 -1,1 l 0,1 c -0.554,0 -1,0.446 -1,1 l 0,2 c 0,0.554 0.446,1 1,1 l 0,1 c 0,0.554 0.446,1 1,1 l 1,0 0,3 0.5,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -0.5,0 0,1 2,0 0,-3 -0.5,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 l 0.5,0 0,-1 1,0 c 0.554,0 1,-0.446 1,-1 l 0,-1 c 0.554,0 1,-0.446 1,-1 l 0,-2 c 0,-0.554 -0.446,-1 -1,-1 l 0,-1 c 0,-0.554 -0.446,-1 -1,-1 0,-0.554 -0.446,-1 -1,-1 z" + d="m 151,209 c -0.554,0 -1,0.446 -1,1 -0.554,0 -1,0.446 -1,1 v 1 c -0.554,0 -1,0.446 -1,1 v 2 c 0,0.554 0.446,1 1,1 v 1 c 0,0.554 0.446,1 1,1 h 1 v 3 h 0.5 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 H 151 v 1 h 2 v -3 h -0.5 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 h 0.5 v -1 h 1 c 0.554,0 1,-0.446 1,-1 v -1 c 0.554,0 1,-0.446 1,-1 v -2 c 0,-0.554 -0.446,-1 -1,-1 v -1 c 0,-0.554 -0.446,-1 -1,-1 0,-0.554 -0.446,-1 -1,-1 z" style="fill:#000000;stroke:none"> betula @@ -4044,37 +4111,37 @@ + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + width="7" /> + d="m 660.5,147 c -0.277,0 -0.5,0.223 -0.5,0.5 v 8 c 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 V 153 H 664.84375 665 c 1.64501,0 3,-1.35499 3,-3 0,-1.64501 -1.35499,-3 -3,-3 H 664.8125 661.5 Z m 1.5,2 H 664.84375 665 c 0.56413,0 1,0.43587 1,1 0,0.56413 -0.43587,1 -1,1 h -0.0937 -2.90625 v -2 z" + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;stroke:none;stroke-width:0.3;marker:none;enable-background:accumulate"> letter P @@ -4249,7 +4316,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect3728" id="vending_machine" - d="M 4.5,242 C 4.223,242 4,242.223 4,242.5 l 0,11 c 0,0.277 0.223,0.5 0.5,0.5 l 7,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-11 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 1,3 3,0 c 0.277,0 0.5,0.223 0.5,0.5 l 0,5 c 0,0.277 -0.223,0.5 -0.5,0.5 l -3,0 C 5.223,251 5,250.777 5,250.5 l 0,-5 C 5,245.223 5.223,245 5.5,245 Z m 5,0 c 0.277,0 0.5,0.223 0.5,0.5 l 0,1 c 0,0.277 -0.223,0.5 -0.5,0.5 -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-1 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m -4.5,3 0,1 c 0,0.55228 0.44772,1 1,1 0.55228,0 1,-0.44772 1,-1 l 0,-1 z" + d="M 4.5,242 C 4.223,242 4,242.223 4,242.5 v 11 c 0,0.277 0.223,0.5 0.5,0.5 h 7 c 0.277,0 0.5,-0.223 0.5,-0.5 v -11 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 1,3 h 3 c 0.277,0 0.5,0.223 0.5,0.5 v 5 c 0,0.277 -0.223,0.5 -0.5,0.5 h -3 C 5.223,251 5,250.777 5,250.5 v -5 C 5,245.223 5.223,245 5.5,245 Z m 5,0 c 0.277,0 0.5,0.223 0.5,0.5 v 1 c 0,0.277 -0.223,0.5 -0.5,0.5 -0.277,0 -0.5,-0.223 -0.5,-0.5 v -1 c 0,-0.277 0.223,-0.5 0.5,-0.5 z M 6,248 v 1 c 0,0.55228 0.44772,1 1,1 0.55228,0 1,-0.44772 1,-1 v -1 z" style="fill:#000000;stroke:none" /> @@ -4445,7 +4512,7 @@ - i letter - + id="i" + d="m 535.5,466 c -0.277,0 -0.5,0.223 -0.5,0.5 v 2 c 0,0.277 0.223,0.5 0.5,0.5 h 2 c 0.277,0 0.5,-0.223 0.5,-0.5 v -2 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m -1,5 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 0.5 v 5 h -0.5 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 4 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 H 538 v -5.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" + style="fill:#000000;stroke:none" /> + id="i_in_square" + d="m 549,467 c -0.554,0 -1,0.446 -1,1 v 7 c 0,0.554 0.446,1 1,1 h 8 c 0.554,0 1,-0.446 1,-1 v -7 c 0,-0.554 -0.446,-1 -1,-1 z m 3.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 v 1 c 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -1 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m -0.5,3 h 0.5 1 c 0.277,0 0.5,0.223 0.5,0.5 v 2.5 c 0.2683,0 0.5,0.23166 0.5,0.5 0,0.26834 -0.2317,0.5 -0.5,0.5 h -2 c -0.2683,0 -0.5,-0.23166 -0.5,-0.5 0,-0.26834 0.2317,-0.5 0.5,-0.5 v -2 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z" + style="fill:#000000;fill-opacity:1;stroke:none"> + letter i in square + <rect rx="0.484375" ry="0.5" - y="460" - x="7" + y="519" + x="503" height="1.5" width="1" id="rect8657" @@ -4910,15 +4977,15 @@ id="rect8659" width="1" height="1.5" - x="9" - y="460" + x="505" + y="519" ry="0.5" rx="0.484375" /> <rect rx="0.484375" ry="0.5" - y="460" - x="11" + y="519" + x="507" height="1.5" width="1" id="rect8661" @@ -4927,7 +4994,7 @@ inkscape:connector-curvature="0" inkscape:label="#path8663" id="tactile_paving" - d="M 3.48438,476 C 3.21603,476 3,476.223 3,476.5 l 0,0.5 -1.5,0 c -0.27614,0 -0.5,0.22386 -0.5,0.5 0,0.27614 0.22386,0.5 0.5,0.5 l 12,0 c 0.27614,0 0.5,-0.22386 0.5,-0.5 0,-0.27614 -0.22386,-0.5 -0.5,-0.5 l -1.5,0 0,-0.5 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 l -0.0312,0 C 11.21603,476 11,476.223 11,476.5 l 0,0.5 -1,0 0,-0.5 C 10,476.223 9.78397,476 9.51562,476 l -0.0312,0 C 9.21603,476 9,476.223 9,476.5 l 0,0.5 -1,0 0,-0.5 C 8,476.223 7.78397,476 7.51562,476 l -0.0312,0 C 7.21603,476 7,476.223 7,476.5 l 0,0.5 -1,0 0,-0.5 C 6,476.223 5.78397,476 5.51562,476 l -0.0312,0 C 5.21603,476 5,476.223 5,476.5 l 0,0.5 -1,0 0,-0.5 C 4,476.223 3.78397,476 3.51562,476 l -0.0312,0 z" + d="M 499.48438,535 C 499.21603,535 499,535.223 499,535.5 v 0.5 h -1.5 c -0.27614,0 -0.5,0.22386 -0.5,0.5 0,0.27614 0.22386,0.5 0.5,0.5 h 12 c 0.27614,0 0.5,-0.22386 0.5,-0.5 0,-0.27614 -0.22386,-0.5 -0.5,-0.5 H 508 v -0.5 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 h -0.0312 C 507.21603,535 507,535.223 507,535.5 v 0.5 h -1 v -0.5 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 h -0.0312 C 505.21603,535 505,535.223 505,535.5 v 0.5 h -1 v -0.5 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 h -0.0312 C 503.21603,535 503,535.223 503,535.5 v 0.5 h -1 v -0.5 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 h -0.0312 C 501.21603,535 501,535.223 501,535.5 v 0.5 h -1 v -0.5 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 h -0.0312 z" style="fill:#000000;stroke:none" /> <rect ry="0.5" @@ -5023,7 +5090,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect8696" id="ventilation" - d="M 242.48438,370 C 242.21603,370 242,370.223 242,370.5 l 0,8 c 0,0.277 0.21603,0.5 0.48438,0.5 l 1.51562,0 0,2.5 c 0,0.277 0.21603,0.5 0.48438,0.5 l 6.03124,0 C 250.78397,382 251,381.777 251,381.5 l 0,-2.5 1.51562,0 C 252.78397,379 253,378.777 253,378.5 l 0,-8 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 z m 1,1 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 243.21603,372 243,371.777 243,371.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 248.21603,372 248,371.777 248,371.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m -5,2 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 243.21603,374 243,373.777 243,373.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 248.21603,374 248,373.777 248,373.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m -5,2 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 243.21603,376 243,375.777 243,375.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 248.21603,376 248,375.777 248,375.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m -5,2 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 243.21603,378 243,377.777 243,377.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 3.03124,0 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 l -3.03124,0 C 248.21603,378 248,377.777 248,377.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z" + d="M 242.48438,370 C 242.21603,370 242,370.223 242,370.5 v 8 c 0,0.277 0.21603,0.5 0.48438,0.5 H 244 v 2.5 c 0,0.277 0.21603,0.5 0.48438,0.5 h 6.03124 C 250.78397,382 251,381.777 251,381.5 V 379 h 1.51562 C 252.78397,379 253,378.777 253,378.5 v -8 c 0,-0.277 -0.21603,-0.5 -0.48438,-0.5 z m 1,1 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 243.21603,372 243,371.777 243,371.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 248.21603,372 248,371.777 248,371.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m -5,2 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 243.21603,374 243,373.777 243,373.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 248.21603,374 248,373.777 248,373.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m -5,2 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 243.21603,376 243,375.777 243,375.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 248.21603,376 248,375.777 248,375.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m -5,2 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 243.21603,378 243,377.777 243,377.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z m 5,0 h 3.03124 c 0.26835,0 0.48438,0.223 0.48438,0.5 0,0.277 -0.21603,0.5 -0.48438,0.5 h -3.03124 C 248.21603,378 248,377.777 248,377.5 c 0,-0.277 0.21603,-0.5 0.48438,-0.5 z" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none" @@ -5054,7 +5121,7 @@ ry="1" /> <path style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none" - d="m 87,202 1,1.5 1,-1.5 1,0 -1,2 0,3 -2,0 0,-3 -1,-2 z" + d="m 87,202 1,1.5 1,-1.5 h 1 l -1,2 v 3 h -2 v -3 l -1,-2 z" id="path8726" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccc" /> @@ -5069,7 +5136,7 @@ inkscape:connector-curvature="0" inkscape:label="#rect8730" id="tree_with_leaf" - d="m 87,210 c -0.554,0 -1,0.446 -1,1 -0.554,0 -1,0.446 -1,1 l 0,1 c -0.554,0 -1,0.446 -1,1 l 0,2 c 0,0.554 0.446,1 1,1 0,0.554 0.446,1 1,1 l 1,2 0,3 2,0 0,-3 1,-2 c 0.554,0 1,-0.446 1,-1 0.554,0 1,-0.446 1,-1 l 0,-2 c 0,-0.554 -0.446,-1 -1,-1 l 0,-1 c 0,-0.554 -0.446,-1 -1,-1 0,-0.554 -0.446,-1 -1,-1 z m 6.5,0 c -1.5,1 -1.5,3 0,4 1.5,-1 1.5,-3 0,-4 z m -6.5,8 2,0 -1,1.5 z" + d="m 87,210 c -0.554,0 -1,0.446 -1,1 -0.554,0 -1,0.446 -1,1 v 1 c -0.554,0 -1,0.446 -1,1 v 2 c 0,0.554 0.446,1 1,1 0,0.554 0.446,1 1,1 l 1,2 v 3 h 2 v -3 l 1,-2 c 0.554,0 1,-0.446 1,-1 0.554,0 1,-0.446 1,-1 v -2 c 0,-0.554 -0.446,-1 -1,-1 v -1 c 0,-0.554 -0.446,-1 -1,-1 0,-0.554 -0.446,-1 -1,-1 z m 6.5,0 c -1.5,1 -1.5,3 0,4 1.5,-1 1.5,-3 0,-4 z m -6.5,8 h 2 l -1,1.5 z" style="fill:#000000;stroke:none"> <title id="title6150">tree with leaf @@ -5116,7 +5183,7 @@ rx="0.5" ry="0.5" /> drain manhole cover @@ -5182,10 +5249,10 @@ inkscape:connector-curvature="0" id="path9319" d="m 577,136 3,-3 3,3 z" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1.0" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -5212,19 +5279,19 @@ id="rect4990" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - - - - + ry="0.5" /> @@ -5476,8 +5511,8 @@ id="rect9398" width="1" height="13" - x="17" - y="97" + x="65" + y="577" rx="0.5" ry="0.50000006" /> - - onion roof shape @@ -5952,7 +5977,7 @@ bicycle @@ -5991,7 +6016,7 @@ rx="0" ry="0" /> + transform="rotate(90)" /> <path sodipodi:nodetypes="ccc" @@ -6078,7 +6103,7 @@ sodipodi:nodetypes="ccc" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 135.5,198 1,0 0.5,7 0,2 -2,0 0,-2 z" + d="m 135.5,198 h 1 l 0.5,7 v 2 h -2 v -2 z" id="path5242" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccc" /> @@ -6094,7 +6119,7 @@ style="fill:none;stroke:#000000;stroke-opacity:1" height="100%" width="100%" - transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-100.17366,154.15938)" + transform="rotate(-45,136,197.99999)" id="use5258" inkscape:transform-center-y="-2.1766918" inkscape:transform-center-x="-2.0659482" @@ -6105,7 +6130,7 @@ style="fill:none;stroke:#000000;stroke-opacity:1" height="100%" width="100%" - transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,179.84062,-38.173666)" + transform="rotate(45,136,198)" id="use5260" inkscape:transform-center-y="2.0659481" inkscape:transform-center-x="-2.1766918" @@ -6150,7 +6175,7 @@ inkscape:connector-curvature="0" inkscape:label="#path5274" id="palm" - d="m 133.17578,209.65625 c -0.43836,-0.0189 -0.91214,0.0118 -1.41797,0.10156 1.30753,0.34817 2.5011,1.25941 3.33399,2.43946 -1.22923,-0.8819 -3.4563,-0.53891 -5.0918,1.80273 1.1694,-0.67759 2.65625,-0.87696 4.07812,-0.63281 -1.49133,0.2479 -2.81922,2.06497 -2.32031,4.875 0.46712,-1.75426 1.93886,-3.31054 3.73047,-4.04688 L 135,221 l 0,2 2,0 0,-2 -0.48828,-6.80469 c 1.79161,0.73634 3.26335,2.29262 3.73047,4.04688 0.49892,-2.81003 -0.82897,-4.6271 -2.32031,-4.875 1.42187,-0.24415 2.90872,-0.0448 4.07812,0.63281 -1.6355,-2.34164 -3.86257,-2.68463 -5.0918,-1.80273 0.83288,-1.18005 2.02646,-2.09129 3.33399,-2.43946 -1.96729,-0.34929 -3.44464,0.19935 -4.24219,1.07032 -0.59248,-0.64703 -1.55769,-1.1172 -2.82422,-1.17188 z" + d="m 133.17578,209.65625 c -0.43836,-0.0189 -0.91214,0.0118 -1.41797,0.10156 1.30753,0.34817 2.5011,1.25941 3.33399,2.43946 -1.22923,-0.8819 -3.4563,-0.53891 -5.0918,1.80273 1.1694,-0.67759 2.65625,-0.87696 4.07812,-0.63281 -1.49133,0.2479 -2.81922,2.06497 -2.32031,4.875 0.46712,-1.75426 1.93886,-3.31054 3.73047,-4.04688 L 135,221 v 2 h 2 v -2 l -0.48828,-6.80469 c 1.79161,0.73634 3.26335,2.29262 3.73047,4.04688 0.49892,-2.81003 -0.82897,-4.6271 -2.32031,-4.875 1.42187,-0.24415 2.90872,-0.0448 4.07812,0.63281 -1.6355,-2.34164 -3.86257,-2.68463 -5.0918,-1.80273 0.83288,-1.18005 2.02646,-2.09129 3.33399,-2.43946 -1.96729,-0.34929 -3.44464,0.19935 -4.24219,1.07032 -0.59248,-0.64703 -1.55769,-1.1172 -2.82422,-1.17188 z" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect ry="1" @@ -6209,7 +6234,7 @@ <path inkscape:label="#rect5311" id="no_foot" - d="m 149.5,563 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 2,0 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 2,0 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 2.4902,0.99609 a 0.50005,0.50005 0 0 0 -0.3437,0.15039 l -2.0547,2.05469 C 153.426,566.07729 153.2241,566 153,566 l -1,0 -1,0 -1,0 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 0.493,0 0.8811,0.35993 0.9648,0.82813 l -3.3183,3.31835 a 0.50005,0.50005 0 1 0 0.707,0.70704 l 8,-8 a 0.50005,0.50005 0 0 0 -0.3633,-0.85743 z m -1.9902,4.625 -4,4 0,0.37891 c 0,0.554 0.446,1 1,1 l 2,0 c 0.554,0 1,-0.446 1,-1 z" + d="m 149.5,563 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 2,0 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 2,0 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 2.4902,0.99609 a 0.50005,0.50005 0 0 0 -0.3437,0.15039 l -2.0547,2.05469 C 153.426,566.07729 153.2241,566 153,566 h -1 -1 -1 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 0.493,0 0.8811,0.35993 0.9648,0.82813 l -3.3183,3.31835 a 0.50005,0.50005 0 1 0 0.707,0.70704 l 8,-8 a 0.50005,0.50005 0 0 0 -0.3633,-0.85743 z m -1.9902,4.625 -4,4 V 573 c 0,0.554 0.446,1 1,1 h 2 c 0.554,0 1,-0.446 1,-1 z" style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" inkscape:connector-curvature="0" /> <path @@ -6227,14 +6252,14 @@ sodipodi:nodetypes="ccccccccccccccccc" inkscape:connector-curvature="0" id="path5359" - d="m 631,34.5 0,3 -5,3 0,2 5,-2 0,4.5 -1.5,1 0,1 2.5,-1 2.5,1 0,-1 -1.5,-1 0,-4.5 5,2 0,-2 -5,-3 0,-3" + d="m 631,34.5 v 3 l -5,3 v 2 l 5,-2 V 45 l -1.5,1 v 1 l 2.5,-1 2.5,1 V 46 L 633,45 v -4.5 l 5,2 v -2 l -5,-3 v -3" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="sscccccccccccccccss" inkscape:connector-curvature="0" inkscape:label="#circle5361" id="plane" - d="m 632,49.5 c -0.55228,0 -1,0.44772 -1,1 l 0,3 -5,3 0,2 5,-2 0,4 -1.5,1 0,1.5 2.5,-1 2.5,1 0,-1.5 -1.5,-1 0,-4 5,2 0,-2 -5,-3 0,-3 c 0,-0.55228 -0.44772,-1 -1,-1 z" + d="m 632,49.5 c -0.55228,0 -1,0.44772 -1,1 v 3 l -5,3 v 2 l 5,-2 v 4 l -1.5,1 V 63 l 2.5,-1 2.5,1 v -1.5 l -1.5,-1 v -4 l 5,2 v -2 l -5,-3 v -3 c 0,-0.55228 -0.44772,-1 -1,-1 z" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"> <title id="title6144">plane @@ -6263,7 +6288,7 @@ y="-245.5" ry="0.5" rx="0.5" - transform="matrix(0,1,-1,0,0,0)" /> + transform="rotate(90)" /> - + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" /> <path inkscape:label="#path5598" - id="jewish" - d="m 632,82.35938 -1.58398,2.77343 -3.27735,0 0.42774,0.74805 1.21093,2.11914 -1.63867,2.86719 0.86133,0 2.41602,0 1.58398,2.77343 1.58398,-2.77343 3.27735,0 L 635.22266,88 l 1.63867,-2.86719 -3.27735,0 z m 0,2.01562 0.43359,0.75781 -0.86718,0 z m -3.13867,1.75781 0.98242,0 -0.49023,0.85938 z m 2.13476,0 2.00782,0 1.06836,1.86719 -1.06836,1.86719 -2.00782,0 L 629.92773,88 Z m 3.16016,0 0.98242,0 -0.49219,0.85938 z m -4.80273,2.875 0.49023,0.85938 -0.98242,0 z m 5.29296,0 0.49219,0.85938 -0.98242,0 z m -3.08007,1.85938 0.86718,0 L 632,91.625 Z" - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="star_of_david" + d="m 632,82.35938 -1.58398,2.77343 h -3.27735 l 0.42774,0.74805 1.21093,2.11914 -1.63867,2.86719 H 628 630.41602 L 632,93.64062 l 1.58398,-2.77343 h 3.27735 L 635.22266,88 l 1.63867,-2.86719 h -3.27735 z m 0,2.01562 0.43359,0.75781 h -0.86718 z m -3.13867,1.75781 h 0.98242 l -0.49023,0.85938 z m 2.13476,0 h 2.00782 L 634.07227,88 l -1.06836,1.86719 h -2.00782 L 629.92773,88 Z m 3.16016,0 h 0.98242 l -0.49219,0.85938 z m -4.80273,2.875 0.49023,0.85938 h -0.98242 z m 5.29296,0 0.49219,0.85938 h -0.98242 z m -3.08007,1.85938 h 0.86718 L 632,91.625 Z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" inkscape:connector-curvature="0" /> <rect style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" @@ -6592,7 +6610,7 @@ <path inkscape:label="#rect5632" id="envelope" - d="m 67.5,53 c -0.09,0 -0.16936,0.0309 -0.24219,0.0723 a 0.50005,0.50005 0 0 1 0.0547,0.0371 l 4.6875,3.75 4.6875,-3.75 a 0.50024408,0.50024408 0 0 1 0.0547,-0.0391 C 76.66958,53.02932 76.58965,53 76.5,53 Z M 67,54.14062 V 60.5 c 0,0.277 0.223,0.5 0.5,0.5 h 9 c 0.277,0 0.5,-0.223 0.5,-0.5 v -6.35938 l -4.6875,3.75 a 0.50005,0.50005 0 0 1 -0.625,0 z" + d="m 83.5,52 c -0.09,0 -0.16936,0.0309 -0.24219,0.0723 a 0.50005,0.50005 0 0 1 0.0547,0.0371 l 4.6875,3.75 4.6875,-3.75 a 0.50024408,0.50024408 0 0 1 0.0547,-0.0391 C 92.66958,52.02932 92.58965,52 92.5,52 Z M 83,53.14062 V 59.5 c 0,0.277 0.223,0.5 0.5,0.5 h 9 c 0.277,0 0.5,-0.223 0.5,-0.5 v -6.35938 l -4.6875,3.75 a 0.50005,0.50005 0 0 1 -0.625,0 z" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" inkscape:connector-curvature="0"> <title @@ -6627,7 +6645,7 @@ sodipodi:cx="536.5" sodipodi:type="arc" id="path5658" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1.0" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" sodipodi:arc-type="arc" /> <path d="m 538,394.5 a 1.5,1.5 0 0 1 -0.75,1.29904 1.5,1.5 0 0 1 -1.5,0 A 1.5,1.5 0 0 1 535,394.5" @@ -6640,7 +6658,7 @@ sodipodi:cx="536.5" sodipodi:type="arc" id="path5660" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1.0" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" sodipodi:arc-type="arc" /> <rect y="389.5" @@ -6648,13 +6666,13 @@ height="5" width="1" id="rect5662" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1.0" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cccc" inkscape:connector-curvature="0" id="path5664" d="m 539,394.5 v -3 c -0.5,1.5 -1,2.5 -2.5,3 z" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1.0" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="scccssccccsscccssssss" inkscape:label="#circle5666" @@ -6790,8 +6808,8 @@ <path inkscape:connector-curvature="0" inkscape:label="#rect5775" - id="noexit" - d="m 628.5,147 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 l 2.5,0 0,6.5 c 0,0.277 0.223,0.5 0.5,0.5 l 1,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-6.5 2.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" + id="t" + d="m 628.5,147 c -0.277,0 -0.5,0.223 -0.5,0.5 v 1 c 0,0.277 0.223,0.5 0.5,0.5 h 2.5 v 6.5 c 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 V 149 h 2.5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"> <title id="title6137">letter T @@ -6799,15 +6817,15 @@ + d="m 616.01562,149 c -0.18139,-0.006 -0.35182,0.0866 -0.44531,0.24219 l -3,5 c -0.20076,0.33374 0.0402,0.75874 0.42969,0.75781 h 6 c 0.38947,9.3e-4 0.63045,-0.42407 0.42969,-0.75781 l -3,-5 c -0.0878,-0.14598 -0.24381,-0.23725 -0.41407,-0.24219 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"> small triangle @@ -6827,25 +6845,25 @@ sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path5790" - d="m 99.5,546.5 2,0 1,5" + d="m 99.5,546.5 h 2 l 1,5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -7297,16 +7315,16 @@ sodipodi:nodetypes="ccccccccccc" inkscape:connector-curvature="0" id="path11291" - d="m 195,41 2,-1 v 7 h 6 v -7 l 2,1 1.5,-3 -4,-2 h -5 l -4,2 z" + d="m 195,39 2,-1 v 7 h 6 v -7 l 2,1 1.5,-3 -4,-2 h -5 l -4,2 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + inkscape:connector-curvature="0"> + wine bottle + @@ -7545,21 +7566,21 @@ id="rect11484" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -7622,30 +7643,30 @@ sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path13219" - d="m 678,422 0.5,6 8.5,-3 -0.5,-3 z" + d="m 678,420 0.5,6 8.5,-3 -0.5,-3 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + y="67.5" + ry="0.5" + rx="0.5" /> - + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -8260,42 +8278,45 @@ + id="stone_with_inscription"> + stone with inscription + @@ -8489,8 +8510,8 @@ @@ -8559,18 +8580,18 @@ id="rect2322" width="10" height="5" - x="691" - y="417" + x="739" + y="416" rx="0" ry="0" /> @@ -8583,19 +8604,19 @@ + d="m 73,233 a 2,2 0 0 1 -1,1.73205 2,2 0 0 1 -2,0 A 2,2 0 0 1 69,233 h 2 z" /> + d="m 72,233 a 1,1 0 0 1 -0.5,0.86603 1,1 0 0 1 -1,0 A 1,1 0 0 1 70,233 h 1 z" /> @@ -8824,20 +8845,20 @@ id="rect5484" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -8899,31 +8920,31 @@ ry="0.5" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> anchor @@ -9185,8 +9206,8 @@ id="title7516">suspension railway @@ -9194,12 +9215,12 @@ sodipodi:nodetypes="cscsscsc" inkscape:connector-curvature="0" id="path9793" - d="m 81.5,459.5 0.5,0 c 1,0 2,1 2,1 0,0 1,1 2,1 l 4,0 c 1,0 2,-1 2,-1 0,0 1,-1 2,-1 l 0.5,0" + d="m 641.5,519.5 h 0.5 c 1,0 2,1 2,1 0,0 1,1 2,1 h 4 c 1,0 2,-1 2,-1 0,0 1,-1 2,-1 h 0.5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - - - - - - - - - - - - - - car_wash - + transform="rotate(90)" /> @@ -9410,7 +9321,7 @@ id="rect9942" width="1" height="9" - x="722" + x="706" y="421" ry="0.5" rx="0.5" /> @@ -9418,7 +9329,7 @@ rx="0.5" ry="0.5" y="421" - x="732" + x="716" height="9" width="1" id="rect9944" @@ -9428,13 +9339,13 @@ id="rect9946" width="1" height="9" - x="727" + x="711" y="421" ry="0.5" rx="0.5" /> @@ -9442,11 +9353,11 @@ sodipodi:nodetypes="ccssc" inkscape:connector-curvature="0" id="path9950" - d="m 728,429.5 c 0,-1 4,-1 4,0 v -8 c 0,-1 -4,-1 -4,0 z" + d="m 712,429.5 c 0,-1 4,-1 4,0 v -8 c 0,-1 -4,-1 -4,0 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -9456,7 +9367,7 @@ width="1" height="10" x="343" - y="163" + y="161" rx="0.5" ry="0.5" /> @@ -9559,7 +9470,7 @@ sodipodi:ry="1" sodipodi:start="0" sodipodi:end="1.5707963" - d="m -514,492 a 1,1 0 0 1 -1,1 l 0,-1 z" + d="m -514,492 a 1,1 0 0 1 -1,1 v -1 z" transform="scale(-1,1)" sodipodi:arc-type="slice" /> @@ -9638,7 +9549,7 @@ sodipodi:nodetypes="ccccc" /> @@ -9656,7 +9567,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -9686,7 +9597,7 @@ style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -9695,7 +9606,7 @@ @@ -9748,25 +9659,25 @@ r="1" /> @@ -9809,25 +9720,10 @@ y="354" ry="0.5" rx="0.5" /> - - - recycling container - - - waste basket - @@ -10147,7 +10034,7 @@ ry="0.5" /> @@ -10156,26 +10043,26 @@ id="path6085" sodipodi:type="arc" sodipodi:cx="184" - sodipodi:cy="390" + sodipodi:cy="389" sodipodi:rx="3" sodipodi:ry="3" sodipodi:start="3.1415927" sodipodi:end="0" sodipodi:open="true" - d="m 181,390 a 3,3 0 0 1 3,-3 3,3 0 0 1 3,3" + d="m 181,389 a 3,3 0 0 1 3,-3 3,3 0 0 1 3,3" sodipodi:arc-type="arc" /> @@ -10379,29 +10266,29 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path5627" - d="m 6.5,266.5 2,0" + d="m 6.5,266.5 h 2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10409,29 +10296,29 @@ sodipodi:nodetypes="cccccc" inkscape:connector-curvature="0" id="path5637" - d="m 20.5,270.5 2,-4 0,-7.5 m 2,0 0,7.5 2,4" + d="m 20.5,270.5 2,-4 V 259 m 2,0 v 7.5 l 2,4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10439,29 +10326,29 @@ sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path5651" - d="m 54.5,260.5 -3.5,3 9,0 -3.5,-3 z" + d="m 70.5,260.5 -3.5,3 h 9 l -3.5,-3 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10469,41 +10356,41 @@ sodipodi:nodetypes="cccccc" inkscape:connector-curvature="0" id="path5665" - d="m 68.5,270.5 2,-4 0,-8.5 m 2,0 0,8.5 2,4" + d="m 84.5,270.5 2,-4 V 258 m 2,0 v 8.5 l 2,4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10511,35 +10398,35 @@ sodipodi:nodetypes="cccccc" inkscape:connector-curvature="0" id="path5683" - d="m 100.5,270.5 2,-4 0,-9 2,0 0,9 2,4" + d="m 116.5,270.5 2,-4 v -9 h 2 v 9 l 2,4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10547,17 +10434,17 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path5703" - d="m 115,260.5 5,0" + d="m 131,260.5 h 5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10565,35 +10452,35 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path5711" - d="m 131.5,262.5 8,0" + d="m 147.5,262.5 h 8" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10601,11 +10488,11 @@ sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path5906" - d="m 147.5,262.5 4,3 1,2" + d="m 163.5,262.5 4,3 1,2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10613,17 +10500,17 @@ sodipodi:nodetypes="cccccccc" inkscape:connector-curvature="0" id="path5910" - d="m 166.5,270.5 0,-3 -3,-5 0,-4 m 8,0 0,4 -3,5 0,3" + d="m 182.5,270.5 v -3 l -3,-5 v -4 m 8,0 v 4 l -3,5 v 3" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10631,11 +10518,11 @@ sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path5918" - d="m 171.5,262.5 -4,3 -1,2" + d="m 187.5,262.5 -4,3 -1,2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10643,23 +10530,23 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path5922" - d="m 162,259.5 11,0" + d="m 178,259.5 h 11" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10667,11 +10554,11 @@ sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path5930" - d="m 179.5,262.5 4,3 1,2" + d="m 195.5,262.5 4,3 1,2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10679,11 +10566,11 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path5934" - d="m 178,262.5 11,0" + d="m 194,262.5 h 11" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10691,17 +10578,17 @@ sodipodi:nodetypes="cccccccc" inkscape:connector-curvature="0" id="path6024" - d="m 197.5,270.5 1,-4 0,-1 -4,-6 m 10,0 -4,6 0,1 1,4" + d="m 213.5,270.5 1,-4 v -1 l -4,-6 m 10,0 -4,6 v 1 l 1,4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10709,23 +10596,23 @@ sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path6032" - d="m 204.5,259.5 -5,4 -1,2" + d="m 220.5,259.5 -5,4 -1,2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10733,11 +10620,11 @@ sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path6044" - d="m 210.5,259.5 5,3 1,2" + d="m 226.5,259.5 5,3 1,2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10745,17 +10632,17 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6048" - d="m 214.5,265.5 2,0" + d="m 230.5,265.5 h 2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10763,8 +10650,8 @@ id="title58961">y-frame transmission tower @@ -10773,37 +10660,37 @@ @@ -10836,7 +10723,7 @@ rx="0.5" /> @@ -10890,13 +10777,13 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -10945,7 +10832,7 @@ y="0" xlink:href="#path6320" id="use6346" - transform="matrix(0,1,-1,0,479,368)" + transform="rotate(90,55.5,423.5)" width="100%" height="100%" inkscape:transform-center-x="-5.5" /> @@ -10955,7 +10842,7 @@ xlink:href="#path6320" inkscape:transform-center-y="-4.7631417" id="use6350" - transform="matrix(0.8660254,0.5,-0.5,0.8660254,219.1856,28.98824)" + transform="rotate(30,55.500011,423.50002)" width="100%" height="100%" inkscape:transform-center-x="-2.7500024" /> @@ -10965,7 +10852,7 @@ xlink:href="#use6346" inkscape:transform-center-x="-4.7631421" id="use6354" - transform="matrix(0.8660254,0.5,-0.5,0.8660254,219.1856,28.98824)" + transform="rotate(30,55.500011,423.50002)" width="100%" height="100%" inkscape:transform-center-y="2.749998" /> @@ -10976,7 +10863,7 @@ inkscape:transform-center-x="-4.7631452" inkscape:transform-center-y="-2.7500025" id="use6358" - transform="matrix(0.8660254,0.5,-0.5,0.8660254,219.1856,28.98824)" + transform="rotate(30,55.500011,423.50002)" width="100%" height="100%" /> @@ -11100,7 +10987,7 @@ inkscape:transform-center-x="5.4999986" height="100%" width="100%" - transform="matrix(0,-1,1,0,-368,479)" + transform="rotate(-90,55.5,423.5)" id="use6476" xlink:href="#path6320" y="0" @@ -11109,7 +10996,7 @@ inkscape:transform-center-x="2.750001" height="100%" width="100%" - transform="matrix(-0.8660254,-0.5,0.5,-0.8660254,-108.18559,818.01176)" + transform="rotate(-150,55.500001,423.5)" id="use6478" inkscape:transform-center-y="4.763137" xlink:href="#path6320" @@ -11119,7 +11006,7 @@ inkscape:transform-center-y="-2.7500027" height="100%" width="100%" - transform="matrix(-0.8660254,-0.5,0.5,-0.8660254,-108.18559,818.01176)" + transform="rotate(-150,55.500001,423.5)" id="use6480" inkscape:transform-center-x="4.7631407" xlink:href="#use6346" @@ -11128,7 +11015,7 @@ @@ -11157,24 +11044,24 @@ + d="m 70.5,554.5 c 0,-0.55228 0.44772,-1 1,-1 0.55228,0 1,0.44772 1,1 v 0 2 0 c 0,0.55228 -0.44772,1 -1,1 -0.55228,0 -1,-0.44772 -1,-1 z" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> @@ -11319,8 +11206,8 @@ id="title58965">h-frame transmission tower @@ -11328,8 +11215,8 @@ id="title58967">h-frame two-level transmission tower @@ -11374,13 +11261,13 @@ ry="0.5" /> @@ -11404,14 +11291,14 @@ inkscape:transform-center-x="2.0000032" inkscape:transform-center-y="3.4641006" id="use6934" - transform="matrix(-0.5,-0.8660254,0.8660254,-0.5,347.05677,533.37333)" + transform="rotate(-120,439.5,166.50001)" width="100%" height="100%" style="fill:none;stroke:#000000;stroke-opacity:1" /> @@ -11536,7 +11423,7 @@ cx="-151.49994" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="circle5971" - transform="scale(-1,-1)" /> + transform="scale(-1)" /> @@ -11710,7 +11597,7 @@ id="path4594-8" style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -11775,7 +11662,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -11790,13 +11677,13 @@ rx="0.5" /> @@ -11878,7 +11765,7 @@ width="1" height="7.5000005" x="530" - y="166.5" + y="163.5" rx="0.5" ry="0.5" /> @@ -11981,7 +11868,7 @@ width="2" height="6" x="577" - y="168" + y="165" rx="0.5" ry="0.5" /> @@ -12031,7 +11918,7 @@ <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 274.5,259.5 10,0" + d="m 290.5,259.5 h 10" id="path6489" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 277.5,261 0,1" + d="m 293.5,261 v 1" id="path6493" inkscape:connector-curvature="0" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6495" - d="m 274.5,270.5 1,-12" + d="m 290.5,270.5 1,-12" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 284.5,270.5 -1,-12" + d="m 300.5,270.5 -1,-12" id="path6497" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path inkscape:connector-curvature="0" id="path6499" - d="m 281.5,261 0,1" + d="m 297.5,261 v 1" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6513" - d="m 290.5,261.5 10,0" + d="m 306.5,261.5 h 10" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path inkscape:connector-curvature="0" id="path6515" - d="m 293.5,263 0,1" + d="m 309.5,263 v 1" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 290.5,270.5 1,-12" + d="m 306.5,270.5 1,-12" id="path6517" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -12086,62 +11973,62 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6519" - d="m 300.5,270.5 -1,-12" + d="m 316.5,270.5 -1,-12" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 297.5,263 0,1" + d="m 313.5,263 v 1" id="path6521" inkscape:connector-curvature="0" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 290.5,259.5 10,0" + d="m 306.5,259.5 h 10" id="path6523" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 306.5,261.5 10,0" + d="m 322.5,261.5 h 10" id="path6525" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 309.5,265 0,1" + d="m 325.5,265 v 1" id="path6527" inkscape:connector-curvature="0" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6529" - d="m 306.5,270.5 1,-12" + d="m 322.5,270.5 1,-12" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 316.5,270.5 -1,-12" + d="m 332.5,270.5 -1,-12" id="path6532" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path inkscape:connector-curvature="0" id="path6534" - d="m 313.5,265 0,1" + d="m 329.5,265 v 1" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6536" - d="m 306.5,259.5 10,0" + d="m 322.5,259.5 h 10" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6538" - d="m 306.5,263.5 10,0" + d="m 322.5,263.5 h 10" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 275.48438,273.99219 a 0.50005,0.50005 0 0 0 -0.48243,0.46679 L 274.95703,275 274.5,275 a 0.50005,0.50005 0 1 0 0,1 l 0.37305,0 -0.8711,10.45898 a 0.50005,0.50005 0 1 0 0.9961,0.082 L 275.87695,276 l 7.2461,0 0.8789,10.54102 a 0.50005,0.50005 0 1 0 0.9961,-0.082 L 284.12695,276 284.5,276 a 0.50005,0.50005 0 1 0 0,-1 l -0.45703,0 -0.0449,-0.54102 a 0.50005,0.50005 0 0 0 -0.49805,-0.46679 0.50005,0.50005 0 0 0 -0.49805,0.54883 l 0.039,0.45898 -7.08204,0 0.0391,-0.45898 a 0.50005,0.50005 0 0 0 -0.51367,-0.54883 z m 2.00781,2.5 A 0.50005,0.50005 0 0 0 277,277 l 0,1 a 0.50005,0.50005 0 1 0 1,0 l 0,-1 a 0.50005,0.50005 0 0 0 -0.50781,-0.50781 z m 4,0 A 0.50005,0.50005 0 0 0 281,277 l 0,1 a 0.50005,0.50005 0 1 0 1,0 l 0,-1 a 0.50005,0.50005 0 0 0 -0.50781,-0.50781 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 291.48438,273.99219 a 0.50005,0.50005 0 0 0 -0.48243,0.46679 L 290.95703,275 H 290.5 a 0.50005,0.50005 0 1 0 0,1 h 0.37305 l -0.8711,10.45898 a 0.50005,0.50005 0 1 0 0.9961,0.082 L 291.87695,276 h 7.2461 l 0.8789,10.54102 a 0.50005,0.50005 0 1 0 0.9961,-0.082 L 300.12695,276 H 300.5 a 0.50005,0.50005 0 1 0 0,-1 h -0.45703 l -0.0449,-0.54102 a 0.50005,0.50005 0 0 0 -0.49805,-0.46679 0.50005,0.50005 0 0 0 -0.49805,0.54883 l 0.039,0.45898 h -7.08204 l 0.0391,-0.45898 a 0.50005,0.50005 0 0 0 -0.51367,-0.54883 z m 2.00781,2.5 A 0.50005,0.50005 0 0 0 293,277 v 1 a 0.50005,0.50005 0 1 0 1,0 v -1 a 0.50005,0.50005 0 0 0 -0.50781,-0.50781 z m 4,0 A 0.50005,0.50005 0 0 0 297,277 v 1 a 0.50005,0.50005 0 1 0 1,0 v -1 a 0.50005,0.50005 0 0 0 -0.50781,-0.50781 z" id="power_tower_portal" inkscape:label="#path6540" inkscape:connector-curvature="0"> @@ -12149,8 +12036,8 @@ id="title58971">portal transmission tower @@ -12158,8 +12045,8 @@ id="title58973">portal two-level transmission tower @@ -12177,7 +12064,7 @@ rx="0.52326202" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 37,269.5 5,0" + d="m 37,269.5 h 5" id="path6334" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 53,269.5 5,0" + d="m 69,269.5 h 5" id="path6336" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -12252,11 +12139,11 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6338" - d="m 69,269.5 5,0" + d="m 85,269.5 h 5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 85,269.5 5,0" + d="m 101,269.5 h 5" id="path6340" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -12264,23 +12151,23 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6342" - d="m 101,269.5 5,0" + d="m 117,269.5 h 5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path6344" - d="m 117,269.5 5,0" + d="m 133,269.5 h 5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 133,269.5 5,0" + d="m 149,269.5 h 5" id="path6346" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 7.47461,273 a 0.50005,0.50005 0 0 0 -0.44922,0.3418 l -0.95703,2.86914 -3.39453,2.91015 A 0.50005,0.50005 0 0 0 3,280 l 3,0 0,2.38281 -1.94727,3.89453 a 0.50005,0.50005 0 1 0 0.89454,0.44532 L 5.30859,286 l 4.38282,0 0.36132,0.72266 a 0.50005,0.50005 0 1 0 0.89454,-0.44532 L 9,282.38281 9,280 l 3,0 a 0.50005,0.50005 0 0 0 0.32617,-0.87891 L 8.93164,276.21094 7.97461,273.3418 A 0.50005,0.50005 0 0 0 7.47461,273 Z M 7.5,275.08203 7.80664,276 7.19336,276 Z M 7,277 l 1,0 0,2 -1,0 z M 6,277.58789 6,279 4.35156,279 Z m 3,0 L 10.64844,279 9,279 Z M 7,280 l 1,0 0,2 -1,0 z m -0.19141,3 1.38282,0 1,2 -3.38282,0 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 7.47461,273 a 0.50005,0.50005 0 0 0 -0.44922,0.3418 l -0.95703,2.86914 -3.39453,2.91015 A 0.50005,0.50005 0 0 0 3,280 h 3 v 2.38281 l -1.94727,3.89453 a 0.50005,0.50005 0 1 0 0.89454,0.44532 L 5.30859,286 h 4.38282 l 0.36132,0.72266 a 0.50005,0.50005 0 1 0 0.89454,-0.44532 L 9,282.38281 V 280 h 3 a 0.50005,0.50005 0 0 0 0.32617,-0.87891 L 8.93164,276.21094 7.97461,273.3418 A 0.50005,0.50005 0 0 0 7.47461,273 Z M 7.5,275.08203 7.80664,276 H 7.19336 Z M 7,277 h 1 v 2 H 7 Z m -1,0.58789 V 279 H 4.35156 Z m 3,0 L 10.64844,279 H 9 Z M 7,280 h 1 v 2 H 7 Z m -0.19141,3 h 1.38282 l 1,2 H 5.80859 Z" id="power_tower_1_level" inkscape:label="#path6348" inkscape:connector-curvature="0"> @@ -12288,8 +12175,8 @@ id="title6627">one-level transmission tower @@ -12297,8 +12184,8 @@ id="title6625">two-level transmission tower @@ -12306,8 +12193,8 @@ id="title6623">donau transmission tower @@ -12315,8 +12202,8 @@ id="title6621">three-level transmission tower @@ -12324,8 +12211,8 @@ id="title6619">barrel transmission tower @@ -12333,8 +12220,8 @@ id="title6617">asymmetric transmission tower @@ -12342,8 +12229,8 @@ id="title6629">triangle transmission tower @@ -12351,8 +12238,8 @@ id="title6615">flag transmission tower @@ -12360,8 +12247,8 @@ id="title6613">four-level transmission tower @@ -12369,8 +12256,8 @@ id="title6611">delta transmission tower @@ -12378,8 +12265,8 @@ id="title6609">delta two-level transmission tower @@ -12409,7 +12296,7 @@ ry="0.5" /> @@ -12478,7 +12365,7 @@ ry="0.5" /> @@ -12542,7 +12429,7 @@ width="3" height="3" x="534" - y="203" + y="201" rx="0.5" ry="0.45580599" /> @@ -12599,7 +12486,7 @@ y="335.51926" rx="0.5" ry="0.45580599" - transform="matrix(0.96592583,-0.25881905,0.25881905,0.96592583,0,0)" /> + transform="rotate(-15)" /> + transform="rotate(-15)" /> @@ -12674,7 +12561,7 @@ sodipodi:nodetypes="cc" /> @@ -12689,21 +12576,21 @@ ry="0.45580599" /> - @@ -12971,12 +12849,12 @@ width="3" id="rect6310" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - transform="matrix(0,1,-1,0,0,0)" /> + transform="rotate(90)" /> @@ -13109,7 +12987,7 @@ ry="0.5" rx="0.5" y="-423" - x="709" + x="693" height="1" width="4" id="rect6457" @@ -13120,13 +12998,13 @@ id="rect6459" width="5" height="1" - x="709" + x="693" y="424" rx="0.5" ry="0.5" /> + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" /> <path - style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="m 603,49 c 0,0 -1,1 -1,2 v 9.5 l -1,2 V 63 h 4 v -0.5 l -1,-2 V 51 c 0,-1 -1,-2 -1,-2 z m -8,1 v 13 h 3 V 52 h 2 v -1 h -2 v -1 z" - id="rocket_on_launch_pad" + id="path22599" inkscape:label="#rect6738" inkscape:connector-curvature="0" sodipodi:nodetypes="csccccccscccccccccc"> @@ -13426,19 +13304,19 @@ style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 5,494.5 0.5,-3 -3,0" + d="m 5,494.5 0.5,-3 h -3" id="path6755" inkscape:connector-curvature="0" sodipodi:nodetypes="ccc" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 1.99878,491.5 3.5,0 -0.5,2.5 -2.5,0 z" + d="m 1.99878,491.5 h 3.5 l -0.5,2.5 h -2.5 z" id="path6757" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 1.50781,503.99219 a 0.50005,0.50005 0 0 0 -0.50195,0.58984 l 1,6 a 0.50090245,0.50090245 0 1 0 0.98828,-0.16406 L 2.92383,510 l 1.65234,0 -0.0703,0.41797 a 0.50090245,0.50090245 0 1 0 0.98828,0.16406 l 0.5,-3 A 0.50005,0.50005 0 0 0 5.5,507 l -3,0 a 0.50005,0.50005 0 0 0 -0.0742,0.004 l -0.43164,-2.58594 a 0.50005,0.50005 0 0 0 -0.48633,-0.42578 z m 11.9668,0.002 a 0.50005,0.50005 0 0 0 -0.46875,0.42383 l -0.43164,2.58789 A 0.50005,0.50005 0 0 0 12.49805,507 l -3,0 a 0.50005,0.50005 0 0 0 -0.49219,0.58203 l 0.5,3 a 0.50005,0.50005 0 1 0 0.98633,-0.16406 L 10.42188,510 l 1.65429,0 -0.0703,0.41797 a 0.50005,0.50005 0 1 0 0.98633,0.16406 l 1,-6 a 0.50005,0.50005 0 0 0 -0.51758,-0.58789 z M 4.5,504 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 2.5,0 0,5.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-5.5 2.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 1.50781,503.99219 a 0.50005,0.50005 0 0 0 -0.50195,0.58984 l 1,6 a 0.50090245,0.50090245 0 1 0 0.98828,-0.16406 L 2.92383,510 h 1.65234 l -0.0703,0.41797 a 0.50090245,0.50090245 0 1 0 0.98828,0.16406 l 0.5,-3 A 0.50005,0.50005 0 0 0 5.5,507 h -3 a 0.50005,0.50005 0 0 0 -0.0742,0.004 l -0.43164,-2.58594 a 0.50005,0.50005 0 0 0 -0.48633,-0.42578 z m 11.9668,0.002 a 0.50005,0.50005 0 0 0 -0.46875,0.42383 l -0.43164,2.58789 A 0.50005,0.50005 0 0 0 12.49805,507 h -3 a 0.50005,0.50005 0 0 0 -0.49219,0.58203 l 0.5,3 a 0.50005,0.50005 0 1 0 0.98633,-0.16406 L 10.42188,510 h 1.65429 l -0.0703,0.41797 a 0.50005,0.50005 0 1 0 0.98633,0.16406 l 1,-6 a 0.50005,0.50005 0 0 0 -0.51758,-0.58789 z M 4.5,504 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 H 7 v 5.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 505 h 2.5 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" id="table_and_two_chairs" inkscape:label="#path6759" inkscape:connector-curvature="0"> @@ -13465,7 +13343,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 65.5,481.5 c 3.5,1.5 7.5,2.5 12,3 l -12,0 z" + d="m 65.5,481.5 c 3.5,1.5 7.5,2.5 12,3 h -12 z" id="path6822" inkscape:connector-curvature="0" sodipodi:nodetypes="cccc" /> @@ -13479,8 +13357,8 @@ id="rect6834" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 65.50195,497 C 65.22506,496.999 65.00003,497.22311 65,497.5 l 0,3 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 l 11.5,0 0,0.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.0221 -0.009,-0.0412 -0.0117,-0.0625 -4.3e-4,-0.003 -10e-4,-0.006 -0.002,-0.01 -0.003,-0.0197 -0.007,-0.0393 -0.0117,-0.0586 -0.0109,-0.0397 -0.0251,-0.0763 -0.0449,-0.11133 -0.0116,-0.0204 -0.0247,-0.04 -0.0391,-0.0586 l -0.008,-0.0117 c -0.0166,-0.0204 -0.0331,-0.0392 -0.0527,-0.0566 l -0.006,-0.006 c -0.031,-0.0262 -0.0651,-0.0485 -0.10156,-0.0664 -0.0159,-0.008 -0.0301,-0.0171 -0.0469,-0.0234 -0.0389,-0.0154 -0.0796,-0.0259 -0.12109,-0.0312 -4.4596,-0.49551 -8.41025,-1.48553 -11.85742,-2.96289 -0.0617,-0.0267 -0.12812,-0.0406 -0.19532,-0.041 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 65.50195,497 C 65.22506,496.999 65.00003,497.22311 65,497.5 v 3 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 H 77 v 0.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 v -1 c 0,-0.0221 -0.009,-0.0412 -0.0117,-0.0625 -4.3e-4,-0.003 -10e-4,-0.006 -0.002,-0.01 -0.003,-0.0197 -0.007,-0.0393 -0.0117,-0.0586 -0.0109,-0.0397 -0.0251,-0.0763 -0.0449,-0.11133 -0.0116,-0.0204 -0.0247,-0.04 -0.0391,-0.0586 l -0.008,-0.0117 c -0.0166,-0.0204 -0.0331,-0.0392 -0.0527,-0.0566 l -0.006,-0.006 c -0.031,-0.0262 -0.0651,-0.0485 -0.10156,-0.0664 -0.0159,-0.008 -0.0301,-0.0171 -0.0469,-0.0234 -0.0389,-0.0154 -0.0796,-0.0259 -0.12109,-0.0312 -4.4596,-0.49551 -8.41025,-1.48553 -11.85742,-2.96289 -0.0617,-0.0267 -0.12812,-0.0406 -0.19532,-0.041 z" id="awning" inkscape:label="#path6863" inkscape:connector-curvature="0" @@ -13553,7 +13431,7 @@ ry="0" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 81,498 0,2 1,0 0,-2 z m 2,0 0,2 1,0 0,-2 z m 2,0 0,2 1,0 0,-2 z m 2,0 0,2 1,0 0,-2 z m 2,0 0,2 1,0 0,-2 z m 2,0 0,2 1,0 0,-2 z m 2,0 0,2 1,0 0,-2 z" + d="m 81,498 v 2 h 1 v -2 z m 2,0 v 2 h 1 v -2 z m 2,0 v 2 h 1 v -2 z m 2,0 v 2 h 1 v -2 z m 2,0 v 2 h 1 v -2 z m 2,0 v 2 h 1 v -2 z m 2,0 v 2 h 1 v -2 z" id="pergola" inkscape:label="#rect6901" inkscape:connector-curvature="0" @@ -13563,7 +13441,7 @@ </path> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 55.51172,497 c -0.0577,-10e-4 -0.11517,0.007 -0.16992,0.0254 l -6,2 c -0.20422,0.0681 -0.34191,0.25932 -0.3418,0.47461 -2.1e-4,0.008 -2.1e-4,0.0169 0,0.0254 l 0,1.47461 1,0 0,-1 11,0 0,1 1,0 0,-1.46484 c 0.016,-0.22808 -0.12472,-0.43797 -0.3418,-0.50977 l -6,-2 c -0.0473,-0.0157 -0.0967,-0.0243 -0.14648,-0.0254 z" + d="m 55.51172,497 c -0.0577,-10e-4 -0.11517,0.007 -0.16992,0.0254 l -6,2 c -0.20422,0.0681 -0.34191,0.25932 -0.3418,0.47461 -2.1e-4,0.008 -2.1e-4,0.0169 0,0.0254 v 1.47461 h 1 v -1 h 11 v 1 h 1 v -1.46484 c 0.016,-0.22808 -0.12472,-0.43797 -0.3418,-0.50977 l -6,-2 c -0.0473,-0.0157 -0.0967,-0.0243 -0.14648,-0.0254 z" id="roof_and_walls" inkscape:label="#rect6943" inkscape:connector-curvature="0" @@ -13572,8 +13450,8 @@ id="title6958">roof and walls @@ -13603,24 +13481,24 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="ellipse6968" cx="667" - cy="425" + cy="423" rx="1.9999999" ry="1.9999998" /> @@ -13628,28 +13506,28 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="circle6976" cx="663.5" - cy="425" + cy="423" r="0.49999994" /> @@ -13666,12 +13544,9 @@ inkscape:connector-curvature="0" sodipodi:nodetypes="cccc" /> - Pac-Man - + id="path190246" /> @@ -13780,7 +13655,7 @@ ry="0.5" rx="0.5" y="420" - x="742" + x="726" height="7.9999995" width="2" id="rect7657" @@ -13790,8 +13665,8 @@ id="rect7659" width="2" height="7.9999995" - x="610.32446" - y="598.76111" + x="594.86957" + y="594.62018" rx="0.5" ry="0.5" transform="rotate(-15)" /> @@ -13800,7 +13675,7 @@ id="rect7665" width="11" height="1" - x="738" + x="722" y="429" rx="0.49999997" ry="0.5" /> @@ -13808,14 +13683,14 @@ ry="0.5" rx="0.49999997" y="418" - x="738" + x="722" height="1" width="11" id="rect7667" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 596,180 c -0.554,0 -1,0.446 -1,1 -0.554,0 -1,0.446 -1,1 l 0,3 c 0,0.554 0.446,1 1,1 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 l 0,-5 c 0,-0.554 -0.446,-1 -1,-1 z m 8,0 c -0.554,0 -1,0.446 -1,1 l 0,5 c 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 0.554,0 1,-0.446 1,-1 l 0,-3 c 0,-0.554 -0.446,-1 -1,-1 0,-0.554 -0.446,-1 -1,-1 z m -6.5,3 0,1 5,0 0,-1 z" + d="m 596,180 c -0.554,0 -1,0.446 -1,1 -0.554,0 -1,0.446 -1,1 v 3 c 0,0.554 0.446,1 1,1 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 v -5 c 0,-0.554 -0.446,-1 -1,-1 z m 8,0 c -0.554,0 -1,0.446 -1,1 v 5 c 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 0.554,0 1,-0.446 1,-1 v -3 c 0,-0.554 -0.446,-1 -1,-1 0,-0.554 -0.446,-1 -1,-1 z m -6.5,3 v 1 h 5 v -1 z" id="dumbbell" inkscape:connector-curvature="0" sodipodi:nodetypes="scsscsssssssscsscsccccc" @@ -13878,7 +13753,7 @@ </path> <path style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 501,214 c 1.25,0 2.28851,0.51259 3.04492,1.19336 0.75641,0.68077 1.28711,1.50195 1.78711,2.25195 0.5,0.75 0.9693,1.42882 1.46289,1.87305 C 507.78851,219.76259 508.25,220 509,220 h 0.5 c 0.277,0 0.5,0.223 0.5,0.5 v 0.33203 a 1.0001,1.0001 0 0 1 0,0.32617 V 221.5 c 0,0.277 -0.223,0.5 -0.5,0.5 H 509 c -1.25,0 -2.28851,-0.51259 -3.04492,-1.19336 -0.75641,-0.68077 -1.28711,-1.50195 -1.78711,-2.25195 -0.5,-0.75 -0.9693,-1.42882 -1.46289,-1.87305 C 502.21149,216.23741 501.75,216 501,216 h -0.5 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -0.33203 a 1.0001,1.0001 0 0 1 0,-0.32617 V 214.5 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m -0.5,3 c 0.277,0 0.5,0.20256 0.5,0.45508 v 0.0898 C 501,217.79744 500.777,218 500.5,218 h -1 c -0.277,0 -0.5,-0.20256 -0.5,-0.45508 v -0.0898 C 499,217.20256 499.223,217 499.5,217 Z m -0.5,2 c 0.277,0 0.5,0.20256 0.5,0.45508 v 0.0898 C 500.5,219.79744 500.277,220 500,220 h -1 c -0.277,0 -0.5,-0.20256 -0.5,-0.45508 v -0.0898 C 498.5,219.20256 498.723,219 499,219 Z m -0.5,2 c 0.277,0 0.5,0.20256 0.5,0.45508 v 0.0898 C 500,221.79744 499.777,222 499.5,222 h -1 c -0.277,0 -0.5,-0.20256 -0.5,-0.45508 v -0.0898 C 498,221.20256 498.223,221 498.5,221 Z" + d="m 501,212 c 1.25,0 2.28851,0.51259 3.04492,1.19336 0.75641,0.68077 1.28711,1.50195 1.78711,2.25195 0.5,0.75 0.9693,1.42882 1.46289,1.87305 C 507.78851,217.76259 508.25,218 509,218 h 0.5 c 0.277,0 0.5,0.223 0.5,0.5 v 0.33203 a 1.0001,1.0001 0 0 1 0,0.32617 V 219.5 c 0,0.277 -0.223,0.5 -0.5,0.5 H 509 c -1.25,0 -2.28851,-0.51259 -3.04492,-1.19336 -0.75641,-0.68077 -1.28711,-1.50195 -1.78711,-2.25195 -0.5,-0.75 -0.9693,-1.42882 -1.46289,-1.87305 C 502.21149,214.23741 501.75,214 501,214 h -0.5 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -0.33203 a 1.0001,1.0001 0 0 1 0,-0.32617 V 212.5 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m -0.5,3 c 0.277,0 0.5,0.20256 0.5,0.45508 v 0.0898 C 501,215.79744 500.777,216 500.5,216 h -1 c -0.277,0 -0.5,-0.20256 -0.5,-0.45508 v -0.0898 C 499,215.20256 499.223,215 499.5,215 Z m -0.5,2 c 0.277,0 0.5,0.20256 0.5,0.45508 v 0.0898 C 500.5,217.79744 500.277,218 500,218 h -1 c -0.277,0 -0.5,-0.20256 -0.5,-0.45508 v -0.0898 C 498.5,217.20256 498.723,217 499,217 Z m -0.5,2 c 0.277,0 0.5,0.20256 0.5,0.45508 v 0.0898 C 500,219.79744 499.777,220 499.5,220 h -1 c -0.277,0 -0.5,-0.20256 -0.5,-0.45508 v -0.0898 C 498,219.20256 498.223,219 498.5,219 Z" id="slide" inkscape:label="#path7765" inkscape:connector-curvature="0"> @@ -13929,7 +13804,7 @@ </path> <path style="fill:#000000;stroke:none" - d="m 166,209 1,2 -1,2 2,-1 2,1 -1,-2 1,-2 -2,1 z m 2,4 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 4,0 0,1 2,0 0,-1 4,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" + d="m 166,209 1,2 -1,2 2,-1 2,1 -1,-2 1,-2 -2,1 z m 2,4 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 4 v 1 h 2 v -1 h 4 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" id="christmas_tree" inkscape:label="#path7845" inkscape:connector-curvature="0"> @@ -13939,7 +13814,7 @@ <path sodipodi:nodetypes="cccccccccccccc" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none" - d="m 72,195 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 4,0 2,0 4,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" + d="m 72,195 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 4 2 4 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" id="path7851" inkscape:connector-curvature="0" inkscape:label="#path3178"> @@ -13957,7 +13832,7 @@ inkscape:label="#path3178" inkscape:connector-curvature="0" id="path7857" - d="m 168,197 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 2,0 c -0.5,1.25 -1.75,2.25 -3,3 l 4,0 2,0 4,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 l 2,0 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" + d="m 168,197 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 2 c -0.5,1.25 -1.75,2.25 -3,3 h 4 2 4 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 h 2 c -1.25,-0.75 -2.5,-1.75 -3,-3 z" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none" sodipodi:nodetypes="cccccccccccccc"> <title @@ -13976,14 +13851,8 @@ id="path7863" inkscape:connector-curvature="0" /> <path - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 36,413 0,-2 2,0 0,-2 2,0 0,-2 2,0 0,3 -3,3 z" - id="path7896" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cccccccccc" /> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 23.50391,497 c -0.19102,-10e-4 -0.36611,0.1063 -0.45118,0.27734 C 22.14218,499.09846 20.38889,500 17.5,500 l 0,1 5.5,0 0,3.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-3.5 5.5,0 0,-1 c -2.88889,0 -4.64218,-0.90154 -5.55273,-2.72266 -0.0838,-0.16852 -0.25516,-0.2757 -0.44336,-0.27734 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 23.50391,497 c -0.19102,-10e-4 -0.36611,0.1063 -0.45118,0.27734 C 22.14218,499.09846 20.38889,500 17.5,500 v 1 H 23 v 3.5 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 V 501 h 5.5 v -1 c -2.88889,0 -4.64218,-0.90154 -5.55273,-2.72266 -0.0838,-0.16852 -0.25516,-0.2757 -0.44336,-0.27734 z" id="umbrella" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccsssccccc" @@ -13996,13 +13865,13 @@ id="rect7913" width="3" height="10" - x="134" - y="451" + x="694" + y="515" rx="1.4558058" ry="1.5" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 135.45581,467 0.0884,0 c 0.80652,0 1.45581,0.669 1.45581,1.5 l 0,7 c 0,0.831 -0.64929,1.5 -1.45581,1.5 l -0.0884,0 C 134.64929,477 134,476.331 134,475.5 l 0,-7 c 0,-0.831 0.64929,-1.5 1.45581,-1.5 z" + d="m 695.45581,531 h 0.0884 c 0.80652,0 1.45581,0.669 1.45581,1.5 v 7 c 0,0.831 -0.64929,1.5 -1.45581,1.5 h -0.0884 C 694.64929,541 694,540.331 694,539.5 v -7 c 0,-0.831 0.64929,-1.5 1.45581,-1.5 z" id="rectangle_vertical_rounded" inkscape:connector-curvature="0"> <title @@ -14011,48 +13880,26 @@ <rect ry="1.5" rx="1.4558058" - y="451" - x="150" + y="515" + x="710" height="10" width="3" id="rect7919" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 145.91797,461.08203 a 1.50015,1.50015 0 0 0 0.01,0.01 1.50015,1.50015 0 0 0 0.11133,0.0996 1.50015,1.50015 0 0 0 0.1211,0.0879 1.50015,1.50015 0 0 0 0.13086,0.0762 1.50015,1.50015 0 0 0 0.13671,0.0605 1.50015,1.50015 0 0 0 0.14063,0.0488 1.50015,1.50015 0 0 0 0.14648,0.0332 1.50015,1.50015 0 0 0 0.14844,0.0195 1.50015,1.50015 0 0 0 0.15039,0.004 1.50015,1.50015 0 0 0 0.15039,-0.0117 1.50015,1.50015 0 0 0 0.14649,-0.0254 1.50015,1.50015 0 0 0 0.14453,-0.041 1.50015,1.50015 0 0 0 0.14062,-0.0547 1.50015,1.50015 0 0 0 0.13282,-0.0684 1.50015,1.50015 0 0 0 0.125,-0.0801 1.50015,1.50015 0 0 0 0.11718,-0.0937 1.50015,1.50015 0 0 0 0.0899,-0.0859 l 8,-8 a 1.50015,1.50015 0 0 0 0.10156,-0.10937 1.50015,1.50015 0 0 0 0.0918,-0.11914 1.50015,1.50015 0 0 0 0.0781,-0.12891 1.50015,1.50015 0 0 0 0.0644,-0.13477 1.50015,1.50015 0 0 0 0.0508,-0.14062 1.50015,1.50015 0 0 0 0.0371,-0.14649 1.50015,1.50015 0 0 0 0.0215,-0.14843 1.50015,1.50015 0 0 0 0.008,-0.14844 1.50015,1.50015 0 0 0 -0.008,-0.15039 1.50015,1.50015 0 0 0 -0.0215,-0.14844 1.50015,1.50015 0 0 0 -0.0371,-0.14453 1.50015,1.50015 0 0 0 -0.0527,-0.14062 1.50015,1.50015 0 0 0 -0.0644,-0.13477 1.50015,1.50015 0 0 0 -0.0801,-0.12891 1.50015,1.50015 0 0 0 -0.0898,-0.11914 1.50015,1.50015 0 0 0 -0.0859,-0.0918 l -10.15625,10.15625 z" + d="m 705.91797,525.08203 a 1.50015,1.50015 0 0 0 0.01,0.01 1.50015,1.50015 0 0 0 0.11133,0.0996 1.50015,1.50015 0 0 0 0.1211,0.0879 1.50015,1.50015 0 0 0 0.13086,0.0762 1.50015,1.50015 0 0 0 0.13671,0.0605 1.50015,1.50015 0 0 0 0.14063,0.0488 1.50015,1.50015 0 0 0 0.14648,0.0332 1.50015,1.50015 0 0 0 0.14844,0.0195 1.50015,1.50015 0 0 0 0.15039,0.004 1.50015,1.50015 0 0 0 0.15039,-0.0117 1.50015,1.50015 0 0 0 0.14649,-0.0254 1.50015,1.50015 0 0 0 0.14453,-0.041 1.50015,1.50015 0 0 0 0.14062,-0.0547 1.50015,1.50015 0 0 0 0.13282,-0.0684 1.50015,1.50015 0 0 0 0.125,-0.0801 1.50015,1.50015 0 0 0 0.11718,-0.0937 1.50015,1.50015 0 0 0 0.0899,-0.0859 l 8,-8 a 1.50015,1.50015 0 0 0 0.10156,-0.10937 1.50015,1.50015 0 0 0 0.0918,-0.11914 1.50015,1.50015 0 0 0 0.0781,-0.12891 1.50015,1.50015 0 0 0 0.0644,-0.13477 1.50015,1.50015 0 0 0 0.0508,-0.14062 1.50015,1.50015 0 0 0 0.0371,-0.14649 1.50015,1.50015 0 0 0 0.0215,-0.14843 1.50015,1.50015 0 0 0 0.008,-0.14844 1.50015,1.50015 0 0 0 -0.008,-0.15039 1.50015,1.50015 0 0 0 -0.0215,-0.14844 1.50015,1.50015 0 0 0 -0.0371,-0.14453 1.50015,1.50015 0 0 0 -0.0527,-0.14062 1.50015,1.50015 0 0 0 -0.0644,-0.13477 1.50015,1.50015 0 0 0 -0.0801,-0.12891 1.50015,1.50015 0 0 0 -0.0898,-0.11914 1.50015,1.50015 0 0 0 -0.0859,-0.0918 l -10.15625,10.15625 z" id="path7925" inkscape:connector-curvature="0" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 151.45508,467 C 150.64856,467 150,467.669 150,468.5 l 0,3.79297 -3.35352,3.35351 a 0.50005,0.50005 0 1 0 0.70704,0.70704 l 8,-8 a 0.50005,0.50005 0 0 0 -0.36329,-0.85743 0.50005,0.50005 0 0 0 -0.34375,0.15039 L 153,469.29297 153,468.5 c 0,-0.831 -0.64856,-1.5 -1.45508,-1.5 z m 1.54492,5.12109 -3,3 0,0.37891 c 0,0.831 0.64856,1.5 1.45508,1.5 l 0.0898,0 C 152.35144,477 153,476.331 153,475.5 Z" + d="M 711.45508,531 C 710.64856,531 710,531.669 710,532.5 v 3.79297 l -3.35352,3.35351 a 0.50005,0.50005 0 1 0 0.70704,0.70704 l 8,-8 a 0.50005,0.50005 0 0 0 -0.36329,-0.85743 0.50005,0.50005 0 0 0 -0.34375,0.15039 L 713,533.29297 V 532.5 c 0,-0.831 -0.64856,-1.5 -1.45508,-1.5 z m 1.54492,5.12109 -3,3 V 539.5 c 0,0.831 0.64856,1.5 1.45508,1.5 h 0.0898 C 712.35144,541 713,540.331 713,539.5 Z" id="rectangle_vertical_rounded_crossed" inkscape:label="#rect7927" inkscape:connector-curvature="0"> <title id="title7939">crossed vertical rounded rectangle - - - @@ -14104,7 +13951,7 @@ width="6" height="5" x="40" - y="70" + y="68" rx="2" ry="2" /> @@ -14188,7 +14035,7 @@ @@ -14538,7 +14385,7 @@ id="rect63032" width="2" height="13" - x="55" + x="71" y="290" ry="0.5" rx="0.5" /> @@ -14547,7 +14394,7 @@ id="rect63034" width="2" height="13" - x="87" + x="103" y="290" ry="0.5" rx="0.5" /> @@ -14556,7 +14403,7 @@ id="rect63036" width="2" height="13" - x="103" + x="119" y="290" ry="0.5" rx="0.5" /> @@ -14565,7 +14412,7 @@ id="rect63038" width="2" height="13" - x="119" + x="135" y="290" ry="0.5" rx="0.5" /> @@ -14574,7 +14421,7 @@ id="rect63040" width="2" height="14" - x="135" + x="151" y="289" ry="0.5" rx="0.5" /> @@ -14583,7 +14430,7 @@ id="rect63042" width="2" height="11" - x="151" + x="167" y="292" ry="0.49999997" rx="0.5" /> @@ -14592,7 +14439,7 @@ id="rect63044" width="2" height="13" - x="167" + x="183" y="290" ry="0.5" rx="0.5" /> @@ -14601,81 +14448,93 @@ id="rect63046" width="2" height="13" - x="183" + x="199" y="290" ry="0.5" rx="0.5" /> + d="M 7.5,306 C 7.223,306 7,306.223 7,306.5 V 307 H 2.5 A 0.5,0.5 0 0 0 2,307.5 0.5,0.5 0 0 0 2.5,308 H 7 v 10.5 c 0,0.277 0.223,0.5 0.5,0.5 h 1 C 8.777,319 9,318.777 9,318.5 V 308 h 4.5 A 0.5,0.5 0 0 0 14,307.5 0.5,0.5 0 0 0 13.5,307 H 9 v -0.5 C 9,306.223 8.777,306 8.5,306 Z m -5,2.5 A 0.5,0.5 0 0 0 2,309 v 1 A 0.5,0.5 0 0 0 2.5,310.5 0.5,0.5 0 0 0 3,310 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z m 11,0 A 0.5,0.5 0 0 0 13,309 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z" + inkscape:connector-curvature="0"> + one-level transmission pole + + d="m 23.5,306 c -0.277,0 -0.5,0.223 -0.5,0.5 v 0.5 h -4.5 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 H 23 v 1 h -4.5 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 H 23 v 8.5 c 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 V 310 h 4.5 A 0.5,0.5 0 0 0 30,309.5 0.5,0.5 0 0 0 29.5,309 H 25 v -1 h 4.5 A 0.5,0.5 0 0 0 30,307.5 0.5,0.5 0 0 0 29.5,307 H 25 v -0.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m -5,4.5 A 0.5,0.5 0 0 0 18,311 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z m 11,0 A 0.5,0.5 0 0 0 29,311 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z" + inkscape:connector-curvature="0"> + two-level transmission pole + + d="m 66.5,306 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 H 71 v 1 h -4.5 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 H 71 v 1 h -4.5 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 H 71 v 7.5 c 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 V 311 h 4.5 A 0.5,0.5 0 0 0 78,310.5 0.5,0.5 0 0 0 77.5,310 H 73 v -1 h 4.5 A 0.5,0.5 0 0 0 78,308.5 0.5,0.5 0 0 0 77.5,308 H 73 v -1 h 4.5 A 0.5,0.5 0 0 0 78,306.5 0.5,0.5 0 0 0 77.5,306 h -5 -1 z m 0,5.5 A 0.5,0.5 0 0 0 66,312 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z m 11,0 A 0.5,0.5 0 0 0 77,312 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z" + inkscape:connector-curvature="0"> + three-level transmission pole + + d="m 103.5,306 c -0.277,0 -0.5,0.223 -0.5,0.5 v 0.5 h -4.5 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 h 4.5 v 1 h -4.5 a 0.5,0.5 0 0 0 -0.5,0.5 0.5,0.5 0 0 0 0.5,0.5 h 4.5 v 8.5 c 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 V 310 h 4.5 a 0.5,0.5 0 0 0 0.5,-0.5 0.5,0.5 0 0 0 -0.5,-0.5 H 105 v -2.5 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m -5,4.5 A 0.5,0.5 0 0 0 98,311 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z m 11,0 A 0.5,0.5 0 0 0 109,311 v 1 a 0.5,0.5 0 0 0 0.5,0.5 0.5,0.5 0 0 0 0.5,-0.5 v -1 a 0.5,0.5 0 0 0 -0.5,-0.5 z" + inkscape:connector-curvature="0"> + asymmetric transmission pole + + d="M 55.5,166.5 H 54 l -0.5,0.5" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> @@ -14698,7 +14557,7 @@ ry="0" rx="0" /> @@ -14741,7 +14600,7 @@ id="rect6446" width="4" height="4" - x="243" + x="259" y="168" rx="2" ry="2" /> @@ -14749,7 +14608,7 @@ ry="2" rx="2" y="168" - x="249" + x="265" height="4" width="4" id="rect6448" @@ -14757,7 +14616,7 @@ @@ -14792,7 +14651,7 @@ ry="1" /> @@ -14819,16 +14678,16 @@ sodipodi:nodetypes="ccccccccccccccccccc" inkscape:connector-curvature="0" id="path6514" - d="m 216,195 -1,2 -1,-1 0,2.5 -1.5,-1 0,1.5 -1.5,0 2.89844,2.80078 L 213,203 l 3,0 3,0 -0.89844,-1.19922 L 221,199 l -1.5,0 0,-1.5 -1.5,1 0,-2.5 -1,1 z" + d="m 216,195 -1,2 -1,-1 v 2.5 l -1.5,-1 V 199 H 211 l 2.89844,2.80078 L 213,203 h 3 3 L 218.10156,201.80078 221,199 h -1.5 v -1.5 l -1.5,1 V 196 l -1,1 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -14916,12 +14775,12 @@ ry="0.5" /> @@ -15006,7 +14865,7 @@ sodipodi:nodetypes="ccscscccc" inkscape:connector-curvature="0" id="path6733" - d="m 578,328.5 0.50001,0 c 1,0 1.49999,-0.5 2,-1.5 0.49999,1 1,1.5 2,1.5 1,0 1.49999,-0.5 2,-1.5 0.49999,1 1,1.5 2,1.5 1,0 1.49999,-0.5 2,-1.5 0.49999,1 1,1.5 2,1.5 l 0.49999,0" + d="m 578,328.5 h 0.50001 c 1,0 1.49999,-0.5 2,-1.5 0.49999,1 1,1.5 2,1.5 1,0 1.49999,-0.5 2,-1.5 0.49999,1 1,1.5 2,1.5 1,0 1.49999,-0.5 2,-1.5 0.49999,1 1,1.5 2,1.5 H 591" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.0999999;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -15284,78 +15143,78 @@ rx="0.5" /> @@ -15526,7 +15385,7 @@ y="0" xlink:href="#path18760" id="use18762" - transform="matrix(0.30892279,0.95108712,-0.95108712,0.30892279,595.81143,26.021079)" + transform="rotate(72.005675,280.00011,422.99988)" width="100%" height="100%" inkscape:transform-center-x="0.4755699" @@ -15537,7 +15396,7 @@ y="0" xlink:href="#path18760" id="use18764" - transform="matrix(-0.80939702,0.58726184,-0.58726184,-0.80939702,755.04378,600.94077)" + transform="rotate(144.03706,280.00057,422.99971)" width="100%" height="100%" inkscape:transform-center-x="1.1755676" @@ -15548,7 +15407,7 @@ y="0" xlink:href="#path18760" id="use18766" - transform="matrix(-0.80852015,-0.5884685,0.5884685,-0.80852015,257.46405,929.7776)" + transform="rotate(-143.9516,280.00068,423.0011)" width="100%" height="100%" inkscape:transform-center-x="0.25027885" @@ -15559,7 +15418,7 @@ y="0" xlink:href="#path18760" id="use18768" - transform="matrix(0.31356777,-0.94956583,0.94956583,0.31356777,-209.46718,556.24917)" + transform="rotate(-71.725627,280.00592,423.00624)" width="100%" height="100%" inkscape:transform-center-x="-0.954358" @@ -15571,9 +15430,9 @@ cy="-459.31671" cx="-224.39595" id="circle18774" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999997;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.0999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -15866,9 +15725,10 @@ sodipodi:ry="2" sodipodi:start="3.1415927" sodipodi:end="4.712389" - d="m 597,324 a 2,2 0 0 1 2,-2 l 0,2 z" /> + d="m 597,324 a 2,2 0 0 1 2,-2 v 2 z" + sodipodi:arc-type="slice" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> + d="m 598,324 a 1,1 0 0 1 1,-1 v 1 z" + sodipodi:arc-type="slice" /> + d="m 603,324 a 1,1 0 0 1 1,-1 v 1 z" + sodipodi:arc-type="slice" /> @@ -15966,15 +15829,15 @@ id="rect22088" width="3" height="2" - x="3" - y="101" + x="51" + y="581" rx="1" ry="1" /> + d="m 142,586.5 c 0,0.277 -0.223,0.5 -0.5,0.5 h -7 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -1 c 0,-0.277 0.22579,-0.53917 0.5,-0.5 l 7,1 c 0.27422,0.0392 0.5,0.223 0.5,0.5" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -16208,79 +16071,82 @@ ry="0" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + inkscape:connector-curvature="0"> + bowling ball + + d="m 544.5,229 v 1 h 2.5 c 1,0 1.34826,4.04477 2,6 l -1,1 h 1 v -2 c 0,-1 1,-2 2,-2 h 3 c 1,0 2,2 2.5,3 -0.5,1 -1,1 -1,1 h 1 c 0,0 0.0502,-1.79931 0,-2 -0.5,-2 0.5,-4 -0.5,-4 -3,0 -7,-1 -8,-2.5 l -1,-1.5 v 1.5 z" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + d="m 577,229 0.5,1 2.5,-0.5 c 0.5,1 1,2.5 1,3.5 0,1.5 0.5,3 -0.5,3.5 h 1 V 233 c 1.5,0 2.66667,-0.0854 4,-0.5 1.9098,-0.59386 1.5,1.5 2.5,1.5 0,1.5 0.5,2 -0.5,2.5 h 1 v -2 -2 c 0,-1 -0.5,-2 -1.5,-2 -3,0 -5.5,-1 -6.5,-2.5 l -1,-1.5 v 1.5 z" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> @@ -16334,7 +16200,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -16357,7 +16223,7 @@ sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path22612" - d="m 622,333.75 c -1,1 -2,1 -3,0 -1,1 -2,1 -3,0 m -1,-0.25 -5,0" + d="m 622,333.75 c -1,1 -2,1 -3,0 -1,1 -2,1 -3,0 m -1,-0.25 h -5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - - - - - @@ -16515,7 +16351,7 @@ id="rect22709" width="2" height="4" - x="634" + x="602" y="226" rx="0.46875" ry="0.5" /> @@ -16524,7 +16360,7 @@ id="rect22711" width="4" height="2" - x="633" + x="601" y="227" rx="0.46875" ry="0.5" /> @@ -16532,7 +16368,7 @@ r="5" cy="71.5" cx="615.5" - style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;opacity:0.2" + style="opacity:0.2;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none" id="circle22757" /> + transform="rotate(45)" /> + transform="rotate(45)" /> @@ -16602,7 +16438,7 @@ ry="0.5" /> @@ -16617,31 +16453,31 @@ rx="0" /> @@ -16652,7 +16488,7 @@ ry="0.5" rx="0.5" y="458" - x="357" + x="5" height="2" width="4" id="rect22839" @@ -16661,7 +16497,7 @@ ry="0.5" rx="0.5" y="455" - x="359" + x="7" height="2" width="4" id="rect22841" @@ -16670,7 +16506,7 @@ ry="0.5" rx="0.5" y="458" - x="362" + x="10" height="2" width="3" id="rect22843" @@ -16679,7 +16515,7 @@ ry="0.5" rx="0.5" y="455" - x="354" + x="2" height="2" width="4" id="rect22845" @@ -16688,7 +16524,7 @@ ry="0.5" rx="0.5" y="452" - x="356" + x="4" height="2" width="4" id="rect22847" @@ -16697,7 +16533,7 @@ ry="0.5" rx="0.5" y="452" - x="361" + x="9" height="2" width="4" id="rect22849" @@ -16706,7 +16542,7 @@ ry="0.5" rx="0.5" y="458" - x="354" + x="2" height="2" width="2" id="rect22851" @@ -16715,7 +16551,7 @@ ry="0.5" rx="0.5" y="452" - x="354" + x="2" height="2" width="1" id="rect22853" @@ -16724,7 +16560,7 @@ ry="0.5" rx="0.5" y="455" - x="364" + x="12" height="2" width="1" id="rect22855" @@ -16734,34 +16570,35 @@ id="rect22857" width="0.5" height="2" - x="354" + x="2" y="452" /> + inkscape:label="#rect22867" + inkscape:connector-curvature="0" /> + d="m 682,-327.5 a 1.5,1.5 0 0 1 -0.75,1.29904 1.5,1.5 0 0 1 -1.5,0 A 1.5,1.5 0 0 1 679,-327.5 h 1.5 z" + transform="scale(1,-1)" + sodipodi:arc-type="slice" /> + d="m 681,-327.5 a 0.5,0.5 0 0 1 -0.25,0.43301 0.5,0.5 0 0 1 -0.5,0 A 0.5,0.5 0 0 1 680,-327.5 h 0.5 z" + transform="scale(1,-1)" + sodipodi:arc-type="slice" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> fountain <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 678.46484,342 A 1.5,1.5 0 0 0 677.75,342.20117 1.5,1.5 0 0 0 677,343.5 l 0,0.5 1,0 0,-0.5 a 0.5,0.5 0 0 1 0.25,-0.43359 0.5,0.5 0 0 1 0.5,0 A 0.5,0.5 0 0 1 679,343.5 l 0,1 1,0 0,-1 a 0.5,0.5 0 0 1 0.25,-0.43359 0.5,0.5 0 0 1 0.5,0 A 0.5,0.5 0 0 1 681,343.5 l 0,0.5 1,0 0,-0.5 a 1.5,1.5 0 0 0 -0.75,-1.29883 1.5,1.5 0 0 0 -0.71484,-0.20117 1.5,1.5 0 0 0 -0.78516,0.20117 1.5,1.5 0 0 0 -0.25,0.1836 1.5,1.5 0 0 0 -0.25,-0.1836 A 1.5,1.5 0 0 0 678.46484,342 Z M 675.5,345 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 0.5,0 2,2 0,1 -0.5,0 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 4,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -0.5,0 0,-1 2,-2 0.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -8,0 z" + d="M 678.46484,342 A 1.5,1.5 0 0 0 677.75,342.20117 1.5,1.5 0 0 0 677,343.5 v 0.5 h 1 v -0.5 a 0.5,0.5 0 0 1 0.25,-0.43359 0.5,0.5 0 0 1 0.5,0 A 0.5,0.5 0 0 1 679,343.5 v 1 h 1 v -1 a 0.5,0.5 0 0 1 0.25,-0.43359 0.5,0.5 0 0 1 0.5,0 A 0.5,0.5 0 0 1 681,343.5 v 0.5 h 1 v -0.5 a 1.5,1.5 0 0 0 -0.75,-1.29883 1.5,1.5 0 0 0 -0.71484,-0.20117 1.5,1.5 0 0 0 -0.78516,0.20117 1.5,1.5 0 0 0 -0.25,0.1836 1.5,1.5 0 0 0 -0.25,-0.1836 A 1.5,1.5 0 0 0 678.46484,342 Z M 675.5,345 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 0.5 l 2,2 v 1 h -0.5 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 4 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 H 681 v -1 l 2,-2 h 0.5 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" id="fountain_bubbler" inkscape:label="#rect23133" inkscape:connector-curvature="0"> @@ -16976,31 +16817,31 @@ id="title25167">bubbler fountain + d="m 709.5,-323.5 a 1,1 0 0 1 -0.70711,-0.29289 A 1,1 0 0 1 708.5,-324.5 h 1 z" + transform="scale(1,-1)" + sodipodi:arc-type="slice" /> + transform="scale(-1)" + sodipodi:arc-type="slice" /> + d="M 710,334 A 3,3 0 0 1 707.87868,333.12132 3,3 0 0 1 707,331 h 3 z" + sodipodi:arc-type="slice" /> + transform="scale(-1,1)" + sodipodi:arc-type="slice" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> + d="m -617.5,-229.5 a 1,1 0 0 1 -0.70711,-0.29289 A 1,1 0 0 1 -618.5,-230.5 h 1 z" + sodipodi:arc-type="slice" /> + y="170" /> <circle - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path24857" cx="252.50002" cy="521.5" @@ -17326,9 +17173,9 @@ cy="521.5" cx="249.50002" id="circle24859" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="circle24861" cx="245.50002" cy="521.5" @@ -17338,10 +17185,10 @@ cy="521.5" cx="242.50002" id="circle24863" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="fill:#000000;stroke:none;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none" - d="M 241.5 532 C 241.223 532 241 532.223 241 532.5 L 241 538.5 C 241 538.777 241.223 539 241.5 539 L 246.5 539 C 246.777 539 247 538.777 247 538.5 L 247 532.5 C 247 532.223 246.777 532 246.5 532 L 241.5 532 z M 248.5 532 C 248.223 532 248 532.223 248 532.5 L 248 538.5 C 248 538.777 248.223 539 248.5 539 L 253.5 539 C 253.777 539 254 538.777 254 538.5 L 254 532.5 C 254 532.223 253.777 532 253.5 532 L 248.5 532 z M 242 533 L 246 533 L 246 536 L 242 536 L 242 533 z M 249 533 L 253 533 L 253 536 L 249 536 L 249 533 z M 242.5 536.77344 A 0.72727275 0.72727275 0 0 1 243.22656 537.5 A 0.72727275 0.72727275 0 0 1 242.5 538.22656 A 0.72727275 0.72727275 0 0 1 241.77344 537.5 A 0.72727275 0.72727275 0 0 1 242.5 536.77344 z M 245.5 536.77344 A 0.72727275 0.72727275 0 0 1 246.22656 537.5 A 0.72727275 0.72727275 0 0 1 245.5 538.22656 A 0.72727275 0.72727275 0 0 1 244.77344 537.5 A 0.72727275 0.72727275 0 0 1 245.5 536.77344 z M 249.5 536.77344 A 0.72727275 0.72727275 0 0 1 250.22656 537.5 A 0.72727275 0.72727275 0 0 1 249.5 538.22656 A 0.72727275 0.72727275 0 0 1 248.77344 537.5 A 0.72727275 0.72727275 0 0 1 249.5 536.77344 z M 252.5 536.77344 A 0.72727275 0.72727275 0 0 1 253.22656 537.5 A 0.72727275 0.72727275 0 0 1 252.5 538.22656 A 0.72727275 0.72727275 0 0 1 251.77344 537.5 A 0.72727275 0.72727275 0 0 1 252.5 536.77344 z " + d="m 241.5,532 c -0.277,0 -0.5,0.223 -0.5,0.5 v 6 c 0,0.277 0.223,0.5 0.5,0.5 h 5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -6 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 7,0 c -0.277,0 -0.5,0.223 -0.5,0.5 v 6 c 0,0.277 0.223,0.5 0.5,0.5 h 5 c 0.277,0 0.5,-0.223 0.5,-0.5 v -6 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m -6.5,1 h 4 v 3 h -4 z m 7,0 h 4 v 3 h -4 z m -6.5,3.77344 A 0.72727275,0.72727275 0 0 1 243.22656,537.5 0.72727275,0.72727275 0 0 1 242.5,538.22656 0.72727275,0.72727275 0 0 1 241.77344,537.5 0.72727275,0.72727275 0 0 1 242.5,536.77344 Z m 3,0 A 0.72727275,0.72727275 0 0 1 246.22656,537.5 0.72727275,0.72727275 0 0 1 245.5,538.22656 0.72727275,0.72727275 0 0 1 244.77344,537.5 0.72727275,0.72727275 0 0 1 245.5,536.77344 Z m 4,0 A 0.72727275,0.72727275 0 0 1 250.22656,537.5 0.72727275,0.72727275 0 0 1 249.5,538.22656 0.72727275,0.72727275 0 0 1 248.77344,537.5 0.72727275,0.72727275 0 0 1 249.5,536.77344 Z m 3,0 A 0.72727275,0.72727275 0 0 1 253.22656,537.5 0.72727275,0.72727275 0 0 1 252.5,538.22656 0.72727275,0.72727275 0 0 1 251.77344,537.5 0.72727275,0.72727275 0 0 1 252.5,536.77344 Z" id="buses" inkscape:label="#rect24886"> <title @@ -17350,7 +17197,7 @@ <path inkscape:connector-curvature="0" id="path24907" - d="m 242,39 3,-3 5,0 3,3 -5.5,5.5 z" + d="m 242,39 3,-3 h 5 l 3,3 -5.5,5.5 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" sodipodi:nodetypes="cccccc" /> <rect @@ -17373,7 +17220,7 @@ ry="0.5" /> <path style="opacity:1;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 245,52 -3,3 5,0 0,-3 -2,0 z m 3,0 0,3 5,0 -3,-3 -2,0 z m -5,4 4,4 0,-4 -4,0 z m 5,0 0,4 4,-4 -4,0 z" + d="m 245,52 -3,3 h 5 v -3 z m 3,0 v 3 h 5 l -3,-3 z m -5,4 4,4 v -4 z m 5,0 v 4 l 4,-4 z" id="path24913" inkscape:connector-curvature="0" /> <path @@ -17389,9 +17236,9 @@ height="9.5" width="1" id="rect24925" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect24927" width="1" height="9.5" @@ -17400,8 +17247,8 @@ rx="0" ry="0" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 135.51172 498 C 135.45402 497.999 135.39655 498.00699 135.3418 498.02539 L 129.3418 500.02539 C 128.80289 500.20442 128.93213 501.00063 129.5 501 L 130 501 L 130 510 L 131 510 L 131 501 L 140 501 L 140 510 L 141 510 L 141 501 L 141.5 501 C 142.06791 501.00066 142.19716 500.20439 141.6582 500.02539 L 135.6582 498.02539 C 135.6109 498.00969 135.5615 498.0011 135.51172 498 z " + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 135.51172,498 c -0.0577,-10e-4 -0.11517,0.007 -0.16992,0.0254 l -6,2 c -0.53891,0.17903 -0.40967,0.97524 0.1582,0.97461 h 0.5 v 9 h 1 v -9 h 9 v 9 h 1 v -9 h 0.5 c 0.56791,6.6e-4 0.69716,-0.79561 0.1582,-0.97461 l -6,-2 c -0.0473,-0.0157 -0.0967,-0.0243 -0.14648,-0.0254 z" id="shelter" inkscape:label="#path24929"> <title @@ -17491,8 +17338,8 @@ id="rect24999" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 182.48633,497.99414 c -0.35684,0.008 -0.59037,0.37699 -0.44531,0.70313 0.25458,0.59403 0.56027,1.20019 0.88671,1.80859 -1.58669,2.80921 -3.84335,5.70273 -5.78125,7.64062 -0.35308,0.34033 -0.0562,0.93267 0.42774,0.85352 l 4.42578,0 0,-1.5 c 0,-0.82843 0.67157,-1.5 1.5,-1.5 0.82843,0 1.5,0.67157 1.5,1.5 l 0,1.5 4.42969,0 c 0.48175,0.0741 0.77404,-0.5145 0.42383,-0.85352 -1.9379,-1.93789 -4.19456,-4.83141 -5.78125,-7.64062 0.32644,-0.6084 0.63213,-1.21456 0.88671,-1.80859 0.14767,-0.33225 -0.0974,-0.70602 -0.46093,-0.70313 -0.20042,0.002 -0.3803,0.12343 -0.45703,0.30859 -0.16048,0.37445 -0.34643,0.75884 -0.54102,1.14454 -0.19459,-0.3857 -0.38054,-0.77009 -0.54102,-1.14454 -0.0789,-0.19045 -0.26655,-0.31298 -0.47265,-0.30859 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 182.48633,497.99414 c -0.35684,0.008 -0.59037,0.37699 -0.44531,0.70313 0.25458,0.59403 0.56027,1.20019 0.88671,1.80859 -1.58669,2.80921 -3.84335,5.70273 -5.78125,7.64062 -0.35308,0.34033 -0.0562,0.93267 0.42774,0.85352 H 182 v -1.5 c 0,-0.82843 0.67157,-1.5 1.5,-1.5 0.82843,0 1.5,0.67157 1.5,1.5 v 1.5 h 4.42969 c 0.48175,0.0741 0.77404,-0.5145 0.42383,-0.85352 -1.9379,-1.93789 -4.19456,-4.83141 -5.78125,-7.64062 0.32644,-0.6084 0.63213,-1.21456 0.88671,-1.80859 0.14767,-0.33225 -0.0974,-0.70602 -0.46093,-0.70313 -0.20042,0.002 -0.3803,0.12343 -0.45703,0.30859 -0.16048,0.37445 -0.34643,0.75884 -0.54102,1.14454 -0.19459,-0.3857 -0.38054,-0.77009 -0.54102,-1.14454 -0.0789,-0.19045 -0.26655,-0.31298 -0.47265,-0.30859 z" id="camp" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccssscccccccccc" @@ -17504,13 +17351,13 @@ sodipodi:nodetypes="ccccccccc" inkscape:connector-curvature="0" id="path25020" - d="m 260.5,163.5 -1,3 -1.5,1 0,1.5 11,0 0,-6 -11,0 0,0.5 z" + d="m 276.5,163.5 -1,3 -1.5,1 v 1.5 h 11 v -6 h -11 v 0.5 z" style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect ry="2" rx="2" y="168" - x="258" + x="274" height="4" width="4" id="rect25022" @@ -17520,7 +17367,7 @@ id="rect25024" width="4" height="4" - x="265" + x="281" y="168" rx="2" ry="2" /> @@ -17530,7 +17377,7 @@ id="rect25026" width="2" height="2" - x="259" + x="275" y="169" rx="1" /> <rect @@ -17539,12 +17386,12 @@ id="rect25028" width="2" height="2" - x="266" + x="282" y="169" rx="1" /> <path style="opacity:0.2;fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 285,169 c 0,1 -1,2 -2,2 l -9,0 0,-6 9,0 2,3 z" + d="m 301,168 c 0,1 -1,2 -2,2 h -9 v -6 h 9 l 2,3 z" id="path25054" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccc" /> @@ -17553,108 +17400,37 @@ id="rect25056" width="2" height="1" - x="285" - y="169" + x="301" + y="168" rx="0" ry="0" /> <rect ry="2" rx="2" - y="170" - x="275" + y="169" + x="291" height="4" width="4" id="rect25058" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect rx="1" - y="171" - x="276" + y="170" + x="292" height="2" width="2" id="rect25060" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" ry="1" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 274,180 c -0.55226,6e-5 -0.99994,0.44774 -1,1 l 0,6 c 6e-5,0.55226 0.44774,0.99994 1,1 l 1,0 c 0,-1.108 0.892,-2 2,-2 1.108,0 2,0.892 2,2 l 4,0 c 0.83333,0 1.5499,-0.38583 2.08203,-0.91797 0.30316,-0.30315 0.54704,-0.67168 0.71094,-1.08203 l 1.20703,0 0,-1 -1,0 0,-1 c -10e-6,-0.19742 -0.0585,-0.39043 -0.16797,-0.55469 l -2,-3 C 283.64656,180.16713 283.33434,180.00003 283,180 l -9,0 z m 0.5,1 3,0 c 0.277,0 0.5,0.223 0.5,0.5 l 0,2 c 0,0.277 -0.223,0.5 -0.5,0.5 l -3,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-2 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m 6,0 2,0 c 0.277,0 0.5,0.223 0.5,0.5 l 0,5 c 0,0.277 -0.223,0.5 -0.5,0.5 l -2,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-5 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m -3.5,6 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 0,-0.554 -0.446,-1 -1,-1 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 290,179 c -0.55226,6e-5 -0.99994,0.44774 -1,1 v 6 c 6e-5,0.55226 0.44774,0.99994 1,1 h 1 c 0,-1.108 0.892,-2 2,-2 1.108,0 2,0.892 2,2 h 4 c 0.83333,0 1.5499,-0.38583 2.08203,-0.91797 0.30316,-0.30315 0.54704,-0.67168 0.71094,-1.08203 H 303 v -1 h -1 v -1 c -10e-6,-0.19742 -0.0585,-0.39043 -0.16797,-0.55469 l -2,-3 C 299.64656,179.16713 299.33434,179.00003 299,179 Z m 0.5,1 h 3 c 0.277,0 0.5,0.223 0.5,0.5 v 2 c 0,0.277 -0.223,0.5 -0.5,0.5 h -3 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -2 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m 6,0 h 2 c 0.277,0 0.5,0.223 0.5,0.5 v 5 c 0,0.277 -0.223,0.5 -0.5,0.5 h -2 c -0.277,0 -0.5,-0.223 -0.5,-0.5 v -5 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m -3.5,6 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 0.554,0 1,-0.446 1,-1 0,-0.554 -0.446,-1 -1,-1 z" id="caravan" inkscape:label="#path25062" inkscape:connector-curvature="0"> <title id="title13093">caravan - - - - - - - - - - - - exchange - + d="M 5.8349365,553.75 A 2.5,2.5 0 0 1 8,552.5 a 2.5,2.5 0 0 1 2.165063,1.25" + sodipodi:arc-type="arc" /> + d="M 4.1028856,552.75 A 4.5,4.5 0 0 1 8,550.5 a 4.5,4.5 0 0 1 3.897114,2.25" + sodipodi:arc-type="arc" /> + d="M 2.3708348,551.75 A 6.5,6.5 0 0 1 8,548.5 a 6.5,6.5 0 0 1 5.629165,3.25" + sodipodi:arc-type="arc" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -17772,7 +17551,7 @@ id="rect25233" width="1" height="13" - x="629" + x="613" y="193.5" rx="0.5" ry="0.5" /> @@ -17781,7 +17560,7 @@ id="rect25235" width="1" height="13" - x="634" + x="618" y="193.5" rx="0.5" ry="0.5" /> @@ -17790,7 +17569,7 @@ id="rect25237" width="1" height="5" - x="645" + x="629" y="193" rx="0" ry="0" /> @@ -17799,34 +17578,34 @@ id="rect25239" width="5" height="1" - x="645" + x="629" y="193" /> hopscotch - - - - - - <rect y="194" - x="674" + x="658" height="1" width="12" id="rect25328" @@ -17995,7 +17718,7 @@ rx="0.5" ry="0.5" y="205" - x="676" + x="660" height="1" width="8" id="rect25330" @@ -18010,7 +17733,7 @@ ry="1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 293.5,197.5 0,2.5 c 0,1 0.5,1.5 1.5,1.5 0.11447,0 0.38477,0 0.5,0" + d="m 293.5,197.5 v 2.5 c 0,1 0.5,1.5 1.5,1.5 0.11447,0 0.38477,0 0.5,0" id="path11666" inkscape:connector-curvature="0" sodipodi:nodetypes="cssc" /> @@ -18018,7 +17741,7 @@ sodipodi:nodetypes="cssc" inkscape:connector-curvature="0" id="path12516" - d="m 298.5,196.5 0,2.5 c 0,1 -0.5,1.5 -1.5,1.5 -0.11447,0 -0.38477,0 -0.5,0" + d="m 298.5,196.5 v 2.5 c 0,1 -0.5,1.5 -1.5,1.5 -0.11447,0 -0.38477,0 -0.5,0" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" @@ -18031,18 +17754,18 @@ rx="0" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 312,205 0,-10" + d="M 312,205 V 195" id="path12520" inkscape:connector-curvature="0" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 312.5,202 0.5,0 c 2,0 3,-1 3,-3 l 0,-1" + d="m 312.5,202 h 0.5 c 2,0 3,-1 3,-3 v -1" id="path12522" inkscape:connector-curvature="0" sodipodi:nodetypes="cssc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 311.5,200 -0.5,0 c -2,0 -3,-1 -3,-3 l 0,-1" + d="M 311.5,200 H 311 c -2,0 -3,-1 -3,-3 v -1" id="path12524" inkscape:connector-curvature="0" sodipodi:nodetypes="cssc" /> @@ -18056,19 +17779,19 @@ <path inkscape:connector-curvature="0" id="path12904" - d="m 328,205 0,-10" + d="M 328,205 V 195" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cssc" inkscape:connector-curvature="0" id="path12906" - d="m 327.5,202 -1.5,0 c -1,0 -2,-1 -2,-2 l 0,-2" + d="M 327.5,202 H 326 c -1,0 -2,-1 -2,-2 v -2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cssc" inkscape:connector-curvature="0" id="path12908" - d="m 328.5,201 1.5,0 c 1,0 2,-1 2,-2 l 0,-2" + d="m 328.5,201 h 1.5 c 1,0 2,-1 2,-2 v -2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect y="204" @@ -18079,51 +17802,51 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" transform="scale(-1,1)" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 328.01562,209.98633 A 1.0001,1.0001 0 0 1 329,211 l 0,5 1,0 c 0.16667,0 0.4501,-0.11417 0.66797,-0.33203 C 330.88583,215.4501 331,215.16667 331,215 l 0,-2 a 1.0001,1.0001 0 0 1 1.01562,-1.01367 A 1.0001,1.0001 0 0 1 333,213 l 0,2 c 0,0.83333 -0.38583,1.5499 -0.91797,2.08203 C 331.5499,217.61417 330.83333,218 330,218 l -1,0 0,2 0,1 0,1 -0.83203,0 a 1.0001,1.0001 0 0 1 -0.32617,0 l -0.8418,0 0,-1 0,-1 0,-1 -1,0 c -0.83333,0 -1.5499,-0.38583 -2.08203,-0.91797 C 323.38583,217.5499 323,216.83333 323,216 l 0,-2 a 1.0001,1.0001 0 0 1 1.01562,-1.01367 A 1.0001,1.0001 0 0 1 325,214 l 0,2 c 0,0.16667 0.11417,0.4501 0.33203,0.66797 C 325.5499,216.88583 325.83333,217 326,217 l 1,0 0,-6 a 1.0001,1.0001 0 0 1 1.01562,-1.01367 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 328.01562,209.98633 A 1.0001,1.0001 0 0 1 329,211 v 5 h 1 c 0.16667,0 0.4501,-0.11417 0.66797,-0.33203 C 330.88583,215.4501 331,215.16667 331,215 v -2 a 1.0001,1.0001 0 0 1 1.01562,-1.01367 A 1.0001,1.0001 0 0 1 333,213 v 2 c 0,0.83333 -0.38583,1.5499 -0.91797,2.08203 C 331.5499,217.61417 330.83333,218 330,218 h -1 v 2 1 1 h -0.83203 a 1.0001,1.0001 0 0 1 -0.32617,0 H 327 v -1 -1 -1 h -1 c -0.83333,0 -1.5499,-0.38583 -2.08203,-0.91797 C 323.38583,217.5499 323,216.83333 323,216 v -2 a 1.0001,1.0001 0 0 1 1.01562,-1.01367 A 1.0001,1.0001 0 0 1 325,214 v 2 c 0,0.16667 0.11417,0.4501 0.33203,0.66797 C 325.5499,216.88583 325.83333,217 326,217 h 1 v -6 a 1.0001,1.0001 0 0 1 1.01562,-1.01367 z" id="cactus" inkscape:label="#path13056" inkscape:connector-curvature="0" /> <circle style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path13072" - cx="116" + cx="68" cy="170" r="1" /> <circle style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path13074" - cx="124" + cx="76" cy="170" r="1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 114,166 c 1,1 1,1.5 2.5,1.5 l 7,0 c 1.5,0 1.5,-0.5 2.5,-1.5" + d="m 66,166 c 1,1 1,1.5 2.5,1.5 h 7 C 77,167.5 77,167 78,166" id="path13076" inkscape:connector-curvature="0" sodipodi:nodetypes="cccc" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 125.99414,181.49219 a 0.50005,0.50005 0 0 0 -0.34766,0.15429 c -0.52946,0.52947 -0.79786,0.909 -1.02148,1.0879 C 124.40138,182.91327 124.19444,183 123.5,183 l -7,0 c -0.69444,0 -0.90138,-0.0867 -1.125,-0.26562 -0.22362,-0.1789 -0.49202,-0.55843 -1.02148,-1.0879 a 0.50005,0.50005 0 0 0 -0.35938,-0.15234 0.50005,0.50005 0 0 0 -0.34766,0.85938 c 0.47054,0.47053 0.70214,0.841 1.10352,1.1621 0.40138,0.32112 0.94444,0.48438 1.75,0.48438 l 7,0 c 0.80556,0 1.34862,-0.16327 1.75,-0.48438 0.40138,-0.3211 0.63298,-0.69157 1.10352,-1.1621 a 0.50005,0.50005 0 0 0 -0.35938,-0.86133 z M 116,185 a 1,1 0 0 0 -1,1 1,1 0 0 0 1,1 1,1 0 0 0 1,-1 1,1 0 0 0 -1,-1 z m 8,0 a 1,1 0 0 0 -1,1 1,1 0 0 0 1,1 1,1 0 0 0 1,-1 1,1 0 0 0 -1,-1 z" + d="m 77.99414,181.49219 a 0.50005,0.50005 0 0 0 -0.34766,0.15429 c -0.52946,0.52947 -0.79786,0.909 -1.02148,1.0879 C 76.40138,182.91327 76.19444,183 75.5,183 h -7 c -0.69444,0 -0.90138,-0.0867 -1.125,-0.26562 -0.22362,-0.1789 -0.49202,-0.55843 -1.02148,-1.0879 a 0.50005,0.50005 0 0 0 -0.35938,-0.15234 0.50005,0.50005 0 0 0 -0.34766,0.85938 c 0.47054,0.47053 0.70214,0.841 1.10352,1.1621 C 67.15138,183.83674 67.69444,184 68.5,184 h 7 c 0.80556,0 1.34862,-0.16327 1.75,-0.48438 0.40138,-0.3211 0.63298,-0.69157 1.10352,-1.1621 a 0.50005,0.50005 0 0 0 -0.35938,-0.86133 z M 68,185 a 1,1 0 0 0 -1,1 1,1 0 0 0 1,1 1,1 0 0 0 1,-1 1,1 0 0 0 -1,-1 z m 8,0 a 1,1 0 0 0 -1,1 1,1 0 0 0 1,1 1,1 0 0 0 1,-1 1,1 0 0 0 -1,-1 z" id="skateboard" inkscape:label="#circle13078" inkscape:connector-curvature="0" /> <rect ry="0.5" rx="0.5" - y="165" - x="274" + y="164" + x="290" height="3" width="4" id="rect13087" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect13089" width="3" height="6" - x="280" - y="165" + x="296" + y="164" rx="0.5" ry="0.5" /> <path @@ -18131,21 +17854,22 @@ id="path13095" sodipodi:type="arc" sodipodi:cx="359.5" - sodipodi:cy="72" + sodipodi:cy="71" sodipodi:rx="4.500001" sodipodi:ry="4.500001" sodipodi:start="0" sodipodi:end="3.1415927" - d="m 364,72 a 4.500001,4.500001 0 0 1 -2.25,3.897115 4.500001,4.500001 0 0 1 -4.5,0 A 4.500001,4.500001 0 0 1 355,72 l 4.5,0 z" /> + d="m 364,71 a 4.500001,4.500001 0 0 1 -2.25,3.897115 4.500001,4.500001 0 0 1 -4.5,0 A 4.500001,4.500001 0 0 1 355,71 h 4.5 z" + sodipodi:arc-type="slice" /> <path sodipodi:nodetypes="ccsscccc" inkscape:connector-curvature="0" id="path13101" - d="m 359.57812,66.640835 c -1.18495,0.451222 -1.5913,1.65797 -1.57812,2.859375 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36794,-1.345314 -0.70716,-1.797516 0.004,0.464275 -0.17444,0.809087 -0.41495,1.01099 -0.63673,-0.526641 -0.71388,-1.20138 -0.29977,-2.072849 z" + d="m 359.57812,65.640835 c -1.18495,0.451222 -1.5913,1.65797 -1.57812,2.859375 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36794,-1.345314 -0.70716,-1.797516 0.004,0.464275 -0.17444,0.809087 -0.41495,1.01099 -0.63673,-0.526641 -0.71388,-1.20138 -0.29977,-2.072849 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 359.57812,82.640625 C 358.39317,83.091847 357.98682,84.298595 358,85.5 c 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36781,-1.344673 -0.70703,-1.796875 0.004,0.464275 -0.17551,0.807863 -0.41602,1.009766 -0.63673,-0.526641 -0.71293,-1.200797 -0.29883,-2.072266 z M 355,88 a 4.500001,4.500001 0 0 0 0.9043,2.705078 l -0.87891,2.636719 a 0.50028276,0.50028276 0 0 0 0.94922,0.316406 l 0.7168,-2.150391 A 4.500001,4.500001 0 0 0 357.25,91.896484 4.500001,4.500001 0 0 0 359,92.466797 L 359,93.5 a 0.50005,0.50005 0 1 0 1,0 l 0,-1.03125 a 4.500001,4.500001 0 0 0 1.75,-0.572266 4.500001,4.500001 0 0 0 0.56055,-0.382812 l 0.71484,2.144531 a 0.50028276,0.50028276 0 0 0 0.94922,-0.316406 l -0.88086,-2.642578 A 4.500001,4.500001 0 0 0 364,88 l -9,0 z" + d="M 359.57812,81.640625 C 358.39317,82.091847 357.98682,83.298595 358,84.5 c 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36781,-1.344673 -0.70703,-1.796875 0.004,0.464275 -0.17551,0.807863 -0.41602,1.009766 -0.63673,-0.526641 -0.71293,-1.200797 -0.29883,-2.072266 z M 355,87 a 4.500001,4.500001 0 0 0 0.9043,2.705078 l -0.87891,2.636719 a 0.50028276,0.50028276 0 0 0 0.94922,0.316406 l 0.7168,-2.150391 A 4.500001,4.500001 0 0 0 357.25,90.896484 4.500001,4.500001 0 0 0 359,91.466797 V 92.5 a 0.50005,0.50005 0 1 0 1,0 v -1.03125 a 4.500001,4.500001 0 0 0 1.75,-0.572266 4.500001,4.500001 0 0 0 0.56055,-0.382812 l 0.71484,2.144531 a 0.50028276,0.50028276 0 0 0 0.94922,-0.316406 l -0.88086,-2.642578 A 4.500001,4.500001 0 0 0 364,87 Z" id="bbq" inkscape:label="#path13121" inkscape:connector-curvature="0"> @@ -18154,13 +17878,13 @@ </path> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 375.57812,68.640835 c -1.18495,0.451222 -1.5913,1.65797 -1.57812,2.859375 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36794,-1.345314 -0.70716,-1.797516 0.004,0.464275 -0.17444,0.809087 -0.41495,1.01099 -0.63673,-0.526641 -0.71388,-1.20138 -0.29977,-2.072849 z" + d="m 375.57812,66.640835 c -1.18495,0.451222 -1.5913,1.65797 -1.57812,2.859375 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36794,-1.345314 -0.70716,-1.797516 0.004,0.464275 -0.17444,0.809087 -0.41495,1.01099 -0.63673,-0.526641 -0.71388,-1.20138 -0.29977,-2.072849 z" id="path13141" inkscape:connector-curvature="0" sodipodi:nodetypes="ccsscccc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 371.5,74.5 8,3" + d="m 371.5,72.5 8,3" id="path13143" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -18168,26 +17892,26 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path13145" - d="m 371.5,77.5 8,-3" + d="m 371.5,75.5 8,-3" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 375.57812,84.640625 C 374.39317,85.091847 373.98682,86.298595 374,87.5 c 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36781,-1.344673 -0.70703,-1.796875 0.004,0.464275 -0.17551,0.807863 -0.41602,1.009766 -0.63673,-0.526641 -0.71293,-1.200797 -0.29883,-2.072266 z m -4.09765,5.357422 a 0.50005,0.50005 0 0 0 -0.15625,0.970703 l 2.75,1.03125 -2.75,1.03125 a 0.50062478,0.50062478 0 1 0 0.35156,0.9375 l 3.82422,-1.433594 3.82422,1.433594 a 0.50062478,0.50062478 0 0 0 0.35156,-0.9375 l -2.75,-1.03125 2.75,-1.03125 a 0.50005,0.50005 0 0 0 -0.16992,-0.970703 0.50005,0.50005 0 0 0 -0.18164,0.0332 L 375.5,91.464844 371.67578,90.03125 a 0.50005,0.50005 0 0 0 -0.19531,-0.0332 z" - id="firepit" + d="M 375.57812,82.640625 C 374.39317,83.091847 373.98682,84.298595 374,85.5 c 7e-5,0.535824 0.28596,1.030917 0.75,1.298828 0.4641,0.267949 1.0359,0.267949 1.5,0 0.46403,-0.267911 0.74992,-0.763004 0.75,-1.298828 0,-0.818561 -0.36781,-1.344673 -0.70703,-1.796875 0.004,0.464275 -0.17551,0.807863 -0.41602,1.009766 -0.63673,-0.526641 -0.71293,-1.200797 -0.29883,-2.072266 z m -4.09765,5.357422 a 0.50005,0.50005 0 0 0 -0.15625,0.970703 l 2.75,1.03125 -2.75,1.03125 a 0.50062478,0.50062478 0 1 0 0.35156,0.9375 l 3.82422,-1.433594 3.82422,1.433594 a 0.50062478,0.50062478 0 0 0 0.35156,-0.9375 l -2.75,-1.03125 2.75,-1.03125 a 0.50005,0.50005 0 0 0 -0.16992,-0.970703 0.50005,0.50005 0 0 0 -0.18164,0.0332 L 375.5,89.464844 371.67578,88.03125 a 0.50005,0.50005 0 0 0 -0.19531,-0.0332 z" + id="fire_pit" inkscape:label="#path13147" inkscape:connector-curvature="0"> <title - id="title13158">firepit + id="title13158">fire pit @@ -18196,8 +17920,8 @@ id="rect9556" width="1" height="2" - x="215" - y="296" + x="407" + y="232" rx="0.5" ry="0.5" /> + d="m 409,-227 a 1.5,1.5 0 0 1 -0.75,1.29904 1.5,1.5 0 0 1 -1.5,0 A 1.5,1.5 0 0 1 406,-227 h 1.5 z" + transform="scale(1,-1)" + sodipodi:arc-type="slice" /> + d="M 407.53516,242.5 A 1.5,1.5 0 0 0 406.75,242.70117 1.5,1.5 0 0 0 406,244 v 1 h -1 v 2 l 1,1 v 6 h 3 v -6 l 1,-1 v -2 h -1 v -1 A 1.5,1.5 0 0 0 408.25,242.70117 1.5,1.5 0 0 0 407.53516,242.5 Z M 407.5,248 c 0.277,0 0.5,0.223 0.5,0.5 v 0.5 0.5 0.5 h -0.5 -0.5 v -0.5 -0.5 -0.5 c 0,-0.277 0.223,-0.5 0.5,-0.5 z" + id="path9564" + inkscape:connector-curvature="0" /> @@ -18256,7 +17982,7 @@ id="rect9586" width="2" height="2" - x="231" + x="247" y="294" rx="1" ry="1" /> @@ -18265,13 +17991,13 @@ id="rect9588" width="2" height="2" - x="231" + x="247" y="295" rx="0" ry="0" /> + d="m 260,290 v 1 1 h 8 v -1 -1 h -2 v 1 h -1 v -1 h -2 v 1 h -1 v -1 z" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -18322,14 +18048,14 @@ ry="0" rx="0" y="297.5" - x="249" + x="265" height="1.5" width="1" id="rect9619" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1.0" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -18351,7 +18077,7 @@ id="rect9630" width="7" height="4" - x="260" + x="276" y="291" rx="0.5" ry="0.5" /> @@ -18360,18 +18086,18 @@ id="rect9632" width="5" height="2" - x="261" + x="277" y="292" rx="0" ry="0" /> + y="391" /> + style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + transform="rotate(90)" /> @@ -18588,7 +18314,7 @@ id="rect6991" width="4" height="1" - x="390" + x="406" y="301" rx="0.5" ry="0.5" /> @@ -18596,66 +18322,70 @@ style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path6993" sodipodi:type="arc" - sodipodi:cx="393.5" + sodipodi:cx="409.5" sodipodi:cy="260.5" sodipodi:rx="3" sodipodi:ry="3" sodipodi:start="5.4977871" sodipodi:end="0.78539816" sodipodi:open="true" - d="M 395.62132,258.37868 A 3,3 0 0 1 396.5,260.5 a 3,3 0 0 1 -0.87868,2.12132" /> + d="M 411.62132,258.37868 A 3,3 0 0 1 412.5,260.5 a 3,3 0 0 1 -0.87868,2.12132" + sodipodi:arc-type="arc" /> + d="M 410.20711,259.79289 A 1,1 0 0 1 410.5,260.5 a 1,1 0 0 1 -0.29289,0.70711" + sodipodi:open="true" + sodipodi:arc-type="arc" /> + transform="scale(-1,1)" + sodipodi:arc-type="arc" /> + transform="scale(-1,1)" + sodipodi:arc-type="arc" /> + inkscape:label="#path7118" + inkscape:connector-curvature="0"> pagoda - - - @@ -18763,42 +18460,42 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7169" - d="m 392.5,230.5 -2,-1" + d="m 200.5,326.5 -2,-1" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -18835,17 +18532,17 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7234" - d="m 358.5,269.5 2,-2" + d="m 374.5,269.5 2,-2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -18853,56 +18550,56 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7248" - d="m 374.5,264.5 2,2" + d="m 390.5,264.5 2,2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -18910,61 +18607,65 @@ style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path7346" sodipodi:type="arc" - sodipodi:cx="374.5" - sodipodi:cy="231.5" + sodipodi:cx="182.5" + sodipodi:cy="327.5" sodipodi:rx="3" sodipodi:ry="3" sodipodi:start="5.4977871" sodipodi:end="0.78539816" sodipodi:open="true" - d="M 376.62132,229.37868 A 3,3 0 0 1 377.5,231.5 a 3,3 0 0 1 -0.87868,2.12132" /> + d="M 184.62132,325.37868 A 3,3 0 0 1 185.5,327.5 a 3,3 0 0 1 -0.87868,2.12132" + sodipodi:arc-type="arc" /> + d="M 183.20711,326.79289 A 1,1 0 0 1 183.5,327.5 a 1,1 0 0 1 -0.29289,0.70711" + sodipodi:open="true" + sodipodi:arc-type="arc" /> + transform="scale(-1,1)" + sodipodi:arc-type="arc" /> + transform="scale(-1,1)" + sodipodi:arc-type="arc" /> @@ -18979,10 +18680,11 @@ sodipodi:start="0" sodipodi:end="3.1415927" sodipodi:open="true" - d="m 93,131 a 5,5 0 0 1 -2.5,4.33013 5,5 0 0 1 -5,0 A 5,5 0 0 1 83,131" /> + d="m 93,131 a 5,5 0 0 1 -2.5,4.33013 5,5 0 0 1 -5,0 A 5,5 0 0 1 83,131" + sodipodi:arc-type="arc" /> @@ -18990,11 +18692,11 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7378" - d="m 101,132 7,9" + d="m 101,131 7,9" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -19002,42 +18704,44 @@ sodipodi:nodetypes="cccccc" inkscape:connector-curvature="0" id="path7382" - d="m 120,142 0,-3 -5,0 5,-7.5 5,7.5 -5,0" + d="m 120,142 v -3 h -5 l 5,-7.5 5,7.5 h -5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="arc" /> + d="m 141,-140.9994 a 5,5 0 0 1 -2.5,4.33012 5,5 0 0 1 -5,0 5,5 0 0 1 -2.5,-4.33012" + transform="scale(1,-1)" + sodipodi:arc-type="arc" /> @@ -19045,11 +18749,11 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7392" - d="m 149,135 6,0" + d="m 149,134 h 6" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -19057,19 +18761,19 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7396" - d="m 156,133 0,8" + d="m 156,132 v 8" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> horizontal ladder @@ -19081,7 +18785,7 @@ id="rect7429" width="8" height="1" - x="406" + x="422" y="258" ry="0.5" rx="0.5" /> @@ -19090,7 +18794,7 @@ id="rect7431" width="4" height="1" - x="408" + x="424" y="259" rx="0.5" ry="0.5" /> @@ -19099,13 +18803,13 @@ id="rect7433" width="2" height="1" - x="407" + x="423" y="260" rx="0.5" ry="0.5" /> @@ -19122,7 +18826,7 @@ id="rect7441" width="1" height="1" - x="409" + x="425" y="260" rx="0.5" ry="0.5" /> @@ -19131,24 +18835,24 @@ id="rect7443" width="1" height="1" - x="412" + x="428" y="259" rx="0.5" ry="0.5" /> @@ -19186,14 +18890,14 @@ + transform="rotate(45)" /> @@ -19304,7 +19008,7 @@ height="5" width="1" id="rect7568" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -19430,96 +19134,45 @@ id="title7648">phone + transform="rotate(45)" /> Hi-Fi - - - - - - - - - @@ -19527,63 +19180,63 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7778" - d="m 171,131 0,9" + d="m 171,131 v 9" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> Japanese map symbol for fire station Japanese map symbol for kōban Japanese map symbol for court house or building Japanese map symbol for forest service office Japanese map symbol for shinto shrine <path - style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 228,499 c -1.108,0 -2,0.892 -2,2 l 0,1 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 l 4,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 0,-1 c 0,-1.108 -0.892,-2 -2,-2 l -1,0 z m 7,0 c -1.108,0 -2,0.892 -2,2 l 0,1 0,1.5 c 0,0.277 0.223,0.5 0.5,0.5 l 4,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1.5 0,-1 c 0,-1.108 -0.892,-2 -2,-2 l -1,0 z m -9,6 c -0.277,0 -0.5,0.21376 -0.5,0.47852 l 0,0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 l 2,0 0,1 -2.5,0 c -0.277,0 -0.5,0.21376 -0.5,0.47852 l 0,0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 l 13,0 c 0.277,0 0.5,-0.21376 0.5,-0.47852 l 0,-0.043 C 239,507.21376 238.777,507 238.5,507 l -2.5,0 0,-1 2,0 c 0.277,0 0.5,-0.21376 0.5,-0.47852 l 0,-0.043 C 238.5,505.21376 238.277,505 238,505 l -5,0 c -0.277,0 -0.5,0.21376 -0.5,0.47852 l 0,0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 l 2,0 0,1 -6,0 0,-1 2,0 c 0.277,0 0.5,-0.21376 0.5,-0.47852 l 0,-0.043 C 231.5,505.21376 231.277,505 231,505 l -5,0 z" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.7;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="m 228,499 c -1.108,0 -2,0.892 -2,2 v 1 1.5 c 0,0.277 0.223,0.5 0.5,0.5 h 4 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1.5 -1 c 0,-1.108 -0.892,-2 -2,-2 z m 7,0 c -1.108,0 -2,0.892 -2,2 v 1 1.5 c 0,0.277 0.223,0.5 0.5,0.5 h 4 c 0.277,0 0.5,-0.223 0.5,-0.5 v -1.5 -1 c 0,-1.108 -0.892,-2 -2,-2 z m -9,6 c -0.277,0 -0.5,0.21376 -0.5,0.47852 v 0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 h 2 v 1 h -2.5 c -0.277,0 -0.5,0.21376 -0.5,0.47852 v 0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 h 13 c 0.277,0 0.5,-0.21376 0.5,-0.47852 v -0.043 C 239,507.21376 238.777,507 238.5,507 H 236 v -1 h 2 c 0.277,0 0.5,-0.21376 0.5,-0.47852 v -0.043 C 238.5,505.21376 238.277,505 238,505 h -5 c -0.277,0 -0.5,0.21376 -0.5,0.47852 v 0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 h 2 v 1 h -6 v -1 h 2 c 0.277,0 0.5,-0.21376 0.5,-0.47852 v -0.043 C 231.5,505.21376 231.277,505 231,505 Z" id="bleachers" inkscape:label="#rect7962" inkscape:connector-curvature="0" /> @@ -19776,7 +19429,7 @@ rx="0.5" ry="0.47790298" /> <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect7985" width="5.0000153" height="5" @@ -19785,7 +19438,7 @@ rx="0.5" ry="0.47790301" /> <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000002;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect7987" width="4" height="3" @@ -19819,10 +19472,10 @@ height="3" width="4" id="rect7993" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000002;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 247.5 498.5 C 247.223 498.5 247 498.71376 247 498.97852 L 247 499 L 245.5 499 C 245.223 499 245 499.21376 245 499.47852 L 245 499.52148 C 245 499.78624 245.223 500 245.5 500 L 247 500 L 247 501 L 248 501 L 248 500 L 249.5 500 C 249.777 500 250 499.78624 250 499.52148 L 250 499.47852 C 250 499.21376 249.777 499 249.5 499 L 248 499 L 248 498.97852 C 248 498.71376 247.777 498.5 247.5 498.5 z M 242.5 502 C 242.223 502 242 502.21376 242 502.47852 L 242 506.52148 C 242 506.78624 242.223 507 242.5 507 C 242.777 507 243 506.78624 243 506.52148 L 243 506 L 245 506 L 245 506.52148 C 245 506.78624 245.223 507 245.5 507 L 249.5 507 C 249.777 507 250 506.78624 250 506.52148 L 250 506 L 252 506 L 252 506.52148 C 252 506.78624 252.223 507 252.5 507 C 252.777 507 253 506.78624 253 506.52148 L 253 502.47852 C 253 502.21376 252.777 502 252.5 502 C 252.223 502 252 502.21376 252 502.47852 L 252 503 L 250 503 L 250 502.47852 C 250 502.21376 249.777 502 249.5 502 L 245.5 502 C 245.223 502 245 502.21376 245 502.47852 L 245 503 L 243 503 L 243 502.47852 C 243 502.21376 242.777 502 242.5 502 z " + d="m 247.5,498.5 c -0.277,0 -0.5,0.21376 -0.5,0.47852 V 499 h -1.5 c -0.277,0 -0.5,0.21376 -0.5,0.47852 v 0.043 c 0,0.26476 0.223,0.47852 0.5,0.47852 h 1.5 v 1 h 1 v -1 h 1.5 c 0.277,0 0.5,-0.21376 0.5,-0.47852 v -0.043 C 250,499.21376 249.777,499 249.5,499 H 248 v -0.0215 C 248,498.71376 247.777,498.5 247.5,498.5 Z m -5,3.5 c -0.277,0 -0.5,0.21376 -0.5,0.47852 v 4.04296 c 0,0.26476 0.223,0.47852 0.5,0.47852 0.277,0 0.5,-0.21376 0.5,-0.47852 V 506 h 2 v 0.52148 c 0,0.26476 0.223,0.47852 0.5,0.47852 h 4 c 0.277,0 0.5,-0.21376 0.5,-0.47852 V 506 h 2 v 0.52148 c 0,0.26476 0.223,0.47852 0.5,0.47852 0.277,0 0.5,-0.21376 0.5,-0.47852 v -4.04296 C 253,502.21376 252.777,502 252.5,502 c -0.277,0 -0.5,0.21376 -0.5,0.47852 V 503 h -2 v -0.52148 C 250,502.21376 249.777,502 249.5,502 h -4 c -0.277,0 -0.5,0.21376 -0.5,0.47852 V 503 h -2 v -0.52148 C 243,502.21376 242.777,502 242.5,502 Z" id="pipeline" /> <rect ry="3.0517578e-05" @@ -19837,41 +19490,41 @@ style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path8026" cx="408" - cy="76" + cy="75" r="1.5" /> <circle r="1.5" - cy="76" + cy="75" cx="413" id="circle8028" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 409.5,76 1,-8.5" + d="m 409.5,75 1,-8.5" id="path8030" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 411.5,76 -1,-8.5" + d="m 411.5,75 -1,-8.5" id="path8032" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 402.5,77.5 0,-8 m 2,-2 1,0" + d="m 402.5,76.5 v -8 m 2,-2 h 1" id="path8036" inkscape:connector-curvature="0" sodipodi:nodetypes="cccc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 402.5,69.5 3,0" + d="m 402.5,68.5 h 3" id="path8038" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 402.5,71.5 3,0" + d="m 402.5,70.5 h 3" id="path8040" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -19879,11 +19532,11 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path8044" - d="m 402.5,73.5 3,0" + d="m 402.5,72.5 h 3" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle r="1.5" - cy="76" + cy="75" cx="424" id="circle8046" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -19891,52 +19544,52 @@ style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="circle8048" cx="429" - cy="76" + cy="75" r="1.5" /> <path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path8050" - d="m 425.5,76 0,-2.5 1,-1 1,-1 0,-4" + d="m 425.5,75 v -2.5 l 1,-1 1,-1 v -4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path8052" - d="m 427.5,76 0,-2.5 -1,-1 -1,-1 0,-4" + d="m 427.5,75 v -2.5 l -1,-1 -1,-1 v -4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cssc" inkscape:connector-curvature="0" id="path8054" - d="m 418.5,77.5 0,-8 c 0,-1.5 1,-2 2,-2 l 1,0" + d="m 418.5,76.5 v -8 c 0,-1.5 1,-2 2,-2 h 1" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path8056" - d="m 418.5,69.5 3,0" + d="m 418.5,68.5 h 3" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path8058" - d="m 418.5,71.5 3,0" + d="m 418.5,70.5 h 3" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 418.5,73.5 3,0" + d="m 418.5,72.5 h 3" id="path8060" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 410,73.5 1,0 -0.5,-2 z" + d="m 410,72.5 h 1 l -0.5,-2 z" id="path8062" inkscape:connector-curvature="0" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 408.9928,77.12443 407.5,78.5" + d="M 408.9928,76.12443 407.5,77.5" id="path8064" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -19951,7 +19604,7 @@ ry="1.4999999" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 435.5,78 2,0 1.5,-4 -5,0 z" + d="m 435.5,78 h 2 l 1.5,-4 h -5 z" id="path8078" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /> @@ -19964,7 +19617,7 @@ y="69" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 444.5,66.5 -1,1.5 0,5.5 1,0 z" + d="m 444.5,66.5 -1,1.5 v 5.5 h 1 z" id="path8084" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /> @@ -19984,7 +19637,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 436.5,69.5 -1.5,-2 3,0 z" + d="m 436.5,69.5 -1.5,-2 h 3 z" id="path8092" inkscape:connector-curvature="0" /> <path @@ -19992,13 +19645,14 @@ id="path8094" sodipodi:type="arc" sodipodi:cx="404.5" - sodipodi:cy="69.5" + sodipodi:cy="68.5" sodipodi:rx="2" sodipodi:ry="2" sodipodi:start="3.1415927" sodipodi:end="4.712389" sodipodi:open="true" - d="m 402.5,69.5 a 2,2 0 0 1 2,-2" /> + d="m 402.5,68.5 a 2,2 0 0 1 2,-2" + sodipodi:arc-type="arc" /> <rect ry="1.4999999" rx="1.5" @@ -20012,7 +19666,7 @@ sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path8098" - d="m 459.5,66.5 -1,1.5 0,5.5 1,0 z" + d="m 459.5,66.5 -1,1.5 v 5.5 h 1 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect y="70" @@ -20037,7 +19691,7 @@ y="-72" rx="1.5" ry="1.4999999" - transform="scale(-1,-1)" /> + transform="scale(-1)" /> <rect y="-73" x="-455" @@ -20045,12 +19699,12 @@ width="4" id="rect8106" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - transform="scale(-1,-1)" /> + transform="scale(-1)" /> <rect ry="0.5" rx="0.5" y="297" - x="390" + x="406" height="1" width="4" id="rect8108" @@ -20059,30 +19713,30 @@ sodipodi:nodetypes="cccccscsc" inkscape:connector-curvature="0" id="path8120" - d="m 259,42 11,0 0,-7 -5,0 0,1 c 0,2 -1,3 -3,3 l -1,0 c -1,0 -2,1 -2,2 z" + d="m 258,42 h 11 v -7 h -5 v 1 c 0,2 -1,3 -3,3 h -1 c -1,0 -2,1 -2,2 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path8122" - d="m 263.5,38.5 1,1" + d="m 262.5,38.5 1,1" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path8124" - d="m 264,37.5 1.5,0" + d="m 263,37.5 h 1.5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect y="43" - x="259" + x="258" height="1" width="6" id="rect8126" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect y="43" - x="266" + x="265" height="1" width="4" id="rect8128" @@ -20090,17 +19744,9 @@ <path inkscape:connector-curvature="0" id="path7100" - d="m 290,295 c 0.5,0.5 1,0.5 1.5,0.5 1,0 1.5,-0.5 2,-1 l 2,0 2,0 c 0.5,0.5 1,1 2,1 0.5,0 1,0 1.5,-0.5 -0.5,0.5 -1.5,1.5 -2.5,1.5 l -6,0 c -1,0 -2,-1 -2.5,-1.5 z" + d="m 306,295 c 0.5,0.5 1,0.5 1.5,0.5 1,0 1.5,-0.5 2,-1 h 2 2 c 0.5,0.5 1,1 2,1 0.5,0 1,0 1.5,-0.5 -0.5,0.5 -1.5,1.5 -2.5,1.5 h -6 c -1,0 -2,-1 -2.5,-1.5 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" sodipodi:nodetypes="cccccccccc" /> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 93 176.86523 A 1.1342664 1.1342664 0 0 0 91.865234 178 A 1.1342664 1.1342664 0 0 0 91.880859 178.16992 L 89.810547 179.20508 A 1.1342664 1.1342664 0 0 0 89 178.86523 A 1.1342664 1.1342664 0 0 0 87.865234 180 A 1.1342664 1.1342664 0 0 0 89 181.13477 A 1.1342664 1.1342664 0 0 0 89.808594 180.79492 L 91.880859 181.83008 A 1.1342664 1.1342664 0 0 0 91.865234 182 A 1.1342664 1.1342664 0 0 0 93 183.13477 A 1.1342664 1.1342664 0 0 0 94.134766 182 A 1.1342664 1.1342664 0 0 0 93 180.86523 A 1.1342664 1.1342664 0 0 0 92.191406 181.20508 L 90.119141 180.16992 A 1.1342664 1.1342664 0 0 0 90.134766 180 A 1.1342664 1.1342664 0 0 0 90.119141 179.83008 L 92.189453 178.79492 A 1.1342664 1.1342664 0 0 0 93 179.13477 A 1.1342664 1.1342664 0 0 0 94.134766 178 A 1.1342664 1.1342664 0 0 0 93 176.86523 z M 85 182 A 0.50005 0.50005 0 0 0 84.535156 182.31445 L 83.535156 184.81445 A 0.50005 0.50005 0 0 0 83.902344 185.49023 L 85.050781 185.7207 A 0.50005 0.50005 0 0 0 85.083984 185.77734 L 87.083984 188.77734 A 0.50005 0.50005 0 0 0 87.923828 188.76562 L 90.277344 185 L 91 185 A 0.50005 0.50005 0 1 0 91 184 L 90.087891 184 A 0.50005 0.50005 0 0 0 89.974609 183.99414 A 0.50005 0.50005 0 0 0 89.925781 184 L 89 184 A 0.50005 0.50005 0 1 0 89 185 L 89.097656 185 L 88.814453 185.45312 L 84.6875 184.62695 L 85.337891 183 L 86.5 183 A 0.50005 0.50005 0 1 0 86.5 182 L 85 182 z M 86.431641 185.99609 L 88.248047 186.35938 L 87.486328 187.57812 L 86.431641 185.99609 z M 83.5 186 C 82.12521 186 81 187.12521 81 188.5 C 81 189.87479 82.12521 191 83.5 191 C 84.87479 191 86 189.87479 86 188.5 C 86 187.12521 84.87479 186 83.5 186 z M 91.5 186 C 90.12521 186 89 187.12521 89 188.5 C 89 189.87479 90.12521 191 91.5 191 C 92.87479 191 94 189.87479 94 188.5 C 94 187.12521 92.87479 186 91.5 186 z M 83.5 187 C 84.33435 187 85 187.66565 85 188.5 C 85 189.33435 84.33435 190 83.5 190 C 82.66565 190 82 189.33435 82 188.5 C 82 187.66565 82.66565 187 83.5 187 z M 91.5 187 C 92.33435 187 93 187.66565 93 188.5 C 93 189.33435 92.33435 190 91.5 190 C 90.66565 190 90 189.33435 90 188.5 C 90 187.66565 90.66565 187 91.5 187 z " - id="bicycle_share" - inkscape:label="#path7102"> - <title - id="title7131">bicycle with share sign - + d="m 317.24264,234.24264 a 6,6 0 0 1 -8.48528,0 6,6 0 0 1 0,-8.48528 L 313,230 Z" + sodipodi:arc-type="slice" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> + sodipodi:cx="328.5" + sodipodi:arc-type="slice" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + d="m 294,229 a 3,3 0 0 1 3,3" + sodipodi:arc-type="arc" /> + d="m 294,230 a 2,2 0 0 1 2,2" + sodipodi:arc-type="arc" /> + d="m 294,228 a 4,4 0 0 1 4,4" + sodipodi:arc-type="arc" /> + d="m 294.5,239 a 1.5,1.5 0 0 1 1.5,1.5" + sodipodi:arc-type="arc" /> + d="m 294.5,237 a 3.5,3.5 0 0 1 3.5,3.5" + sodipodi:arc-type="arc" /> @@ -20262,7 +19917,8 @@ sodipodi:cx="328.5" sodipodi:type="arc" id="path7253" - style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="arc" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="arc" /> + sodipodi:cx="344.5" + sodipodi:arc-type="slice" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> @@ -20334,7 +19993,7 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7313" - d="m 695.5,137.5 0,-3.5" + d="M 695.5,137.5 V 134" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -20354,7 +20013,7 @@ inkscape:connector-curvature="0" /> @@ -20367,7 +20026,7 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7325" - d="m 745.49643,135.50357 -3.5,0" + d="m 745.49643,135.50357 h -3.5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 184 146 C 180.69814 146 178 148.69814 178 152 C 178 155.30186 180.69814 158 184 158 C 187.30186 158 190 155.30186 190 152 C 190 148.69814 187.30186 146 184 146 z M 184 148 C 184.74369 148 185.42906 148.21376 186.02344 148.5625 L 184 150.58594 L 181.97656 148.5625 C 182.57094 148.21376 183.25631 148 184 148 z M 180.5625 149.97656 L 182.58594 152 L 180.5625 154.02344 C 180.21376 153.42906 180 152.74369 180 152 C 180 151.25631 180.21376 150.57094 180.5625 149.97656 z M 187.4375 149.97656 C 187.78624 150.57094 188 151.25631 188 152 C 188 152.74369 187.78624 153.42906 187.4375 154.02344 L 185.41406 152 L 187.4375 149.97656 z M 184 153.41406 L 186.02344 155.4375 C 185.42906 155.78624 184.74369 156 184 156 C 183.25631 156 182.57094 155.78624 181.97656 155.4375 L 184 153.41406 z " - id="circle7418" /> + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 184,146 c -3.30186,0 -6,2.69814 -6,6 0,3.30186 2.69814,6 6,6 3.30186,0 6,-2.69814 6,-6 0,-3.30186 -2.69814,-6 -6,-6 z m 0,2 c 0.74369,0 1.42906,0.21376 2.02344,0.5625 L 184,150.58594 181.97656,148.5625 C 182.57094,148.21376 183.25631,148 184,148 Z m -3.4375,1.97656 2.02344,2.02344 -2.02344,2.02344 C 180.21376,153.42906 180,152.74369 180,152 c 0,-0.74369 0.21376,-1.42906 0.5625,-2.02344 z m 6.875,0 C 187.78624,150.57094 188,151.25631 188,152 c 0,0.74369 -0.21376,1.42906 -0.5625,2.02344 L 185.41406,152 Z m -3.4375,3.4375 2.02344,2.02344 C 185.42906,155.78624 184.74369,156 184,156 c -0.74369,0 -1.42906,-0.21376 -2.02344,-0.5625 z" + id="japan_police_station" /> <circle r="5" cy="-45.254833" cx="237.58788" id="circle7431" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /> + transform="rotate(45)" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7433" - d="m 200,131.75736 0,8.48528" + d="m 200,134 v 4" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 204.24264,136 -8.48528,0" + d="m 202,136 h -4" id="path7436" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> - <path - id="path7444" - d="m 204.24264,147.75736 c -2.33477,-2.33477 -6.15051,-2.33477 -8.48528,0 -2.33477,2.33477 -2.33477,6.15051 0,8.48528 2.33477,2.33477 6.15051,2.33477 8.48528,0 2.33477,-2.33477 2.33477,-6.15051 0,-8.48528 z m -1.41421,1.41421 c 0.52587,0.52587 0.85934,1.16165 1.03304,1.82854 l -2.86158,0 0,-2.86158 c 0.66689,0.1737 1.30267,0.50717 1.82854,1.03304 z m -3.82832,-1.03304 0,2.86158 -2.86158,0 c 0.1737,-0.66689 0.50717,-1.30267 1.03304,-1.82854 0.52587,-0.52587 1.16165,-0.85934 1.82854,-1.03304 z m 4.86136,4.86136 c -0.1737,0.66689 -0.50717,1.30267 -1.03304,1.82854 -0.52587,0.52587 -1.16165,0.85934 -1.82854,1.03304 l 0,-2.86158 2.86158,0 z m -4.86136,0 0,2.86158 c -0.66689,-0.1737 -1.30267,-0.50717 -1.82854,-1.03304 -0.52587,-0.52587 -0.85934,-1.16165 -1.03304,-1.82854 l 2.86158,0 z" - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - inkscape:connector-curvature="0" /> <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7448" - d="m 220,133 -8,0" + d="m 220,133 h -8" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 216,141 0,-7" + d="m 216,141 v -7" id="path7450" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -20468,77 +20122,43 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7452" - d="m 211,134 0,-2" + d="m 211,134 v -2" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 221,134 0,-2" + d="m 221,134 v -2" id="path7454" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 210,147 0,1 0,2 0,1 2,0 0,-1 3,0 0,7 0,1 2,0 0,-1 0,-7 3,0 0,1 2,0 0,-1 0,-2 0,-1 -2,0 0,1 -8,0 0,-1 z" - id="path7456" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 210,147 v 1 2 1 h 2 v -1 h 3 v 7 1 h 2 v -1 -7 h 3 v 1 h 2 v -1 -2 -1 h -2 v 1 h -8 v -1 z" + id="japan_weather_station" inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccccccccccccccccccccc" /> + sodipodi:nodetypes="ccccccccccccccccccccccc"> + <title + id="title64436">Japanese map symbol for meteorological observatory + + r="1.5" + transform="scale(-1,1)" /> + r="1.5" + transform="scale(-1,1)" /> - - - - - - + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="arc" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 51.5,3.5 0,10 1,0 0,-5 1,0 z" + d="m 51.5,2.5 v 10 h 1 v -5 h 1 z" id="path7602" inkscape:connector-curvature="0" /> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 57.5,3.5 0,5 2,0 -1,5 1,0 0,-10 z" + d="m 57.5,2.5 v 5 h 2 l -1,5 h 1 v -10 z" id="path7604" inkscape:connector-curvature="0" /> <path inkscape:connector-curvature="0" id="path7615" - d="m 67.5,3.5 0,10 0.5,0 c 0.5,-1 0.5,-3 0,-4 l 1.5,0 c -0.5,-2 -1,-4.5 -2,-6 z" + d="m 67.5,2.5 v 10 H 68 c 0.5,-1 0.5,-3 0,-4 h 1.5 c -0.5,-2 -1,-4.5 -2,-6 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" sodipodi:nodetypes="cccccc" /> <path inkscape:connector-curvature="0" id="path7617" - d="m 73.5,3.5 0,5 2,0 c -0.5,1 -1,3 -1,5 l 1,0 0,-10 z" + d="m 73.5,2.5 v 5 h 2 c -0.5,1 -1,3 -1,5 h 1 v -10 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" sodipodi:nodetypes="ccccccc" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 67.49219,19 C 67.21911,19.004 66.99999,19.22689 67,19.5 l 0,10 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 l 0.5,0 c 0.18961,-1.7e-4 0.36282,-0.10757 0.44727,-0.27734 C 68.75689,29.1034 68.875,28.30556 68.875,27.5 c 0,-0.54367 -0.15889,-1.0156 -0.28906,-1.5 L 69.5,26 c 0.32489,-5.2e-4 0.56302,-0.30587 0.48438,-0.62109 C 69.48913,23.39793 69.01558,20.872 67.91602,19.22266 67.82172,19.08124 67.66213,18.99737 67.49219,19 Z M 73,19 l 0,0.5 0,5 0,0.5 0.5,0 0.5,0 0.81641,0 C 74.3738,26.16446 74,27.75121 74,29.5 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 l 1,0 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 L 76,24.48242 76,19.5 c -3e-5,-0.27613 -0.22387,-0.49997 -0.5,-0.5 L 74,19 73.5,19 73,19 Z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 67.49219,18 C 67.21911,18.004 66.99999,18.22689 67,18.5 v 10 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 H 68 c 0.18961,-1.7e-4 0.36282,-0.10757 0.44727,-0.27734 C 68.75689,28.1034 68.875,27.30556 68.875,26.5 c 0,-0.54367 -0.15889,-1.0156 -0.28906,-1.5 H 69.5 c 0.32489,-5.2e-4 0.56302,-0.30587 0.48438,-0.62109 C 69.48913,22.39793 69.01558,19.872 67.91602,18.22266 67.82172,18.08124 67.66213,17.99737 67.49219,18 Z M 73,18 v 0.5 5 0.5 h 0.5 0.5 0.81641 C 74.3738,25.16446 74,26.75121 74,28.5 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 h 1 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 V 23.48242 18.5 c -3e-5,-0.27613 -0.22387,-0.49997 -0.5,-0.5 H 74 73.5 Z" id="knives" inkscape:label="#path7619" inkscape:connector-curvature="0"> @@ -20680,31 +20302,31 @@ width="1" height="6" x="73" - y="3" + y="2" rx="0" ry="0" /> <circle style="fill:none;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="circle8246" - cx="-4" + cx="-3" cy="-85" r="1" transform="matrix(0,-1,-1,0,0,0)" /> <path sodipodi:nodetypes="cccccccccccccc" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 86,14.00001 0,-2 -1,-1 -1,1 0,2 -1,-1 0,-2 1,-1 0,-6 2,0 0,6 1,1 0,2 z" + d="m 86,13.00001 v -2 l -1,-1 -1,1 v 2 l -1,-1 v -2 l 1,-1 v -6 h 2 v 6 l 1,1 v 2 z" id="path8248" inkscape:connector-curvature="0" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 90,14.00001 90,9 90.5,6.5 89.5,6 89,6 89,6.5 88,6.50001 88,4 l 1,0 0,0.5 0.5,0 L 90,4 92,4 c 1,0 1.5,0 2.5,1 l 0,0.50001 C 93.5,4.5 91.5,4.5 91.5,6.5 L 92,9 l 0,5.00001 z" + d="M 90,13.00001 V 8 L 90.5,5.5 89.5,5 H 89 v 0.5 l -1,10e-6 V 3 h 1 v 0.5 h 0.5 L 90,3 h 2 c 1,0 1.5,0 2.5,1 V 4.50001 C 93.5,3.5 91.5,3.5 91.5,5.5 L 92,8 v 5.00001 z" id="path8250" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccccccccccc" /> <path style="fill:#000000;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 85,19 a 1,1 0 0 0 -1,1 l 0,6 -1,1 0,2 1,1 0,-2 1,-1 1,1 0,2 1,-1 0,-2 -1,-1 0,-6 a 1,1 0 0 0 -1,-1 z m 3,1 0,2.5 1,0 0,-0.5 0.5,0 1,0.5 -0.5,2.5 0,5 2,0 0,-5 -0.5,-2.5 c 0,-2 2,-2.00001 3,-1 l 0,-0.5 C 93.5,20 93,20 92,20 l -2,0 -0.5,0.5 -0.5,0 0,-0.5 -1,0 z" + d="m 85,18 a 1,1 0 0 0 -1,1 v 6 l -1,1 v 2 l 1,1 v -2 l 1,-1 1,1 v 2 l 1,-1 v -2 l -1,-1 v -6 a 1,1 0 0 0 -1,-1 z m 3,1 v 2.5 h 1 V 21 h 0.5 l 1,0.5 L 90,24 v 5 h 2 v -5 l -0.5,-2.5 c 0,-2 2,-2.00001 3,-1 V 20 C 93.5,19 93,19 92,19 h -2 l -0.5,0.5 H 89 V 19 Z" id="wretch_and_hammer" inkscape:label="#circle8263" inkscape:connector-curvature="0"> @@ -20715,13 +20337,13 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path8272" cx="98" - cy="9" + cy="8" r="1" /> <circle style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path8274" cx="109" - cy="9" + cy="8" r="1" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" @@ -20729,39 +20351,39 @@ width="1" height="3" x="98" - y="9" /> + y="8" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect8278" width="10" height="2" x="98.5" - y="10" /> + y="9" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect8280" width="1" height="3" x="108" - y="9" /> + y="8" /> <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.10000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect8282" width="11" height="5" x="98" - y="5" + y="4" ry="1.5" rx="1.5" /> <circle r="2" - cy="9" + cy="8" cx="98" id="circle8300" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle r="2" - cy="9" + cy="8" cx="109" id="circle8302" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -20771,29 +20393,29 @@ width="12" height="4" x="97.5" - y="9" /> + y="8" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect8329" width="1" height="6.5" x="103" - y="4.5" /> + y="3.5" /> <path style="opacity:1;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 98,23 c 1.10457,0 2,0.89543 2,2 l 4.5,0 0,-4 -5,0 C 98.669,21 98,21.669 98,22.5 m 11,0 c 0,-0.831 -0.669,-1.5 -1.5,-1.5 l -3.5,0 0,4 3,0 c 0,-1.10457 0.89543,-2 2,-2 m -11,1 c -0.55228,0 -1,0.44772 -1,1 0,0.55228 0.44772,1 1,1 l 0,2 1,0 9.5,0 0.5,0 0,-2 c 0.55228,0 1,-0.44772 1,-1 0,-0.55228 -0.44772,-1 -1,-1 -0.55228,0 -1,0.44772 -1,1 l 0,1 -9,0 0,-1 c 0,-0.55228 -0.44772,-1 -1,-1 z" + d="m 98,22 c 1.10457,0 2,0.89543 2,2 h 4.5 v -4 h -5 C 98.669,20 98,20.669 98,21.5 m 11,0 C 109,20.669 108.331,20 107.5,20 H 104 v 4 h 3 c 0,-1.10457 0.89543,-2 2,-2 m -11,1 c -0.55228,0 -1,0.44772 -1,1 0,0.55228 0.44772,1 1,1 v 2 h 1 9.5 0.5 v -2 c 0.55228,0 1,-0.44772 1,-1 0,-0.55228 -0.44772,-1 -1,-1 -0.55228,0 -1,0.44772 -1,1 v 1 h -9 v -1 c 0,-0.55228 -0.44772,-1 -1,-1 z" id="circle8331" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccsccsccccssccccccsssccss" /> <path style="opacity:1;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 116,18 0,3 8,0 0,-3 -8,0 z m 3.5,1 1,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -3.5,3 0,3 8,0 0,-3 -8,0 z m 3.5,1 1,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -3.5,3 0,3 1,0 0,1 1,0 0,-1 4,0 0,1 1,0 0,-1 1,0 0,-3 -8,0 z m 3.5,1 1,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z" + d="m 116,18 v 3 h 8 v -3 z m 3.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -3.5,3 v 3 h 8 v -3 z m 3.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -3.5,3 v 3 h 1 v 1 h 1 v -1 h 4 v 1 h 1 v -1 h 1 v -3 z m 3.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z" id="rect8357" inkscape:connector-curvature="0" /> <path - style="opacity:1;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 131,18 0,3 10,0 0,-3 -10,0 z m 4.5,1 1,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -4.5,3 0,3 10,0 0,-3 -10,0 z m 4.5,1 1,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -4.5,3 0,3 1,0 0,1 1,0 0,-1 6,0 0,1 1,0 0,-1 1,0 0,-3 -10,0 z m 4.5,1 1,0 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z" - id="rect8373" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="m 131,18 v 3 h 10 v -3 z m 4.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -4.5,3 v 3 h 10 v -3 z m 4.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z m -4.5,3 v 3 h 1 v 1 h 1 v -1 h 6 v 1 h 1 v -1 h 1 v -3 z m 4.5,1 h 1 c 0.277,0 0.5,0.223 0.5,0.5 0,0.277 -0.223,0.5 -0.5,0.5 h -1 c -0.277,0 -0.5,-0.223 -0.5,-0.5 0,-0.277 0.223,-0.5 0.5,-0.5 z" + id="drawer" inkscape:connector-curvature="0" /> <rect y="10" @@ -20921,7 +20543,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 403,98 0,5.95 4,0 0,-5.95 z" + d="m 403,98 v 5.95 h 4 V 98 Z" id="path8434" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /> @@ -20952,7 +20574,7 @@ <path inkscape:connector-curvature="0" id="path8444" - d="m 410,105.5 1,0 -0.5,-2 z" + d="m 410,105.5 h 1 l -0.5,-2 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" @@ -20963,7 +20585,7 @@ y="-300.00824" inkscape:transform-center-x="0.34438145" inkscape:transform-center-y="1.3535204" - transform="matrix(0.5,0.8660254,-0.8660254,0.5,0,0)" /> + transform="rotate(60)" /> <rect transform="matrix(-0.5,0.8660254,0.8660254,0.5,0,0)" inkscape:transform-center-y="1.3535241" @@ -20982,18 +20604,18 @@ r="1" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 403 114 L 401.31055 114.97461 L 402.31055 116.70703 L 403 116.30859 L 403 119.94922 L 407 119.94922 L 407 116.30859 L 407.68945 116.70703 L 408.68945 114.97461 L 407 114 L 406 114 A 1 1 0 0 1 405 115 A 1 1 0 0 1 404 114 L 403 114 z M 410.50195 114.99219 A 0.50005 0.50005 0 0 0 410.21875 115.08008 A 0.50005 0.50005 0 0 0 410.20312 115.0918 A 0.50005 0.50005 0 0 0 410.16797 115.12109 A 0.50005 0.50005 0 0 0 410.16016 115.125 A 0.50005 0.50005 0 0 0 410.13086 115.15625 A 0.50005 0.50005 0 0 0 410.12891 115.1582 A 0.50005 0.50005 0 0 0 410.12305 115.16602 A 0.50005 0.50005 0 0 0 410.10938 115.18164 A 0.50005 0.50005 0 0 0 410.09766 115.19727 A 0.50005 0.50005 0 0 0 410.07227 115.23438 A 0.50005 0.50005 0 0 0 410.07031 115.23828 A 0.50005 0.50005 0 0 0 410.05664 115.26562 A 0.50005 0.50005 0 0 0 410.04688 115.2832 A 0.50005 0.50005 0 0 0 410.03516 115.31055 A 0.50005 0.50005 0 0 0 410.01367 115.37695 A 0.50005 0.50005 0 0 0 410.00781 115.40625 A 0.50005 0.50005 0 0 0 410 115.47656 L 409.18555 122.40039 C 408.85236 122.15218 408.44425 122 408 122 C 406.90135 122 406 122.90135 406 124 C 406 125.09865 406.90135 126 408 126 C 409.06334 126 409.93487 125.15388 409.99023 124.10352 A 0.50005 0.50005 0 0 0 409.99609 124.05859 L 410.23828 122 L 410.76172 122 L 411.00391 124.05859 A 0.50005 0.50005 0 0 0 411.01172 124.11523 C 411.07308 125.16006 411.94068 126 413 126 C 414.09865 126 415 125.09865 415 124 C 415 122.90135 414.09865 122 413 122 C 412.55575 122 412.14764 122.15218 411.81445 122.40039 L 411 115.47461 A 0.50005 0.50005 0 0 0 410.50195 114.99219 z M 408 123 C 408.55474 123 408.9926 123.43695 408.99805 123.99023 A 0.50005 0.50005 0 0 0 408.99805 124.00977 C 408.9926 124.56305 408.55474 125 408 125 C 407.44179 125 407 124.55821 407 124 C 407 123.44179 407.44179 123 408 123 z M 413 123 C 413.55821 123 414 123.44179 414 124 C 414 124.55821 413.55821 125 413 125 C 412.44526 125 412.0074 124.5611 412.00195 124.00781 A 0.50005 0.50005 0 0 0 412 123.99414 C 412.00316 123.43877 412.44379 123 413 123 z " + d="m 403,114 -1.68945,0.97461 1,1.73242 L 403,116.30859 v 3.64063 h 4 v -3.64063 l 0.68945,0.39844 1,-1.73242 L 407,114 h -1 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 z m 7.50195,0.99219 a 0.50005,0.50005 0 0 0 -0.2832,0.0879 0.50005,0.50005 0 0 0 -0.0156,0.0117 0.50005,0.50005 0 0 0 -0.0351,0.0293 0.50005,0.50005 0 0 0 -0.008,0.004 0.50005,0.50005 0 0 0 -0.0293,0.0312 0.50005,0.50005 0 0 0 -0.002,0.002 0.50005,0.50005 0 0 0 -0.006,0.008 0.50005,0.50005 0 0 0 -0.0137,0.0156 0.50005,0.50005 0 0 0 -0.0117,0.0156 0.50005,0.50005 0 0 0 -0.0254,0.0371 0.50005,0.50005 0 0 0 -0.002,0.004 0.50005,0.50005 0 0 0 -0.0137,0.0273 0.50005,0.50005 0 0 0 -0.01,0.0176 0.50005,0.50005 0 0 0 -0.0117,0.0274 0.50005,0.50005 0 0 0 -0.0215,0.0664 0.50005,0.50005 0 0 0 -0.006,0.0293 0.50005,0.50005 0 0 0 -0.008,0.0703 l -0.81445,6.92383 C 408.85236,122.15218 408.44425,122 408,122 c -1.09865,0 -2,0.90135 -2,2 0,1.09865 0.90135,2 2,2 1.06334,0 1.93487,-0.84612 1.99023,-1.89648 a 0.50005,0.50005 0 0 0 0.006,-0.0449 L 410.23828,122 h 0.52344 l 0.24219,2.05859 a 0.50005,0.50005 0 0 0 0.008,0.0566 C 411.07308,125.16006 411.94068,126 413,126 c 1.09865,0 2,-0.90135 2,-2 0,-1.09865 -0.90135,-2 -2,-2 -0.44425,0 -0.85236,0.15218 -1.18555,0.40039 L 411,115.47461 a 0.50005,0.50005 0 0 0 -0.49805,-0.48242 z M 408,123 c 0.55474,0 0.9926,0.43695 0.99805,0.99023 a 0.50005,0.50005 0 0 0 0,0.0195 C 408.9926,124.56305 408.55474,125 408,125 c -0.55821,0 -1,-0.44179 -1,-1 0,-0.55821 0.44179,-1 1,-1 z m 5,0 c 0.55821,0 1,0.44179 1,1 0,0.55821 -0.44179,1 -1,1 -0.55474,0 -0.9926,-0.4389 -0.99805,-0.99219 A 0.50005,0.50005 0 0 0 412,123.99414 C 412.00316,123.43877 412.44379,123 413,123 Z" id="t_shirt_and_scissors" inkscape:label="#path8458"> <title id="title8489">t-shirt and scissors @@ -21001,44 +20623,44 @@ sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path9177" - d="m 138.5,101.5 0,4 -1,0 0,-4 z" + d="m 218.5,581.5 v 4 h -1 v -4 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -21105,10 +20727,11 @@ sodipodi:ry="4" sodipodi:start="3.1415927" sodipodi:end="0" - d="m 227.5,137.5 a 4,4 0 0 1 4,-4 4,4 0 0 1 4,4 l -4,0 z" /> + d="m 227.5,137.5 a 4,4 0 0 1 4,-4 4,4 0 0 1 4,4 h -4 z" + sodipodi:arc-type="slice" /> @@ -21122,8 +20745,9 @@ sodipodi:ry="1" sodipodi:start="3.1415927" sodipodi:end="0" - d="m -131.5,238.5 a 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 l -1,0 z" - transform="matrix(0,-1,1,0,0,0)" /> + d="m -131.5,238.5 a 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 h -1 z" + transform="rotate(-90)" + sodipodi:arc-type="slice" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:arc-type="slice" /> - - shower - + d="m 244.5,137.5 a 3,3 0 0 1 3,-3 3,3 0 0 1 3,3 h -3 z" + sodipodi:arc-type="slice" /> @@ -21267,7 +20883,7 @@ sodipodi:nodetypes="ccccccccccc" inkscape:connector-curvature="0" id="path10054" - d="m 395.5,167 -7,0 0,1 1,1 1,-1 1,1 1,-1 1,1 2,0 0,0 z" + d="m 395.5,167 h -7 v 1 l 1,1 1,-1 1,1 1,-1 1,1 h 2 v 0 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -21295,7 +20911,7 @@ ry="0.5" /> @@ -21303,7 +20919,7 @@ sodipodi:nodetypes="ccccccccc" inkscape:connector-curvature="0" id="path10074" - d="m 409,167.5 0,1 1,1 1,0 0.5,-0.5 0,-2 -0.5,-0.5 -1,0 z" + d="m 409,167.5 v 1 l 1,1 h 1 l 0.5,-0.5 v -2 l -0.5,-0.5 h -1 z" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -21325,15 +20941,15 @@ stratovolcano shield volcano <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 294.5,22 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 4.94922,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 L 294.5,22 Z m 1,2 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 l 1,0 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -1,0 z" + d="m 294.5,22 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 4.94922 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 1,2 c -0.277,0 -0.5,0.223 -0.5,0.5 0,0.277 0.223,0.5 0.5,0.5 h 1 c 0.277,0 0.5,-0.223 0.5,-0.5 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" id="smoke_2" inkscape:label="#rect7547" inkscape:connector-curvature="0"> <title - id="title7560">smoke 2 + id="title61746">clouds + inkscape:label="#path7562" + inkscape:connector-curvature="0" /> @@ -21579,9 +21196,9 @@ id="title7362">small letter P lunokhod @@ -21589,31 +21206,31 @@ @@ -21656,15 +21273,15 @@ rx="1" ry="1" /> probe @@ -21691,29 +21308,29 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path7569" - d="m 694,39 7.5,0" + d="m 694,39 h 7.5" style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 709,34 0,8 1,0 1,1 -1,3 4,0 -1,-3 1,-1 1,0 0,-8 z" + d="m 709,34 v 8 h 1 l 1,1 -1,3 h 4 l -1,-3 1,-1 h 1 v -8 z" id="path7777" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> @@ -21750,7 +21367,7 @@ sodipodi:nodetypes="ccccccccccc" inkscape:connector-curvature="0" id="third_stage" - d="m 709,50 0,8 1,0 1,1 -1,3 4,0 -1,-3 1,-1 1,0 0,-8 z" + d="m 709,50 v 8 h 1 l 1,1 -1,3 h 4 l -1,-3 1,-1 h 1 v -8 z" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" inkscape:label="#path7779"> <title @@ -21804,7 +21421,7 @@ y="38" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 727 52 C 726.446 52 726 52.446 726 53 L 726 55 L 725 55 L 725 57 L 726 57 L 726 59 C 726 59.554 726.446 60 727 60 L 729 60 C 729.554 60 730 59.554 730 59 L 730 57 L 731 57 L 731 55 L 730 55 L 730 53 C 730 52.446 729.554 52 729 52 L 727 52 z M 721 54 L 721 58 L 722 58 L 722 54 L 721 54 z M 723 54 L 723 58 L 724 58 L 724 54 L 723 54 z M 732 54 L 732 58 L 733 58 L 733 54 L 732 54 z M 734 54 L 734 58 L 735 58 L 735 54 L 734 54 z " + d="m 727,52 c -0.554,0 -1,0.446 -1,1 v 2 h -1 v 2 h 1 v 2 c 0,0.554 0.446,1 1,1 h 2 c 0.554,0 1,-0.446 1,-1 v -2 h 1 v -2 h -1 v -2 c 0,-0.554 -0.446,-1 -1,-1 z m -6,2 v 4 h 1 v -4 z m 2,0 v 4 h 1 v -4 z m 9,0 v 4 h 1 v -4 z m 2,0 v 4 h 1 v -4 z" id="orbiter" inkscape:label="#rect7795"> <title @@ -21812,33 +21429,30 @@ </path> <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 306.5,12.5 11,0 c -1.5,-1 -1.5,-2 -2,-3 -2,1.5 -5,1.5 -7,0 -0.5,1 -0.5,2 -2,3 z" + d="m 306.5,9.5 h 11 c -1.5,-1 -1.5,-2 -2,-3 -2,1.5 -5,1.5 -7,0 -0.5,1 -0.5,2 -2,3 z" id="path7810" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 308.47266,25 c -0.17969,0.01014 -0.34006,0.116064 -0.41993,0.277344 -0.55962,1.119257 -0.45808,1.891979 -1.83007,2.80664 C 305.81126,28.358467 306.00544,28.999813 306.5,29 l 11,0 c 0.49456,-1.87e-4 0.68874,-0.641533 0.27734,-0.916016 -1.37199,-0.914661 -1.27045,-1.687383 -1.83007,-2.80664 -0.13896,-0.28007 -0.49797,-0.36537 -0.74805,-0.177735 -1.808,1.356 -4.59044,1.356 -6.39844,0 -0.0942,-0.07084 -0.21043,-0.106117 -0.32812,-0.09961 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 308.47266,22 c -0.17969,0.01014 -0.34006,0.116064 -0.41993,0.277344 -0.55962,1.119257 -0.45808,1.891979 -1.83007,2.80664 C 305.81126,25.358467 306.00544,25.999813 306.5,26 h 11 c 0.49456,-1.87e-4 0.68874,-0.641533 0.27734,-0.916016 -1.37199,-0.914661 -1.27045,-1.687383 -1.83007,-2.80664 -0.13896,-0.28007 -0.49797,-0.36537 -0.74805,-0.177735 -1.808,1.356 -4.59044,1.356 -6.39844,0 -0.0942,-0.07084 -0.21043,-0.106117 -0.32812,-0.09961 z" id="crater" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccc" - inkscape:label="#path7812"> - <title - id="title7816">crater - + inkscape:label="#path7812" /> + d="m 659,102.5 a 4.5,4.5 0 0 1 4.5,-4.5 4.5,4.5 0 0 1 4.5,4.5" + sodipodi:open="true" + sodipodi:arc-type="arc" /> + d="m 676,103.5 a 3.5,3.5 0 0 1 3.5,-3.5 3.5,3.5 0 0 1 3.5,3.5 h -3.5 z" + sodipodi:arc-type="slice" /> @@ -22082,7 +21698,7 @@ style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + d="m 709,104.5 a 2.5,2.5 0 0 1 2.5,-2.5 2.5,2.5 0 0 1 2.5,2.5 h -2.5 z" + sodipodi:arc-type="slice" /> city gate <path style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 247.5,387.5 0,8" + d="m 247.5,387.5 v 8" id="path10945" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 244.5,388.5 -1.5,2.5 3,0 z" + d="M 244.5,388.5 243,391 h 3 z" id="path10991" inkscape:connector-curvature="0" /> <path inkscape:connector-curvature="0" id="path10993" - d="m 250.5,394.5 -1.5,-2.5 3,0 z" + d="M 250.5,394.5 249,392 h 3 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <rect style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" @@ -22357,7 +21974,7 @@ sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 257.5,402 c -0.277,0 -0.5,0.20653 -0.5,0.46289 l 0,0.0742 c 0,0.25636 0.223,0.46289 0.5,0.46289 l 0.5,0 0,9 -0.5,0 c -0.277,0 -0.5,0.20653 -0.5,0.46289 l 0,0.0742 c 0,0.25636 0.223,0.46289 0.5,0.46289 l 12,0 c 0.277,0 0.5,-0.20653 0.5,-0.46289 l 0,-0.0742 C 270,412.20653 269.777,412 269.5,412 l -3.5,0 0,-4.19141 3.72266,-1.86132 a 0.50005,0.50005 0 0 0 -0.22071,-0.95118 0.50005,0.50005 0 0 0 -0.22461,0.0566 l -3.96679,1.98243 c -0.008,0.003 -0.0153,0.008 -0.0234,0.0117 l -0.01,0.006 a 0.50005,0.50005 0 0 0 -0.11523,0.0723 c -0.008,0.007 -0.0157,0.0139 -0.0234,0.0215 A 0.50005,0.50005 0 0 0 265,407.63867 l 0,4.36133 -1,0 0,-9 0.44922,0 c 0.277,0 0.5,-0.20653 0.5,-0.46289 l 0,-0.0742 c 0,-0.25636 -0.223,-0.46289 -0.5,-0.46289 l -6.94922,0 z m 2.5,2 2,0 c 0.554,0 1,0.446 1,1 l 0,2 c 0,0.554 -0.446,1 -1,1 l -2,0 c -0.554,0 -1,-0.446 -1,-1 l 0,-2 c 0,-0.554 0.446,-1 1,-1 z" + d="m 257.5,402 c -0.277,0 -0.5,0.20653 -0.5,0.46289 v 0.0742 c 0,0.25636 0.223,0.46289 0.5,0.46289 h 0.5 v 9 h -0.5 c -0.277,0 -0.5,0.20653 -0.5,0.46289 v 0.0742 c 0,0.25636 0.223,0.46289 0.5,0.46289 h 12 c 0.277,0 0.5,-0.20653 0.5,-0.46289 v -0.0742 C 270,412.20653 269.777,412 269.5,412 H 266 v -4.19141 l 3.72266,-1.86132 a 0.50005,0.50005 0 0 0 -0.22071,-0.95118 0.50005,0.50005 0 0 0 -0.22461,0.0566 l -3.96679,1.98243 c -0.008,0.003 -0.0153,0.008 -0.0234,0.0117 l -0.01,0.006 a 0.50005,0.50005 0 0 0 -0.11523,0.0723 c -0.008,0.007 -0.0157,0.0139 -0.0234,0.0215 A 0.50005,0.50005 0 0 0 265,407.63867 V 412 h -1 v -9 h 0.44922 c 0.277,0 0.5,-0.20653 0.5,-0.46289 v -0.0742 c 0,-0.25636 -0.223,-0.46289 -0.5,-0.46289 H 257.5 Z m 2.5,2 h 2 c 0.554,0 1,0.446 1,1 v 2 c 0,0.554 -0.446,1 -1,1 h -2 c -0.554,0 -1,-0.446 -1,-1 v -2 c 0,-0.554 0.446,-1 1,-1 z" id="toll_booth" inkscape:label="#rect11071" inkscape:connector-curvature="0"> @@ -22368,28 +21985,28 @@ inkscape:connector-curvature="0" id="path11645" d="m 202,166 -4,2 4,2" - style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle r="1.1342664" cy="168" cx="198" id="circle11647" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999997;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle r="1.1342664" cy="166" cx="202" id="circle11649" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999997;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle r="1.1342664" cy="170" cx="202" id="circle11651" - style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09999997;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 202 180.86523 A 1.1342664 1.1342664 0 0 0 200.86523 182 A 1.1342664 1.1342664 0 0 0 200.88086 182.16992 L 198.81055 183.20508 A 1.1342664 1.1342664 0 0 0 198 182.86523 A 1.1342664 1.1342664 0 0 0 196.86523 184 A 1.1342664 1.1342664 0 0 0 198 185.13477 A 1.1342664 1.1342664 0 0 0 198.80859 184.79492 L 200.88086 185.83008 A 1.1342664 1.1342664 0 0 0 200.86523 186 A 1.1342664 1.1342664 0 0 0 202 187.13477 A 1.1342664 1.1342664 0 0 0 203.13477 186 A 1.1342664 1.1342664 0 0 0 202 184.86523 A 1.1342664 1.1342664 0 0 0 201.19141 185.20508 L 199.11914 184.16992 A 1.1342664 1.1342664 0 0 0 199.13477 184 A 1.1342664 1.1342664 0 0 0 199.11914 183.83008 L 201.18945 182.79492 A 1.1342664 1.1342664 0 0 0 202 183.13477 A 1.1342664 1.1342664 0 0 0 203.13477 182 A 1.1342664 1.1342664 0 0 0 202 180.86523 z " + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.7;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 202,180.86523 A 1.1342664,1.1342664 0 0 0 200.86523,182 a 1.1342664,1.1342664 0 0 0 0.0156,0.16992 l -2.07031,1.03516 A 1.1342664,1.1342664 0 0 0 198,182.86523 1.1342664,1.1342664 0 0 0 196.86523,184 1.1342664,1.1342664 0 0 0 198,185.13477 a 1.1342664,1.1342664 0 0 0 0.80859,-0.33985 l 2.07227,1.03516 A 1.1342664,1.1342664 0 0 0 200.86523,186 1.1342664,1.1342664 0 0 0 202,187.13477 1.1342664,1.1342664 0 0 0 203.13477,186 1.1342664,1.1342664 0 0 0 202,184.86523 a 1.1342664,1.1342664 0 0 0 -0.80859,0.33985 l -2.07227,-1.03516 a 1.1342664,1.1342664 0 0 0 0.0156,-0.16992 1.1342664,1.1342664 0 0 0 -0.0156,-0.16992 l 2.07031,-1.03516 A 1.1342664,1.1342664 0 0 0 202,183.13477 1.1342664,1.1342664 0 0 0 203.13477,182 1.1342664,1.1342664 0 0 0 202,180.86523 Z" id="sharing" inkscape:label="#path11653" /> <circle @@ -22402,11 +22019,11 @@ sodipodi:nodetypes="cccccccccccccc" inkscape:connector-curvature="0" id="path11667" - d="m 178,167 2,0 1,1 -1,1 -2,0 1,1 2,0 1,-1 6,0 0,-2 -6,0 -1,-1 -2,0 z" + d="m 178,167 h 2 l 1,1 -1,1 h -2 l 1,1 h 2 l 1,-1 h 6 v -2 h -6 l -1,-1 h -2 z" style="fill:none;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <path style="fill:#000000;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 179 182 L 178 183 L 180 183 L 181 184 L 180 185 L 178 185 L 179 186 L 181 186 L 182 185 L 188 185 A 1 1 0 0 0 189 184 A 1 1 0 0 0 188 183 L 182 183 L 181 182 L 179 182 z " + d="m 179,182 -1,1 h 2 l 1,1 -1,1 h -2 l 1,1 h 2 l 1,-1 h 6 a 1,1 0 0 0 1,-1 1,1 0 0 0 -1,-1 h -6 l -1,-1 z" id="wrench" inkscape:label="#circle11669" /> <path @@ -22420,52 +22037,52 @@ id="path11676" inkscape:connector-curvature="0" /> <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 774.49414 149.99414 A 0.50005 0.50005 0 0 0 774.14648 150.85352 L 775.29297 152 L 774.14648 153.14648 A 0.50005 0.50005 0 1 0 774.85352 153.85352 L 776 152.70703 L 777.14648 153.85352 A 0.50005 0.50005 0 1 0 777.85352 153.14648 L 776.70703 152 L 777.85352 150.85352 A 0.50005 0.50005 0 0 0 777.49023 149.99609 A 0.50005 0.50005 0 0 0 777.14648 150.14648 L 776 151.29297 L 774.85352 150.14648 A 0.50005 0.50005 0 0 0 774.49414 149.99414 z " - id="x_small" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 774.49414,149.99414 a 0.50005,0.50005 0 0 0 -0.34766,0.85938 l 1.14649,1.14648 -1.14649,1.14648 a 0.50005,0.50005 0 1 0 0.70704,0.70704 L 776,152.70703 l 1.14648,1.14649 a 0.50005,0.50005 0 1 0 0.70704,-0.70704 L 776.70703,152 l 1.14649,-1.14648 a 0.50005,0.50005 0 0 0 -0.36329,-0.85743 0.50005,0.50005 0 0 0 -0.34375,0.15039 L 776,151.29297 l -1.14648,-1.14649 a 0.50005,0.50005 0 0 0 -0.35938,-0.15234 z" + id="x_4" inkscape:label="#path11678"> <title - id="title11687">small letter x + id="title11687">small letter x 4 px @@ -22490,7 +22107,7 @@ ry="0.5" rx="0.5" /> - - tooth - + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> stained glass + + dent + + + + + + + inverse donau transmission tower + + + tooth + + + + + + + + + + + + + + + + bump + + + + + + + + + + + + + small letter x 5 px + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hunting stand + + + + + + + + milestone + + + + + + turning loop + + + + + + + + + + + + + + + + bicycle parking wall loops + + + bicycle parking rack + + + bicycle parking stand + + + + + + + + + + + + + + + + + + + + + + + + crane + + + + + + + observatory + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + portal crane + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + books + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/map_machine/main.py b/map_machine/main.py index 0802430..178af72 100644 --- a/map_machine/main.py +++ b/map_machine/main.py @@ -6,7 +6,7 @@ import logging import sys from pathlib import Path -from map_machine.ui import parse_arguments +from map_machine.ui.cli import parse_arguments from map_machine.workspace import Workspace __author__ = "Sergey Vartanov" @@ -29,22 +29,22 @@ def main() -> None: elif arguments.command == "render": from map_machine import mapper - mapper.ui(arguments) + mapper.render_map(arguments) elif arguments.command == "tile": - from map_machine import tile + from map_machine.slippy import tile - tile.ui(arguments) + tile.generate_tiles(arguments) elif arguments.command == "icons": - from map_machine.grid import draw_icons + from map_machine.pictogram.icon_collection import draw_icons draw_icons() elif arguments.command == "mapcss": from map_machine import mapcss - mapcss.ui(arguments) + mapcss.generate_mapcss(arguments) elif arguments.command == "element": from map_machine.element import draw_element @@ -52,15 +52,17 @@ def main() -> None: draw_element(arguments) elif arguments.command == "server": - from map_machine import server + from map_machine.slippy import server - server.ui(arguments) + server.run_server(arguments) elif arguments.command == "taginfo": from map_machine.scheme import Scheme - from map_machine.taginfo import write_taginfo_project_file + from map_machine.doc.taginfo import write_taginfo_project_file - write_taginfo_project_file(Scheme(workspace.DEFAULT_SCHEME_PATH)) + write_taginfo_project_file( + Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) + ) if __name__ == "__main__": diff --git a/map_machine/map_configuration.py b/map_machine/map_configuration.py index ef3e558..4727652 100644 --- a/map_machine/map_configuration.py +++ b/map_machine/map_configuration.py @@ -4,51 +4,49 @@ Map drawing configuration. import argparse from dataclasses import dataclass from enum import Enum +from typing import Optional + +from colour import Color __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" class DrawingMode(Enum): - """ - Map drawing mode. - """ + """Map drawing mode.""" - NORMAL: str = "normal" - AUTHOR: str = "author" - TIME: str = "time" + NORMAL = "normal" + AUTHOR = "author" + TIME = "time" + WHITE = "white" + BLACK = "black" class LabelMode(Enum): - """ - Label drawing mode. - """ + """Label drawing mode.""" - NO: str = "no" - MAIN: str = "main" - ALL: str = "all" + NO = "no" + MAIN = "main" + ALL = "all" + ADDRESS = "address" class BuildingMode(Enum): - """ - Building drawing mode. - """ + """Building drawing mode.""" - NO: str = "no" - FLAT: str = "flat" - ISOMETRIC: str = "isometric" - ISOMETRIC_NO_PARTS: str = "isometric-no-parts" + NO = "no" + FLAT = "flat" + ISOMETRIC = "isometric" + ISOMETRIC_NO_PARTS = "isometric-no-parts" @dataclass class MapConfiguration: - """ - Map drawing configuration. - """ + """Map drawing configuration.""" - drawing_mode: str = DrawingMode.NORMAL - building_mode: str = BuildingMode.FLAT - label_mode: str = LabelMode.MAIN + drawing_mode: DrawingMode = DrawingMode.NORMAL + building_mode: BuildingMode = BuildingMode.FLAT + label_mode: LabelMode = LabelMode.MAIN zoom_level: float = 18.0 overlap: int = 12 level: str = "overground" @@ -57,6 +55,8 @@ class MapConfiguration: country: str = "world" ignore_level_matching: bool = False draw_roofs: bool = True + use_building_colors: bool = False + show_overlapped: bool = False @classmethod def from_options( @@ -75,8 +75,16 @@ class MapConfiguration: options.country, options.ignore_level_matching, options.roofs, + options.building_colors, + options.show_overlapped, ) def is_wireframe(self) -> bool: """Whether drawing mode is special.""" return self.drawing_mode != DrawingMode.NORMAL + + def background_color(self) -> Optional[Color]: + """Get background map color based on drawing mode.""" + if self.drawing_mode not in (DrawingMode.NORMAL, DrawingMode.BLACK): + return Color("#111111") + return None diff --git a/map_machine/mapcss.py b/map_machine/mapcss.py index eeca2bb..34ff70f 100644 --- a/map_machine/mapcss.py +++ b/map_machine/mapcss.py @@ -9,9 +9,9 @@ from typing import Dict, List, Optional, TextIO from colour import Color from map_machine import __project__, __url__ -from map_machine.grid import IconCollection -from map_machine.icon import ShapeExtractor -from map_machine.osm_reader import STAGES_OF_DECAY +from map_machine.osm.osm_reader import STAGES_OF_DECAY +from map_machine.pictogram.icon import ShapeExtractor +from map_machine.pictogram.icon_collection import IconCollection from map_machine.scheme import Matcher, Scheme from map_machine.workspace import workspace @@ -67,9 +67,7 @@ meta {{ class MapCSSWriter: - """ - Writer that converts Map Machine scheme into MapCSS 0.2 format. - """ + """Writer that converts Map Machine scheme into MapCSS 0.2 format.""" def __init__( self, @@ -105,6 +103,10 @@ class MapCSSWriter: """ elements: Dict[str, str] = {} + for value in matcher.tags.values(): + if value.startswith("^"): + return "" + clean_shapes = matcher.get_clean_shapes() if clean_shapes: elements["icon-image"] = ( @@ -134,8 +136,8 @@ class MapCSSWriter: return "" selector: str = target + matcher.get_mapcss_selector(prefix) + " {\n" - for element in elements: - selector += f" {element}: {elements[element]};\n" + for key, value in elements.items(): + selector += f" {key}: {value};\n" selector += "}\n" return selector @@ -164,7 +166,7 @@ class MapCSSWriter: return for index, stage_of_decay in enumerate(STAGES_OF_DECAY): - opacity: float = 0.6 - 0.4 * index / (len(STAGES_OF_DECAY) - 1) + opacity: float = 0.6 - 0.4 * index / (len(STAGES_OF_DECAY) - 1.0) for matcher in self.point_matchers: if len(matcher.tags) > 1: continue @@ -176,18 +178,19 @@ class MapCSSWriter: ) -def ui(options: argparse.Namespace) -> None: +def generate_mapcss(options: argparse.Namespace) -> None: """Write MapCSS 0.2 scheme.""" directory: Path = workspace.get_mapcss_path() icons_with_outline_path: Path = workspace.get_mapcss_icons_path() - scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) + scheme: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) extractor: ShapeExtractor = ShapeExtractor( workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) collection: IconCollection = IconCollection.from_scheme(scheme, extractor) collection.draw_icons( icons_with_outline_path, + workspace.ICONS_LICENSE_PATH, color=Color("black"), outline=True, outline_opacity=0.5, diff --git a/map_machine/mapper.py b/map_machine/mapper.py index a333862..9b8ddf0 100644 --- a/map_machine/mapper.py +++ b/map_machine/mapper.py @@ -3,6 +3,7 @@ Simple OpenStreetMap renderer. """ import argparse import logging +import sys from pathlib import Path from typing import Dict, Iterator, List, Optional, Set @@ -13,18 +14,21 @@ from svgwrite.container import Group from svgwrite.path import Path as SVGPath from svgwrite.shapes import Rect -from map_machine.boundary_box import BoundaryBox from map_machine.constructor import Constructor +from map_machine.drawing import draw_text +from map_machine.feature.building import Building, draw_walls, BUILDING_SCALE +from map_machine.feature.road import Intersection, Road, RoadPart from map_machine.figure import StyledFigure -from map_machine.flinger import Flinger -from map_machine.icon import ShapeExtractor +from map_machine.geometry.boundary_box import BoundaryBox +from map_machine.geometry.flinger import Flinger +from map_machine.geometry.vector import Segment from map_machine.map_configuration import LabelMode, MapConfiguration -from map_machine.osm_getter import NetworkError, get_osm -from map_machine.osm_reader import OSMData, OSMNode -from map_machine.point import Occupied, Point -from map_machine.road import Intersection, Road, RoadPart +from map_machine.osm.osm_getter import NetworkError, get_osm +from map_machine.osm.osm_reader import OSMData, OSMNode +from map_machine.pictogram.icon import ShapeExtractor +from map_machine.pictogram.point import Occupied, Point from map_machine.scheme import Scheme -from map_machine.ui import BuildingMode, progress_bar +from map_machine.ui.cli import BuildingMode from map_machine.workspace import workspace __author__ = "Sergey Vartanov" @@ -32,9 +36,7 @@ __email__ = "me@enzet.ru" class Map: - """ - Map drawing. - """ + """Map drawing.""" def __init__( self, @@ -49,33 +51,32 @@ class Map: self.configuration = configuration self.background_color: Color = self.scheme.get_color("background_color") - if self.configuration.is_wireframe(): - self.background_color: Color = Color("#111111") + if color := self.configuration.background_color(): + self.background_color = color def draw(self, constructor: Constructor) -> None: """Draw map.""" self.svg.add( - Rect((0, 0), self.flinger.size, fill=self.background_color) + Rect((0.0, 0.0), self.flinger.size, fill=self.background_color) ) ways: List[StyledFigure] = sorted( constructor.figures, key=lambda x: x.line_style.priority ) - ways_length: int = len(ways) - for index, way in enumerate(ways): - progress_bar(index, ways_length, step=10, text="Drawing ways") + logging.info("Drawing ways...") + + for way in ways: path_commands: str = way.get_path(self.flinger) if path_commands: path: SVGPath = SVGPath(d=path_commands) path.update(way.line_style.style) self.svg.add(path) - progress_bar(-1, 0, text="Drawing ways") constructor.roads.draw(self.svg, self.flinger) for tree in constructor.trees: tree.draw(self.svg, self.flinger, self.scheme) - for tree in constructor.craters: - tree.draw(self.svg, self.flinger) + for crater in constructor.craters: + crater.draw(self.svg, self.flinger) self.draw_buildings(constructor) @@ -97,22 +98,16 @@ class Map: nodes: List[Point] = sorted( constructor.points, key=lambda x: -x.priority ) - steps: int = len(nodes) - - for index, node in enumerate(nodes): - progress_bar(index, steps * 3, step=10, text="Drawing main icons") + logging.info("Drawing main icons...") + for node in nodes: node.draw_main_shapes(self.svg, occupied) - for index, point in enumerate(nodes): - progress_bar( - steps + index, steps * 3, step=10, text="Drawing extra icons" - ) + logging.info("Drawing extra icons...") + for point in nodes: point.draw_extra_shapes(self.svg, occupied) - for index, point in enumerate(nodes): - progress_bar( - steps * 2 + index, steps * 3, step=10, text="Drawing texts" - ) + logging.info("Drawing texts...") + for point in nodes: if ( not self.configuration.is_wireframe() and self.configuration.label_mode != LabelMode.NO @@ -121,7 +116,7 @@ class Map: self.svg, occupied, self.configuration.label_mode ) - progress_bar(-1, len(nodes), step=10, text="Drawing nodes") + self.draw_credits(constructor.flinger.size) def draw_buildings(self, constructor: Constructor) -> None: """Draw buildings: shade, walls, and roof.""" @@ -132,21 +127,36 @@ class Map: building.draw(self.svg, self.flinger) return - scale: float = self.flinger.get_scale() / 3.0 + logging.info("Drawing buildings...") + + scale: float = self.flinger.get_scale() building_shade: Group = Group(opacity=0.1) for building in constructor.buildings: building.draw_shade(building_shade, self.flinger) self.svg.add(building_shade) - previous_height: float = 0 - count: int = len(constructor.heights) - for index, height in enumerate(sorted(constructor.heights)): - progress_bar(index, count, step=1, text="Drawing buildings") - fill: Color() - for building in constructor.buildings: - if building.height < height or building.min_height > height: + walls: dict[Segment, Building] = {} + + for building in constructor.buildings: + for part in building.parts: + walls[part] = building + + sorted_walls = sorted(walls.keys()) + + previous_height: float = 0.0 + for height in sorted(constructor.heights): + shift_1: np.ndarray = np.array( + (0.0, -previous_height * scale * BUILDING_SCALE) + ) + shift_2: np.ndarray = np.array( + (0.0, -height * scale * BUILDING_SCALE) + ) + for wall in sorted_walls: + building: Building = walls[wall] + if building.height < height or building.min_height >= height: continue - building.draw_walls(self.svg, height, previous_height, scale) + + draw_walls(self.svg, building, wall, height, shift_1, shift_2) if self.configuration.draw_roofs: for building in constructor.buildings: @@ -155,16 +165,14 @@ class Map: previous_height = height - progress_bar(-1, count, step=1, text="Drawing buildings") - - def draw_roads(self, roads: Iterator[Road]) -> None: + def draw_simple_roads(self, roads: Iterator[Road]) -> None: """Draw road as simple SVG path.""" nodes: Dict[OSMNode, Set[RoadPart]] = {} for road in roads: - for index in range(len(road.outers[0]) - 1): - node_1: OSMNode = road.outers[0][index] - node_2: OSMNode = road.outers[0][index + 1] + for index in range(len(road.nodes) - 1): + node_1: OSMNode = road.nodes[index] + node_2: OSMNode = road.nodes[index + 1] point_1: np.ndarray = self.flinger.fling(node_1.coordinates) point_2: np.ndarray = self.flinger.fling(node_2.coordinates) scale: float = self.flinger.get_scale(node_1.coordinates) @@ -179,17 +187,43 @@ class Map: nodes[node_1].add(part_1) nodes[node_2].add(part_2) - for node in nodes: - parts: Set[RoadPart] = nodes[node] + for node, parts in nodes.items(): if len(parts) < 4: continue intersection: Intersection = Intersection(list(parts)) intersection.draw(self.svg, True) + def draw_credits(self, size: np.ndarray): -def ui(arguments: argparse.Namespace) -> None: + for text, point in ( + ("Data: © OpenStreetMap contributors", np.array((15, 27))), + ("Rendering: Map Machine", np.array((15, 15))), + ): + for stroke_width, stroke, opacity in ( + (3.0, Color("white"), 0.7), + (1.0, None, 1.0), + ): + draw_text( + self.svg, + text, + size - point, + 10, + Color("#888888"), + anchor="end", + stroke_width=stroke_width, + stroke=stroke, + opacity=opacity, + ) + + +def fatal(message: str) -> None: + logging.fatal(message) + sys.exit(1) + + +def render_map(arguments: argparse.Namespace) -> None: """ - Map Machine entry point. + Map rendering entry point. :param arguments: command-line arguments """ @@ -199,64 +233,65 @@ def ui(arguments: argparse.Namespace) -> None: cache_path: Path = Path(arguments.cache) cache_path.mkdir(parents=True, exist_ok=True) + # Compute boundary box + boundary_box: Optional[BoundaryBox] = None - input_file_names: List[Path] = [] + + if arguments.boundary_box: + boundary_box = BoundaryBox.from_text(arguments.boundary_box) + elif arguments.coordinates and arguments.size: + coordinates: np.ndarray = np.array( + list(map(float, arguments.coordinates.split(","))) + ) + if len(coordinates) != 2: + fatal("Wrong number of coordinates.") + width, height = np.array(list(map(float, arguments.size.split(",")))) + boundary_box = BoundaryBox.from_coordinates( + coordinates, configuration.zoom_level, width, height + ) + + # Determine files if arguments.input_file_names: input_file_names = list(map(Path, arguments.input_file_names)) - if arguments.boundary_box: - boundary_box = BoundaryBox.from_text(arguments.boundary_box) - else: - if arguments.boundary_box: - boundary_box = BoundaryBox.from_text(arguments.boundary_box) - elif arguments.coordinates and arguments.size: - coordinates: np.ndarray = np.array( - list(map(float, arguments.coordinates.split(","))) - ) - width, height = np.array( - list(map(float, arguments.size.split(","))) - ) - boundary_box = BoundaryBox.from_coordinates( - coordinates, configuration.zoom_level, width, height - ) - else: - logging.fatal( - "Specify either --input, or --boundary-box, or --coordinates " - "and --size." - ) - exit(1) - + elif boundary_box: try: cache_file_path: Path = ( cache_path / f"{boundary_box.get_format()}.osm" ) get_osm(boundary_box, cache_file_path) input_file_names = [cache_file_path] - except NetworkError as e: - logging.fatal(e.message) - exit(1) + except NetworkError as error: + logging.fatal(error.message) + sys.exit(1) + else: + fatal( + "Specify either --input, or --boundary-box, or --coordinates and " + "--size." + ) - scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) - min_: np.ndarray - max_: np.ndarray - osm_data: OSMData + # Get OpenStreetMap data osm_data: OSMData = OSMData() - for input_file_name in input_file_names: if not input_file_name.is_file(): logging.fatal(f"No such file: {input_file_name}.") - exit(1) + sys.exit(1) if input_file_name.name.endswith(".json"): osm_data.parse_overpass(input_file_name) else: osm_data.parse_osm_file(input_file_name) - view_box: BoundaryBox = boundary_box if boundary_box else osm_data.view_box + if not boundary_box: + boundary_box = osm_data.view_box + if not boundary_box: + boundary_box = osm_data.boundary_box + + # Render flinger: Flinger = Flinger( - view_box, arguments.zoom, osm_data.equator_length + boundary_box, arguments.zoom, osm_data.equator_length ) size: np.ndarray = flinger.size @@ -265,6 +300,7 @@ def ui(arguments: argparse.Namespace) -> None: workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) + scheme: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) constructor: Constructor = Constructor( osm_data=osm_data, flinger=flinger, @@ -274,10 +310,10 @@ def ui(arguments: argparse.Namespace) -> None: ) constructor.construct() - painter: Map = Map( + map_: Map = Map( flinger=flinger, svg=svg, scheme=scheme, configuration=configuration ) - painter.draw(constructor) + map_.draw(constructor) logging.info(f"Writing output SVG to {arguments.output_file_name}...") with open(arguments.output_file_name, "w", encoding="utf-8") as output_file: diff --git a/map_machine/osm/__init__.py b/map_machine/osm/__init__.py new file mode 100644 index 0000000..31178c7 --- /dev/null +++ b/map_machine/osm/__init__.py @@ -0,0 +1,3 @@ +""" +OpenStreetMap-specific things. +""" diff --git a/map_machine/osm_getter.py b/map_machine/osm/osm_getter.py similarity index 94% rename from map_machine/osm_getter.py rename to map_machine/osm/osm_getter.py index b5c232f..a02ee94 100644 --- a/map_machine/osm_getter.py +++ b/map_machine/osm/osm_getter.py @@ -9,7 +9,7 @@ from typing import Dict import urllib3 -from map_machine.boundary_box import BoundaryBox +from map_machine.geometry.boundary_box import BoundaryBox __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -52,8 +52,8 @@ def get_osm( "Cannot download data: too many nodes (limit is 50000). Try " "to request smaller area." ) - else: - raise NetworkError("Cannot download data.") + + raise NetworkError("Cannot download data.") with cache_file_path.open("bw+") as output_file: output_file.write(content) diff --git a/map_machine/osm_reader.py b/map_machine/osm/osm_reader.py similarity index 86% rename from map_machine/osm_reader.py rename to map_machine/osm/osm_reader.py index a0436a7..a3266b5 100644 --- a/map_machine/osm_reader.py +++ b/map_machine/osm/osm_reader.py @@ -13,7 +13,7 @@ from xml.etree.ElementTree import Element import numpy as np -from map_machine.boundary_box import BoundaryBox +from map_machine.geometry.boundary_box import BoundaryBox from map_machine.util import MinMax __author__ = "Sergey Vartanov" @@ -25,6 +25,9 @@ METERS_PATTERN: re.Pattern = re.compile("^(?P\\d*\\.?\\d*)\\s*m$") KILOMETERS_PATTERN: re.Pattern = re.compile("^(?P\\d*\\.?\\d*)\\s*km$") MILES_PATTERN: re.Pattern = re.compile("^(?P\\d*\\.?\\d*)\\s*mi$") +EARTH_EQUATOR_LENGTH: float = 40_075_017.0 + +Tags = Dict[str, str] # See https://wiki.openstreetmap.org/wiki/Lifecycle_prefix#Stages_of_decay STAGES_OF_DECAY: List[str] = [ @@ -59,11 +62,9 @@ def parse_levels(string: str) -> List[float]: @dataclass class Tagged: - """ - Something with tags (string to string mapping). - """ + """Something with tags (string to string mapping).""" - tags: Dict[str, str] + tags: Tags def get_tag(self, key: str) -> Optional[str]: """ @@ -106,6 +107,20 @@ class Tagged: return None + def verify(self) -> bool: + """Check key and value types.""" + is_well_formed: bool = True + + for value, key in self.tags.items(): + if not isinstance(key, str): + logging.warning(f"Not string key {key}.") + is_well_formed = False + if not isinstance(value, str): + logging.warning(f"Not string value {value}.") + is_well_formed = False + + return is_well_formed + @dataclass class OSMNode(Tagged): @@ -127,9 +142,9 @@ class OSMNode(Tagged): def from_xml_structure(cls, element: Element) -> "OSMNode": """Parse node from OSM XML `` element.""" attributes = element.attrib - tags: Dict[str, str] = dict( - [(x.attrib["k"], x.attrib["v"]) for x in element if x.tag == "tag"] - ) + tags: Tags = { + x.attrib["k"]: x.attrib["v"] for x in element if x.tag == "tag" + } return cls( tags, int(attributes["id"]), @@ -156,6 +171,14 @@ class OSMNode(Tagged): coordinates=np.array((structure["lat"], structure["lon"])), ) + def get_boundary_box(self) -> BoundaryBox: + return BoundaryBox( + self.coordinates[1], + self.coordinates[0], + self.coordinates[1], + self.coordinates[0], + ) + def __hash__(self) -> int: return self.id_ @@ -182,9 +205,9 @@ class OSMWay(Tagged): ) -> "OSMWay": """Parse way from OSM XML `` element.""" attributes = element.attrib - tags: Dict[str, str] = dict( - [(x.attrib["k"], x.attrib["v"]) for x in element if x.tag == "tag"] - ) + tags: Tags = { + x.attrib["k"]: x.attrib["v"] for x in element if x.tag == "tag" + } return cls( tags, int(element.attrib["id"]), @@ -221,12 +244,13 @@ class OSMWay(Tagged): def __repr__(self) -> str: return f"Way <{self.id_}> {self.nodes}" + def __hash__(self) -> int: + return self.id_ + @dataclass class OSMMember: - """ - Member of OpenStreetMap relation. - """ + """Member of OpenStreetMap relation.""" type_: str ref: int @@ -254,7 +278,7 @@ class OSMRelation(Tagged): """Parse relation from OSM XML `` element.""" attributes = element.attrib members: List[OSMMember] = [] - tags: Dict[str, str] = {} + tags: Tags = {} for subelement in element: if subelement.tag == "member": subattributes = subelement.attrib @@ -298,17 +322,11 @@ class OSMRelation(Tagged): class NotWellFormedOSMDataException(Exception): - """ - OSM data structure is not well-formed. - """ - - pass + """OSM data structure is not well-formed.""" class OSMData: - """ - The whole OpenStreetMap information about nodes, ways, and relations. - """ + """The whole OpenStreetMap information about nodes, ways, and relations.""" def __init__(self) -> None: self.nodes: Dict[int, OSMNode] = {} @@ -319,7 +337,8 @@ class OSMData: self.levels: Set[float] = set() self.time: MinMax = MinMax() self.view_box: Optional[BoundaryBox] = None - self.equator_length: float = 40_075_017.0 + self.boundary_box: Optional[BoundaryBox] = None + self.equator_length: float = EARTH_EQUATOR_LENGTH def add_node(self, node: OSMNode) -> None: """Add node and update map parameters.""" @@ -334,6 +353,10 @@ class OSMData: self.levels.union(parse_levels(node.tags["level"])) self.time.update(node.timestamp) + if not self.boundary_box: + self.boundary_box = node.get_boundary_box() + self.boundary_box.update(node.coordinates) + def add_way(self, way: OSMWay) -> None: """Add way and update map parameters.""" if way.id_ in self.ways: @@ -345,7 +368,8 @@ class OSMData: self.authors.add(way.user) if way.tags.get("level"): self.levels.union(parse_levels(way.tags["level"])) - self.time.update(way.timestamp) + if way.timestamp: + self.time.update(way.timestamp) def add_relation(self, relation: OSMRelation) -> None: """Add relation and update map parameters.""" @@ -372,6 +396,14 @@ class OSMData: node = OSMNode.parse_from_structure(element) node_map[node.id_] = node self.add_node(node) + if not self.view_box: + self.view_box = BoundaryBox( + node.coordinates[1], + node.coordinates[0], + node.coordinates[1], + node.coordinates[0], + ) + self.view_box.update(node.coordinates) for element in structure["elements"]: if element["type"] == "way": diff --git a/map_machine/pictogram/__init__.py b/map_machine/pictogram/__init__.py new file mode 100644 index 0000000..a667ec5 --- /dev/null +++ b/map_machine/pictogram/__init__.py @@ -0,0 +1,3 @@ +""" +Icons and points. +""" diff --git a/map_machine/icon.py b/map_machine/pictogram/icon.py similarity index 64% rename from map_machine/icon.py rename to map_machine/pictogram/icon.py index 168e1a5..4ac776c 100644 --- a/map_machine/icon.py +++ b/map_machine/pictogram/icon.py @@ -23,7 +23,6 @@ from map_machine.color import is_bright __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -DEFAULT_COLOR: Color = Color("#444444") DEFAULT_SHAPE_ID: str = "default" DEFAULT_SMALL_SHAPE_ID: str = "default_small" @@ -38,18 +37,43 @@ GRID_STEP: int = 16 @dataclass class Shape: - """ - SVG icon path description. - """ + """SVG icon path description.""" - path: str # SVG icon path - offset: np.ndarray # vector that should be used to shift the path - id_: str # shape identifier - name: Optional[str] = None # icon description + # String representation of SVG path commands. + path: str + + # Vector that should be used to shift the path. + offset: np.ndarray + + # Shape unique string identifier, e.g. `tree`. + id_: str + + # Shape human-readable description. + name: Optional[str] = None + + # If value is `None`, shape doesn't have distinct direction or its + # direction doesn't make sense. Shape is directed to the right if value is + # `True` and to the left if value is `False`. + # + # E.g. CCTV camera shape has direction and may be flipped horizontally to + # follow surveillance direction, whereas car shape has direction but + # flipping icon doesn't make any sense. is_right_directed: Optional[bool] = None + + # Set of emojis that represent the same entity. E.g. 🍐 (pear) for `pear`; + # 🍏 (green apple) and 🍎 (red apple) for `apple`. emojis: Set[str] = field(default_factory=set) + + # If shape is used only as a part of other icons. is_part: bool = False + # Hierarchical icon group. Is used for icon sorting. + group: str = "" + + # Icon categories that is used in OpenStreetMap wiki. E.g. `barrier` means + # https://wiki.openstreetmap.org/wiki/Category:Barrier_icons. + categories: Set[str] = field(default_factory=set) + @classmethod def from_structure( cls, @@ -70,6 +94,9 @@ class Shape: """ shape: "Shape" = cls(path, offset, id_, name) + if "name" in structure: + shape.name = structure["name"] + if "directed" in structure: if structure["directed"] == "right": shape.is_right_directed = True @@ -83,6 +110,12 @@ class Shape: if "is_part" in structure: shape.is_part = structure["is_part"] + if "group" in structure: + shape.group = structure["group"] + + if "categories" in structure: + shape.categories = set(structure["categories"]) + return shape def is_default(self) -> bool: @@ -95,8 +128,8 @@ class Shape: def get_path( self, point: np.ndarray, - offset: np.ndarray = np.array((0, 0)), - scale: np.ndarray = np.array((1, 1)), + offset: np.ndarray = np.array((0.0, 0.0)), + scale: np.ndarray = np.array((1.0, 1.0)), ) -> SVGPath: """ Draw icon into SVG file. @@ -110,7 +143,7 @@ class Shape: transformations.append(f"translate({shift[0]},{shift[1]})") - if not np.allclose(scale, np.array((1, 1))): + if not np.allclose(scale, np.array((1.0, 1.0))): transformations.append(f"scale({scale[0]},{scale[1]})") transformations.append(f"translate({self.offset[0]},{self.offset[1]})") @@ -119,6 +152,10 @@ class Shape: d=self.path, transform=" ".join(transformations) ) + def get_full_id(self) -> str: + """Compute full shape identifier with group for sorting.""" + return self.group + "_" + self.id_ + def parse_length(text: str) -> float: """Parse length from SVG attribute.""" @@ -139,8 +176,12 @@ def verify_sketch_element(element: Element, id_: str) -> bool: return True style: Dict[str, str] = dict( - [x.split(":") for x in element.attrib["style"].split(";")] + (x.split(":")[0], x.split(":")[1]) + for x in element.attrib["style"].split(";") ) + + # Sketch stroke element (black 0.1 px stroke, no fill). + if ( style["fill"] == "none" and style["stroke"] == "#000000" @@ -149,6 +190,8 @@ def verify_sketch_element(element: Element, id_: str) -> bool: ): return True + # Sketch fill element (black fill, no stroke, 20% opacity). + if ( style["fill"] == "none" and style["stroke"] == "#000000" @@ -157,7 +200,13 @@ def verify_sketch_element(element: Element, id_: str) -> bool: ): return True - if style["fill"] == "#0000ff" and style["stroke"] == "none": + # Experimental shape (blue fill, no stroke). + + if ( + style["fill"] == "#0000ff" + and "stroke" in style + and style["stroke"] == "none" + ): return True if style and not id_.startswith("use"): @@ -166,6 +215,43 @@ def verify_sketch_element(element: Element, id_: str) -> bool: return True +def parse_configuration(root: dict, configuration: dict, group: str) -> None: + """ + Shape description is a probably empty dictionary with optional fields + `name`, `emoji`, `is_part`, `directed`, and `categories`. Shape + configuration is a dictionary that contains shape descriptions. Shape + descriptions may be grouped and the nesting level may be arbitrary: + + { + : {}, + : {}, + : { + : {}, + : {} + }, + : { + : { + : {}, + : {} + } + } + } + """ + for key, value in root.items(): + if ( + not value + or "name" in value + or "emoji" in value + or "is_part" in value + or "directed" in value + or "categories" in value + ): + configuration[key] = value + configuration[key]["group"] = group + else: + parse_configuration(value, configuration, f"{group}_{key}") + + class ShapeExtractor: """ Extract shapes from SVG file. @@ -179,14 +265,26 @@ class ShapeExtractor: """ :param svg_file_name: input SVG file name with icons. File may contain any other irrelevant graphics. + :param configuration_file_name: JSON file with grouped shape + descriptions """ self.shapes: Dict[str, Shape] = {} - self.configuration: Dict[str, Any] = json.load( - configuration_file_name.open(encoding="utf-8") + + self.configuration: Dict[str, Any] = {} + parse_configuration( + json.load(configuration_file_name.open(encoding="utf-8")), + self.configuration, + "root", ) root: Element = ElementTree.parse(svg_file_name).getroot() self.parse(root) + for shape_id in self.configuration: + if shape_id not in self.shapes: + logging.warning( + f"Configuration for unknown shape `{shape_id}`." + ) + def parse(self, node: Element) -> None: """ Extract icon paths into a map. @@ -218,7 +316,7 @@ class ShapeExtractor: def get_offset(value: str) -> float: """Get negated icon offset from the origin.""" return ( - -int(float(value) / GRID_STEP) * GRID_STEP - GRID_STEP / 2 + -int(float(value) / GRID_STEP) * GRID_STEP - GRID_STEP / 2.0 ) point: np.ndarray = np.array( @@ -229,9 +327,15 @@ class ShapeExtractor: name = child_node.text break - configuration: Dict[str, Any] = ( - self.configuration[id_] if id_ in self.configuration else {} - ) + configuration: Dict[str, Any] = {} + + if id_ in self.configuration: + configuration = self.configuration[id_] + if "name" not in configuration: + logging.warning(f"Shape `{id_}` doesn't have name.") + else: + logging.warning(f"Shape `{id_}` doesn't have configuration.") + self.shapes[id_] = Shape.from_structure( configuration, path, point, id_, name ) @@ -252,13 +356,11 @@ class ShapeExtractor: @dataclass class ShapeSpecification: - """ - Specification for shape as a part of an icon. - """ + """Specification for shape as a part of an icon.""" shape: Shape - color: Color = DEFAULT_COLOR - offset: np.ndarray = np.array((0, 0)) + color: Color + offset: np.ndarray = np.array((0.0, 0.0)) flip_horizontally: bool = False flip_vertically: bool = False use_outline: bool = True @@ -274,6 +376,7 @@ class ShapeSpecification: tags: Dict[str, Any] = None, outline: bool = False, outline_opacity: float = 1.0, + scale: float = 1.0, ) -> None: """ Draw icon shape into SVG file. @@ -284,15 +387,18 @@ class ShapeSpecification: displayed, this argument should be None :param outline: draw outline for the shape :param outline_opacity: opacity of the outline + :param scale: scale icon by the magnitude """ - scale: np.ndarray = np.array((1, 1)) + scale_vector: np.ndarray = np.array((scale, scale)) if self.flip_vertically: - scale = np.array((1, -1)) + scale_vector = np.array((scale, -scale)) if self.flip_horizontally: - scale = np.array((-1, 1)) + scale_vector = np.array((-scale, scale)) point: np.ndarray = np.array(list(map(int, point))) - path: SVGPath = self.shape.get_path(point, self.offset, scale) + path: SVGPath = self.shape.get_path( + point, self.offset * scale, scale_vector + ) path.update({"fill": self.color.hex}) if outline and self.use_outline: @@ -326,9 +432,7 @@ class ShapeSpecification: @dataclass class Icon: - """ - Icon that consists of (probably) multiple shapes. - """ + """Icon that consists of (probably) multiple shapes.""" shape_specifications: List[ShapeSpecification] opacity: float = 1.0 @@ -337,6 +441,14 @@ class Icon: """Get all shape identifiers in the icon.""" return [x.shape.id_ for x in self.shape_specifications] + def has_names(self) -> bool: + """Check whether oll shape names are known.""" + for specification in self.shape_specifications: + if not specification.shape.name: + return False + + return True + def get_names(self) -> List[str]: """Get all shape names in the icon.""" return [ @@ -344,12 +456,39 @@ class Icon: for x in self.shape_specifications ] + def get_name(self) -> str: + """Get combined human-readable icon name.""" + names: List[str] = self.get_names() + + if len(names) == 1: + return names[0] + + return ", ".join(names[:-1]) + " and " + names[-1] + + def has_categories(self) -> bool: + """Check whether oll shape categories are known.""" + for specification in self.shape_specifications: + if specification.shape.categories: + return True + + return False + + def get_categories(self) -> Set[str]: + """Get all shape names in the icon.""" + result: Set[str] = set() + + for specification in self.shape_specifications: + result = result.union(specification.shape.categories) + + return result + def draw( self, svg: svgwrite.Drawing, point: np.ndarray, tags: Dict[str, Any] = None, outline: bool = False, + scale: float = 1.0, ) -> None: """ Draw icon to SVG. @@ -358,18 +497,21 @@ class Icon: :param point: 2D position of the icon centre :param tags: tags to be displayed as a tooltip :param outline: draw outline for the icon + :param scale: scale icon by the magnitude """ if outline: bright: bool = is_bright(self.shape_specifications[0].color) opacity: float = 0.7 if bright else 0.5 outline_group: Group = Group(opacity=opacity) for shape_specification in self.shape_specifications: - shape_specification.draw(outline_group, point, tags, True) + shape_specification.draw( + outline_group, point, tags, True, scale=scale + ) svg.add(outline_group) else: group: Group = Group(opacity=self.opacity) for shape_specification in self.shape_specifications: - shape_specification.draw(group, point, tags) + shape_specification.draw(group, point, tags, scale=scale) svg.add(group) def draw_to_file( @@ -394,7 +536,7 @@ class Icon: shape_specification.color = color shape_specification.draw( svg, - np.array((8, 8)), + np.array((8.0, 8.0)), outline=outline, outline_opacity=outline_opacity, ) @@ -402,14 +544,17 @@ class Icon: for shape_specification in self.shape_specifications: if color: shape_specification.color = color - shape_specification.draw(svg, np.array((8, 8))) + shape_specification.draw(svg, np.array((8.0, 8.0))) with file_name.open("w", encoding="utf-8") as output_file: svg.write(output_file) def is_default(self) -> bool: """Check whether first shape is default.""" - return self.shape_specifications[0].is_default() + return ( + len(self.shape_specifications) == 1 + and self.shape_specifications[0].is_default() + ) def recolor(self, color: Color, white: Optional[Color] = None) -> None: """Paint all shapes in the color.""" @@ -432,19 +577,21 @@ class Icon: def __lt__(self, other: "Icon") -> bool: return "".join( - [x.shape.id_ for x in self.shape_specifications] - ) < "".join([x.shape.id_ for x in other.shape_specifications]) + [x.shape.get_full_id() for x in self.shape_specifications] + ) < "".join([x.shape.get_full_id() for x in other.shape_specifications]) @dataclass class IconSet: - """ - Node representation: icons and color. - """ + """Node representation: icons and color.""" main_icon: Icon extra_icons: List[Icon] + # Icon to use if the point is hidden by overlapped icons but still need to + # be shown. + default_icon: Optional[Icon] + # Tag keys that were processed to create icon set (other tag keys should be # displayed by text or ignored) processed: Set[str] diff --git a/map_machine/grid.py b/map_machine/pictogram/icon_collection.py similarity index 62% rename from map_machine/grid.py rename to map_machine/pictogram/icon_collection.py index 3ae0f85..0bc964a 100644 --- a/map_machine/grid.py +++ b/map_machine/pictogram/icon_collection.py @@ -2,6 +2,7 @@ Icon grid drawing. """ import logging +import shutil from dataclasses import dataclass from pathlib import Path from typing import Dict, List, Optional, Set, Union @@ -9,9 +10,13 @@ from typing import Dict, List, Optional, Set, Union import numpy as np from colour import Color from svgwrite import Drawing -from svgwrite.shapes import Rect -from map_machine.icon import Icon, Shape, ShapeExtractor, ShapeSpecification +from map_machine.pictogram.icon import ( + Icon, + Shape, + ShapeExtractor, + ShapeSpecification, +) from map_machine.scheme import NodeMatcher, Scheme from map_machine.workspace import workspace @@ -21,9 +26,7 @@ __email__ = "me@enzet.ru" @dataclass class IconCollection: - """ - Collection of icons. - """ + """Collection of icons.""" icons: List[Icon] @@ -38,7 +41,10 @@ class IconCollection: add_all: bool = False, ) -> "IconCollection": """ - Collect all possible icon combinations in grid. + Collect all possible icon combinations. + + This collection won't contain icons for tags matched with regular + expressions. E.g. traffic_sign=maxspeed; maxspeed=42. :param scheme: tag specification :param extractor: shape extractor for icon creation @@ -50,57 +56,51 @@ class IconCollection: """ icons: List[Icon] = [] - def add() -> Icon: + def add(current_set: List[Dict[str, str]]) -> None: """Construct icon and add it to the list.""" - specifications: List[ShapeSpecification] = [ - scheme.get_shape_specification(x, extractor) - for x in current_set - ] + specifications: List[ShapeSpecification] = [] + for shape_specification in current_set: + if "#" in shape_specification["shape"]: + return + specifications.append( + scheme.get_shape_specification( + shape_specification, extractor + ) + ) constructed_icon: Icon = Icon(specifications) constructed_icon.recolor(color, white=background_color) if constructed_icon not in icons: icons.append(constructed_icon) - return constructed_icon - - current_set: List[Union[str, Dict[str, str]]] - for matcher in scheme.node_matchers: matcher: NodeMatcher if matcher.shapes: - current_set = matcher.shapes - add() + add(matcher.shapes) if matcher.add_shapes: - current_set = matcher.add_shapes - add() + add(matcher.add_shapes) if not matcher.over_icon: continue if matcher.under_icon: for icon_id in matcher.under_icon: - current_set = [icon_id] + matcher.over_icon - add() + add([icon_id] + matcher.over_icon) if not (matcher.under_icon and matcher.with_icon): continue for icon_id in matcher.under_icon: for icon_2_id in matcher.with_icon: - current_set: List[str] = ( - [icon_id] + [icon_2_id] + matcher.over_icon - ) - add() + add([icon_id] + [icon_2_id] + matcher.over_icon) for icon_2_id in matcher.with_icon: for icon_3_id in matcher.with_icon: - current_set = ( - [icon_id] - + [icon_2_id] - + [icon_3_id] - + matcher.over_icon - ) if ( icon_2_id != icon_3_id and icon_2_id != icon_id and icon_3_id != icon_id ): - add() + add( + [icon_id] + + [icon_2_id] + + [icon_3_id] + + matcher.over_icon + ) specified_ids: Set[str] = set() @@ -112,15 +112,15 @@ class IconCollection: shape: Shape = extractor.get_shape(shape_id) if shape.is_part: continue - icon: Icon = Icon([ShapeSpecification(shape)]) - icon.recolor(color) + icon: Icon = Icon([ShapeSpecification(shape, color)]) + icon.recolor(color, white=background_color) icons.append(icon) if add_all: for shape_id in extractor.shapes.keys(): shape: Shape = extractor.get_shape(shape_id) - icon: Icon = Icon([ShapeSpecification(shape)]) - icon.recolor(color) + icon: Icon = Icon([ShapeSpecification(shape, color)]) + icon.recolor(color, white=background_color) icons.append(icon) return cls(icons) @@ -128,6 +128,7 @@ class IconCollection: def draw_icons( self, output_directory: Path, + license_path: Path, by_name: bool = False, color: Optional[Color] = None, outline: bool = False, @@ -136,6 +137,7 @@ class IconCollection: """ :param output_directory: path to the directory to store individual SVG files for icons + :param license_path: path to the file with license :param by_name: use names instead of identifiers :param color: fill color :param outline: if true, draw outline beneath the icon @@ -145,7 +147,7 @@ class IconCollection: def get_file_name(x: Icon) -> str: """Generate human-readable file name.""" - return f"Röntgen {' + '.join(x.get_names())}.svg" + return f"Röntgen {x.get_name()}.svg" else: @@ -161,12 +163,15 @@ class IconCollection: outline_opacity=outline_opacity, ) + shutil.copy(license_path, output_directory / "LICENSE") + def draw_grid( self, file_name: Path, columns: int = 16, - step: float = 24, - background_color: Color = Color("white"), + step: float = 24.0, + background_color: Optional[Color] = Color("white"), + scale: float = 1.0, ) -> None: """ Draw icons in the form of table. @@ -175,26 +180,25 @@ class IconCollection: :param columns: number of columns in grid :param step: horizontal and vertical distance between icons in grid :param background_color: background color + :param scale: scale icon by the magnitude """ - point: np.ndarray = np.array((step / 2, step / 2)) - width: float = step * columns + point: np.ndarray = np.array((step / 2.0 * scale, step / 2.0 * scale)) + width: float = step * columns * scale - height: int = int(int(len(self.icons) / (width / step) + 1) * step) + height: int = int(int(len(self.icons) / columns + 1.0) * step * scale) svg: Drawing = Drawing(str(file_name), (width, height)) - svg.add(svg.rect((0, 0), (width, height), fill=background_color.hex)) + if background_color is not None: + svg.add( + svg.rect((0, 0), (width, height), fill=background_color.hex) + ) for icon in self.icons: - icon: Icon - rectangle: Rect = svg.rect( - point - np.array((10, 10)), (20, 20), fill=background_color.hex - ) - svg.add(rectangle) - icon.draw(svg, point) - point += np.array((step, 0)) - if point[0] > width - 8: - point[0] = step / 2 - point += np.array((0, step)) - height += step + icon.draw(svg, point, scale=scale) + point += np.array((step * scale, 0.0)) + if point[0] > width - 8.0: + point[0] = step / 2.0 * scale + point += np.array((0.0, step * scale)) + height += step * scale with file_name.open("w", encoding="utf-8") as output_file: svg.write(output_file) @@ -212,21 +216,35 @@ def draw_icons() -> None: Draw all possible icon shapes combinations as grid in one SVG file and as individual SVG files. """ - scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) + scheme: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) extractor: ShapeExtractor = ShapeExtractor( workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) - collection: IconCollection = IconCollection.from_scheme( - scheme, extractor, add_all=True - ) - icon_grid_path: Path = workspace.get_icon_grid_path() - collection.draw_grid(icon_grid_path) - logging.info(f"Icon grid is written to {icon_grid_path}.") + collection: IconCollection = IconCollection.from_scheme(scheme, extractor) + collection.sort() + + # Draw individual icons. icons_by_id_path: Path = workspace.get_icons_by_id_path() + collection.draw_icons(icons_by_id_path, workspace.ICONS_LICENSE_PATH) + icons_by_name_path: Path = workspace.get_icons_by_name_path() - collection.draw_icons(icons_by_id_path) - collection.draw_icons(icons_by_name_path, by_name=True) + collection.draw_icons( + icons_by_name_path, workspace.ICONS_LICENSE_PATH, by_name=True + ) + logging.info( f"Icons are written to {icons_by_name_path} and {icons_by_id_path}." ) + + # Draw grid. + + for icon in collection.icons: + icon.recolor(Color("#444444")) + + for path, scale in ( + (workspace.get_icon_grid_path(), 1.0), + (workspace.GRID_PATH, 2.0), + ): + collection.draw_grid(path, scale=scale) + logging.info(f"Icon grid is written to {path}.") diff --git a/map_machine/point.py b/map_machine/pictogram/point.py similarity index 68% rename from map_machine/point.py rename to map_machine/pictogram/point.py index 599fa06..10c98dd 100644 --- a/map_machine/point.py +++ b/map_machine/pictogram/point.py @@ -1,22 +1,22 @@ """ Point: node representation on the map. """ +import logging from typing import Dict, List, Optional, Set import numpy as np import svgwrite from colour import Color -from map_machine.icon import Icon, IconSet +from map_machine.drawing import draw_text from map_machine.map_configuration import LabelMode -from map_machine.osm_reader import Tagged +from map_machine.osm.osm_reader import Tagged +from map_machine.pictogram.icon import Icon, IconSet from map_machine.text import Label __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -DEFAULT_FONT: str = "Roboto" - class Occupied: """ @@ -26,19 +26,28 @@ class Occupied: def __init__(self, width: int, height: int, overlap: int) -> None: self.matrix = np.full((int(width), int(height)), False, dtype=bool) + try: + self.matrix = np.full((int(width), int(height)), False, dtype=bool) + except Exception: + logging.fatal( + "Failed to allocate a matrix required by overlap algorithm. " + "Try to use smallest area or try --overlap=0 options." + ) + exit(1) + self.width: float = width self.height: float = height self.overlap: int = overlap def check(self, point: np.ndarray) -> bool: """Check whether point is already occupied by other elements.""" - if 0 <= point[0] < self.width and 0 <= point[1] < self.height: + if 0.0 <= point[0] < self.width and 0.0 <= point[1] < self.height: return self.matrix[point[0], point[1]] return True def register(self, point: np.ndarray) -> None: """Register that point is occupied by an element.""" - if 0 <= point[0] < self.width and 0 <= point[1] < self.height: + if 0.0 <= point[0] < self.width and 0.0 <= point[1] < self.height: self.matrix[point[0], point[1]] = True assert self.matrix[point[0], point[1]] @@ -57,7 +66,7 @@ class Point(Tagged): tags: Dict[str, str], processed: Set[str], point: np.ndarray, - priority: float = 0, + priority: float = 0.0, is_for_node: bool = True, draw_outline: bool = True, add_tooltips: bool = False, @@ -71,12 +80,12 @@ class Point(Tagged): self.processed: Set[str] = processed self.point: np.ndarray = point self.priority: float = priority - self.layer: float = 0 + self.layer: float = 0.0 self.is_for_node: bool = is_for_node self.draw_outline: bool = draw_outline self.add_tooltips: bool = add_tooltips - self.y = 0 + self.y: float = 0.0 self.main_icon_painted: bool = False def draw_main_shapes( @@ -91,15 +100,20 @@ class Point(Tagged): ): return - position: np.ndarray = self.point + np.array((0, self.y)) + position: np.ndarray = self.point + np.array((0.0, self.y)) tags: Optional[Dict[str, str]] = ( self.tags if self.add_tooltips else None ) self.main_icon_painted: bool = self.draw_point_shape( - svg, self.icon_set.main_icon, position, occupied, tags=tags + svg, + self.icon_set.main_icon, + self.icon_set.default_icon, + position, + occupied, + tags=tags, ) if self.main_icon_painted: - self.y += 16 + self.y += 16.0 def draw_extra_shapes( self, svg: svgwrite.Drawing, occupied: Optional[Occupied] = None @@ -110,7 +124,7 @@ class Point(Tagged): is_place_for_extra: bool = True if occupied: - left: float = -(len(self.icon_set.extra_icons) - 1) * 8 + left: float = -(len(self.icon_set.extra_icons) - 1.0) * 8.0 for _ in self.icon_set.extra_icons: point: np.ndarray = np.array( (int(self.point[0] + left), int(self.point[1] + self.y)) @@ -118,38 +132,46 @@ class Point(Tagged): if occupied.check(point): is_place_for_extra = False break - left += 16 + left += 16.0 if is_place_for_extra: - left: float = -(len(self.icon_set.extra_icons) - 1) * 8 + left: float = -(len(self.icon_set.extra_icons) - 1.0) * 8.0 for icon in self.icon_set.extra_icons: point: np.ndarray = self.point + np.array((left, self.y)) - self.draw_point_shape(svg, icon, point, occupied=occupied) - left += 16 + self.draw_point_shape(svg, icon, None, point, occupied=occupied) + left += 16.0 if self.icon_set.extra_icons: - self.y += 16 + self.y += 16.0 def draw_point_shape( self, svg: svgwrite.Drawing, icon: Icon, + default_icon: Optional[Icon], position: np.ndarray, - occupied: Occupied, + occupied: Optional[Occupied], tags: Optional[Dict[str, str]] = None, ) -> bool: """Draw one combined icon and its outline.""" # Down-cast floats to integers to make icons pixel-perfect. position: np.ndarray = np.array((int(position[0]), int(position[1]))) + icon_to_draw: Icon = icon + is_painted: bool = True + if occupied and occupied.check(position): - return False + if default_icon: + icon_to_draw = default_icon + is_painted = False + else: + return False if self.draw_outline: - icon.draw(svg, position, outline=True) + icon_to_draw.draw(svg, position, outline=True) - icon.draw(svg, position, tags=tags) + icon_to_draw.draw(svg, position, tags=tags) - if occupied: + if occupied and is_painted: overlap: int = occupied.overlap for i in range(-overlap, overlap): for j in range(-overlap, overlap): @@ -157,13 +179,13 @@ class Point(Tagged): np.array((position[0] + i, position[1] + j)) ) - return True + return is_painted def draw_texts( self, svg: svgwrite.Drawing, occupied: Optional[Occupied] = None, - label_mode: str = LabelMode.MAIN, + label_mode: LabelMode = LabelMode.MAIN, ) -> None: """Draw all labels.""" labels: List[Label] @@ -180,9 +202,15 @@ class Point(Tagged): text = text.replace(""", '"') text = text.replace("&", "&") text = text[:26] + ("..." if len(text) > 26 else "") - point = self.point + np.array((0, self.y + 2)) + point = self.point + np.array((0.0, self.y + 2.0)) self.draw_text( - svg, text, point, occupied, label.fill, size=label.size + svg, + text, + point, + occupied, + label.fill, + label.size, + label.out_fill, ) def draw_text( @@ -192,8 +220,8 @@ class Point(Tagged): point: np.ndarray, occupied: Optional[Occupied], fill: Color, - size: float = 10.0, - out_fill: Color = Color("white"), + size: float, + out_fill: Color, out_opacity: float = 0.5, out_fill_2: Optional[Color] = None, out_opacity_2: float = 1.0, @@ -212,9 +240,9 @@ class Point(Tagged): if occupied: is_occupied: bool = False - for i in range(-int(length / 2), int(length / 2)): + for i in range(-int(length / 2.0), int(length / 2.0)): text_position: np.ndarray = np.array( - (int(point[0] + i), int(point[1] - 4)) + (int(point[0] + i), int(point[1] - 4.0)) ) if occupied.check(text_position): is_occupied = True @@ -223,7 +251,7 @@ class Point(Tagged): if is_occupied: return - for i in range(-int(length / 2), int(length / 2)): + for i in range(-int(length / 2.0), int(length / 2.0)): for j in range(-12, 5): occupied.register( np.array((int(point[0] + i), int(point[1] + j))) @@ -232,26 +260,28 @@ class Point(Tagged): svg.add(svg.rect((point[0] + i, point[1] + j), (1, 1))) if out_fill_2: - text_element = svg.text( - text, point, font_size=size, text_anchor="middle", - font_family=DEFAULT_FONT, fill=out_fill_2.hex, - stroke_linejoin="round", stroke_width=5, stroke=out_fill_2.hex, - opacity=out_opacity_2 - ) # fmt: skip - svg.add(text_element) + draw_text( + svg, + text, + point, + size, + fill=out_fill_2, + stroke_width=5.0, + stroke=out_fill_2, + opacity=out_opacity_2, + ) if out_fill: - text_element = svg.text( - text, point, font_size=size, text_anchor="middle", - font_family=DEFAULT_FONT, fill=out_fill.hex, - stroke_linejoin="round", stroke_width=3, stroke=out_fill.hex, + draw_text( + svg, + text, + point, + size, + fill, + stroke_width=3.0, + stroke=out_fill, opacity=out_opacity, - ) # fmt: skip - svg.add(text_element) - text_element = svg.text( - text, point, font_size=size, text_anchor="middle", - font_family=DEFAULT_FONT, fill=fill.hex, - ) # fmt: skip - svg.add(text_element) + ) + draw_text(svg, text, point, size, fill) self.y += 11 @@ -264,7 +294,9 @@ class Point(Tagged): width: int = icon_size * ( 1 + max(2, len(self.icon_set.extra_icons) - 1) ) - height: int = icon_size * (1 + int(len(self.icon_set.extra_icons) / 3)) + height: int = icon_size * ( + 1 + np.ceil(len(self.icon_set.extra_icons) / 3.0) + ) if len(self.labels): height += 4 + 11 * len(self.labels) return np.array((width, height)) diff --git a/map_machine/scheme.py b/map_machine/scheme.py index 6110e03..b827738 100644 --- a/map_machine/scheme.py +++ b/map_machine/scheme.py @@ -2,6 +2,7 @@ Map Machine drawing scheme. """ import logging +import re from dataclasses import dataclass from enum import Enum from pathlib import Path @@ -11,18 +12,18 @@ import numpy as np import yaml from colour import Color -from map_machine.direction import DirectionSet -from map_machine.icon import ( - DEFAULT_COLOR, +from map_machine.feature.direction import DirectionSet +from map_machine.map_configuration import MapConfiguration +from map_machine.osm.osm_reader import Tagged, Tags +from map_machine.pictogram.icon import ( DEFAULT_SHAPE_ID, Icon, IconSet, Shape, ShapeExtractor, ShapeSpecification, + DEFAULT_SMALL_SHAPE_ID, ) -from map_machine.map_configuration import MapConfiguration -from map_machine.text import Label, get_address, get_text __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -32,30 +33,28 @@ IconDescription = List[Union[str, Dict[str, str]]] @dataclass class LineStyle: - """ - SVG line style and its priority. - """ + """SVG line style and its priority.""" style: Dict[str, Union[int, float, str]] + parallel_offset: float = 0.0 priority: float = 0.0 class MatchingType(Enum): - """ - Description on how tag was matched. - """ + """Description on how tag was matched.""" NOT_MATCHED = 0 MATCHED_BY_SET = 1 MATCHED_BY_WILDCARD = 2 MATCHED = 3 + MATCHED_BY_REGEX = 4 def is_matched_tag( matcher_tag_key: str, matcher_tag_value: Union[str, list], - tags: Dict[str, str], -) -> MatchingType: + tags: Tags, +) -> Tuple[MatchingType, List[str]]: """ Check whether element tags contradict tag matcher. @@ -63,20 +62,21 @@ def is_matched_tag( :param matcher_tag_value: tag value, tag value list, or "*" :param tags: element tags to check """ - if matcher_tag_key in tags: - if matcher_tag_value == "*": - return MatchingType.MATCHED_BY_WILDCARD - if ( - isinstance(matcher_tag_value, str) - and tags[matcher_tag_key] == matcher_tag_value - ): - return MatchingType.MATCHED - if ( - isinstance(matcher_tag_value, list) - and tags[matcher_tag_key] in matcher_tag_value - ): - return MatchingType.MATCHED_BY_SET - return MatchingType.NOT_MATCHED + if matcher_tag_key not in tags: + return MatchingType.NOT_MATCHED, [] + + if matcher_tag_value == "*": + return MatchingType.MATCHED_BY_WILDCARD, [] + if tags[matcher_tag_key] == matcher_tag_value: + return MatchingType.MATCHED, [] + if matcher_tag_value.startswith("^"): + matcher: Optional[re.Match] = re.match( + matcher_tag_value, tags[matcher_tag_key] + ) + if matcher: + return MatchingType.MATCHED_BY_REGEX, list(matcher.groups()) + + return MatchingType.NOT_MATCHED, [] def get_selector(key: str, value: str, prefix: str = "") -> str: @@ -103,15 +103,13 @@ def match_location(restrictions: Dict[str, str], country: str) -> bool: return True -class Matcher: - """ - Tag matching. - """ +class Matcher(Tagged): + """Tag matching.""" def __init__( self, structure: Dict[str, Any], group: Optional[Dict[str, Any]] = None ) -> None: - self.tags: Dict[str, str] = structure["tags"] + super().__init__(structure["tags"]) self.exception: Dict[str, str] = {} if "exception" in structure: @@ -129,6 +127,8 @@ class Matcher: if "location_restrictions" in structure: self.location_restrictions = structure["location_restrictions"] + self.verify() + def check_zoom_level(self, zoom_level: float) -> bool: """Check whether zoom level is matching.""" return ( @@ -136,16 +136,16 @@ class Matcher: ) def is_matched( - self, - tags: Dict[str, str], - configuration: Optional[MapConfiguration] = None, - ) -> bool: + self, tags: Tags, configuration: Optional[MapConfiguration] = None + ) -> Tuple[bool, Dict[str, str]]: """ Check whether element tags matches tag matcher. :param tags: element tags to be matched :param configuration: current map configuration to be matched """ + groups: Dict[str, str] = {} + if ( configuration is not None and self.location_restrictions @@ -153,28 +153,30 @@ class Matcher: self.location_restrictions, configuration.country ) ): - return False + return False, {} for config_tag_key in self.tags: config_tag_key: str - tag_matcher = self.tags[config_tag_key] - if ( - is_matched_tag(config_tag_key, tag_matcher, tags) - == MatchingType.NOT_MATCHED - ): - return False + is_matched, matched_groups = is_matched_tag( + config_tag_key, self.tags[config_tag_key], tags + ) + if is_matched == MatchingType.NOT_MATCHED: + return False, {} + + if matched_groups: + for index, element in enumerate(matched_groups): + groups[f"#{config_tag_key}{index}"] = element if self.exception: for config_tag_key in self.exception: config_tag_key: str - tag_matcher = self.exception[config_tag_key] - if ( - is_matched_tag(config_tag_key, tag_matcher, tags) - != MatchingType.NOT_MATCHED - ): - return False + is_matched, matched_groups = is_matched_tag( + config_tag_key, self.exception[config_tag_key], tags + ) + if is_matched != MatchingType.NOT_MATCHED: + return False, {} - return True + return True, groups def get_mapcss_selector(self, prefix: str = "") -> str: """ @@ -195,10 +197,21 @@ class Matcher: return {} +def get_shape_specifications( + structure: List[Union[str, Dict[str, Any]]] +) -> List[dict]: + """Parse shape specification from scheme.""" + shapes: List[dict] = [] + for shape_specification in structure: + if isinstance(shape_specification, str): + shapes.append({"shape": shape_specification}) + else: + shapes.append(shape_specification) + return shapes + + class NodeMatcher(Matcher): - """ - Tag specification matcher. - """ + """Tag specification matcher.""" def __init__( self, structure: Dict[str, Any], group: Dict[str, Any] @@ -212,15 +225,15 @@ class NodeMatcher(Matcher): self.shapes: Optional[IconDescription] = None if "shapes" in structure: - self.shapes = structure["shapes"] + self.shapes = get_shape_specifications(structure["shapes"]) self.over_icon: Optional[IconDescription] = None if "over_icon" in structure: - self.over_icon = structure["over_icon"] + self.over_icon = get_shape_specifications(structure["over_icon"]) self.add_shapes: Optional[IconDescription] = None if "add_shapes" in structure: - self.add_shapes = structure["add_shapes"] + self.add_shapes = get_shape_specifications(structure["add_shapes"]) self.set_main_color: Optional[str] = None if "set_main_color" in structure: @@ -232,23 +245,21 @@ class NodeMatcher(Matcher): self.under_icon: Optional[IconDescription] = None if "under_icon" in structure: - self.under_icon = structure["under_icon"] + self.under_icon = get_shape_specifications(structure["under_icon"]) self.with_icon: Optional[IconDescription] = None if "with_icon" in structure: - self.with_icon = structure["with_icon"] + self.with_icon = get_shape_specifications(structure["with_icon"]) def get_clean_shapes(self) -> Optional[List[str]]: """Get list of shape identifiers for shapes.""" if not self.shapes: return None - return [(x if isinstance(x, str) else x["shape"]) for x in self.shapes] + return [x["shape"] for x in self.shapes] class WayMatcher(Matcher): - """ - Special tag matcher for ways. - """ + """Special tag matcher for ways.""" def __init__(self, structure: Dict[str, Any], scheme: "Scheme") -> None: super().__init__(structure) @@ -260,38 +271,42 @@ class WayMatcher(Matcher): self.style[key] = scheme.get_color(style[key]).hex.upper() else: self.style[key] = style[key] - self.priority: int = 0 + + self.priority: float = 0.0 if "priority" in structure: self.priority = structure["priority"] + self.parallel_offset: float = 0.0 + if parallel_offset := structure.get("parallel_offset"): + self.parallel_offset = parallel_offset + def get_style(self) -> Dict[str, Any]: """Return way SVG style.""" return self.style class RoadMatcher(Matcher): - """ - Special tag matcher for highways. - """ + """Special tag matcher for highways.""" def __init__(self, structure: Dict[str, Any], scheme: "Scheme") -> None: super().__init__(structure) self.border_color: Color = Color( scheme.get_color(structure["border_color"]) ) - self.color: Color = Color("white") + self.color: Color = scheme.get_color("road_color") if "color" in structure: self.color = Color(scheme.get_color(structure["color"])) self.default_width: float = structure["default_width"] - self.priority: float = 0 + self.priority: float = 0.0 if "priority" in structure: self.priority = structure["priority"] - def get_priority(self, tags: Dict[str, str]) -> float: - layer: float = 0 + def get_priority(self, tags: Tags) -> float: + """Get priority for drawing order.""" + layer: float = 0.0 if "layer" in tags: layer = float(tags.get("layer")) - return 1000 * layer + self.priority + return 1000.0 * layer + self.priority class Scheme: @@ -301,7 +316,56 @@ class Scheme: Specifies map colors and rules to draw icons for OpenStreetMap tags. """ - def __init__(self, file_name: Path) -> None: + def __init__(self, content: Dict[str, Any]) -> None: + self.node_matchers: List[NodeMatcher] = [] + if "node_icons" in content: + for group in content["node_icons"]: + for element in group["tags"]: + self.node_matchers.append(NodeMatcher(element, group)) + + self.colors: Dict[str, str] = ( + content["colors"] if "colors" in content else {} + ) + self.material_colors: Dict[str, str] = ( + content["material_colors"] if "material_colors" in content else {} + ) + + self.way_matchers: List[WayMatcher] = ( + [WayMatcher(x, self) for x in content["ways"]] + if "ways" in content + else [] + ) + self.road_matchers: List[RoadMatcher] = ( + [RoadMatcher(x, self) for x in content["roads"]] + if "roads" in content + else [] + ) + self.area_matchers: List[Matcher] = ( + [Matcher(x) for x in content["area_tags"]] + if "area_tags" in content + else [] + ) + self.keys_to_write: List[str] = ( + content["keys_to_write"] if "keys_to_write" in content else [] + ) + self.prefix_to_write: List[str] = ( + content["prefix_to_write"] if "prefix_to_write" in content else [] + ) + self.keys_to_skip: List[str] = ( + content["keys_to_skip"] if "keys_to_skip" in content else [] + ) + self.prefix_to_skip: List[str] = ( + content["prefix_to_skip"] if "prefix_to_skip" in content else [] + ) + self.tags_to_skip: Dict[str, str] = ( + content["tags_to_skip"] if "tags_to_skip" in content else {} + ) + + # Storage for created icon sets. + self.cache: Dict[str, Tuple[IconSet, int]] = {} + + @classmethod + def from_file(cls, file_name: Path) -> "Scheme": """ :param file_name: name of the scheme file with tags, colors, and tag key specification @@ -310,30 +374,7 @@ class Scheme: content: Dict[str, Any] = yaml.load( input_file.read(), Loader=yaml.FullLoader ) - self.node_matchers: List[NodeMatcher] = [] - for group in content["node_icons"]: - for element in group["tags"]: - self.node_matchers.append(NodeMatcher(element, group)) - - self.colors: Dict[str, str] = content["colors"] - self.material_colors: Dict[str, str] = content["material_colors"] - - self.way_matchers: List[WayMatcher] = [ - WayMatcher(x, self) for x in content["ways"] - ] - self.road_matchers: List[RoadMatcher] = [ - RoadMatcher(x, self) for x in content["roads"] - ] - self.area_matchers: List[Matcher] = [ - Matcher(x) for x in content["area_tags"] - ] - self.tags_to_write: List[str] = content["tags_to_write"] - self.prefix_to_write: List[str] = content["prefix_to_write"] - self.tags_to_skip: List[str] = content["tags_to_skip"] - self.prefix_to_skip: List[str] = content["prefix_to_skip"] - - # Storage for created icon sets. - self.cache: Dict[str, Tuple[IconSet, int]] = {} + return cls(content) def get_color(self, color: str) -> Color: """ @@ -343,42 +384,91 @@ class Scheme: :return: color specification """ if color in self.colors: - return Color(self.colors[color]) + specification: Union[str, dict] = self.colors[color] + if isinstance(specification, str): + return Color(self.colors[color]) + + color: Color = self.get_color(specification["color"]) + if "darken" in specification: + percent: float = float(specification["darken"]) + color.set_luminance(color.get_luminance() * (1 - percent)) + return color + if color.lower() in self.colors: return Color(self.colors[color.lower()]) + try: return Color(color) except (ValueError, AttributeError): - return DEFAULT_COLOR + logging.debug(f"Unknown color `{color}`.") + return Color(self.colors["default"]) - def is_no_drawable(self, key: str) -> bool: + def get_default_color(self) -> Color: + """Get default color for a main icon.""" + return self.get_color("default") + + def get_extra_color(self) -> Color: + """Get default color for an extra icon.""" + return self.get_color("extra") + + def get(self, variable_name: str): + """ + FIXME: colors should be variables. + """ + if variable_name in self.colors: + return self.colors[variable_name] + return 0.0 + + def is_no_drawable(self, key: str, value: str) -> bool: """ Return true if key is specified as no drawable (should not be represented on the map as icon set or as text) by the scheme. :param key: OpenStreetMap tag key + :param value: OpenStreetMap tag value """ - if key in self.tags_to_write or key in self.tags_to_skip: + if ( + key in self.keys_to_write + self.keys_to_skip + or key in self.tags_to_skip + and self.tags_to_skip[key] == value + ): return True - for prefix in self.prefix_to_write + self.prefix_to_skip: - if key[: len(prefix) + 1] == f"{prefix}:": + + if ":" in key: + prefix: str = key.split(":")[0] + if prefix in self.prefix_to_write + self.prefix_to_skip: return True + return False - def is_writable(self, key: str) -> bool: + def is_writable(self, key: str, value: str) -> bool: """ Return true if key is specified as writable (should be represented on the map as text) by the scheme. :param key: OpenStreetMap tag key + :param value: OpenStreetMap tag value """ - if key in self.tags_to_skip: + if ( + key in self.keys_to_skip + or key in self.tags_to_skip + and self.tags_to_skip[key] == value + ): return False - if key in self.tags_to_write: + + if key in self.keys_to_write: return True - for prefix in self.prefix_to_write: - if key[: len(prefix) + 1] == f"{prefix}:": - return True + + prefix: Optional[str] = None + if ":" in key: + prefix = key.split(":")[0] + + if prefix in self.prefix_to_skip: + return False + + if prefix in self.prefix_to_write: + return True + return False def get_icon( @@ -406,11 +496,13 @@ class Scheme: main_icon: Optional[Icon] = None extra_icons: List[Icon] = [] priority: int = 0 + color: Optional[Color] = None for index, matcher in enumerate(self.node_matchers): if not matcher.replace_shapes and main_icon: continue - if not matcher.is_matched(tags, configuration): + matching, groups = matcher.is_matched(tags, configuration) + if not matching: continue if ( not configuration.ignore_level_matching @@ -423,7 +515,7 @@ class Scheme: processed |= matcher_tags if matcher.shapes: specifications = [ - self.get_shape_specification(x, extractor) + self.get_shape_specification(x, extractor, groups) for x in matcher.shapes ] main_icon = Icon(specifications) @@ -437,18 +529,18 @@ class Scheme: processed |= matcher_tags if matcher.add_shapes: specifications = [ - self.get_shape_specification(x, extractor, Color("#888888")) + self.get_shape_specification( + x, extractor, color=self.get_extra_color() + ) for x in matcher.add_shapes ] extra_icons += [Icon(specifications)] processed |= matcher_tags if matcher.set_main_color and main_icon: - main_icon.recolor(self.get_color(matcher.set_main_color)) + color = self.get_color(matcher.set_main_color) if matcher.set_opacity and main_icon: main_icon.opacity = matcher.set_opacity - color: Optional[Color] = None - if "material" in tags: value: str = tags["material"] if value in self.material_colors: @@ -465,24 +557,36 @@ class Scheme: color = self.get_color(tags[color_tag_key]) processed.add(color_tag_key) + if not main_icon: + dot_spec: ShapeSpecification = ShapeSpecification( + extractor.get_shape(DEFAULT_SHAPE_ID), self.get_color("default") + ) + main_icon: Icon = Icon([dot_spec]) + if main_icon and color: main_icon.recolor(color) - default_shape = extractor.get_shape(DEFAULT_SHAPE_ID) - if not main_icon: - main_icon = Icon([ShapeSpecification(default_shape)]) + default_icon: Optional[Icon] = None + if configuration.show_overlapped: + small_dot_spec: ShapeSpecification = ShapeSpecification( + extractor.get_shape(DEFAULT_SMALL_SHAPE_ID), + color if color else self.get_color("default"), + ) + default_icon = Icon([small_dot_spec]) - returned: IconSet = IconSet(main_icon, extra_icons, processed) + returned: IconSet = IconSet( + main_icon, extra_icons, default_icon, processed + ) self.cache[tags_hash] = returned, priority - for key in ["direction", "camera:direction"]: + for key in "direction", "camera:direction": if key in tags: for specification in main_icon.shape_specifications: if ( - DirectionSet(tags[key]).is_right() is False - and specification.shape.is_right_directed is True - or specification.shape.is_right_directed is True - and specification.shape.is_right_directed is False + DirectionSet(tags[key]).is_right() is not None + and specification.shape.is_right_directed is not None + and DirectionSet(tags[key]).is_right() + != specification.shape.is_right_directed ): specification.flip_horizontally = True @@ -493,119 +597,51 @@ class Scheme: line_styles = [] for matcher in self.way_matchers: - if not matcher.is_matched(tags): + matching, _ = matcher.is_matched(tags) + if not matching: continue - line_styles.append(LineStyle(matcher.style, matcher.priority)) + line_style: LineStyle = LineStyle( + matcher.style, matcher.parallel_offset, matcher.priority + ) + line_styles.append(line_style) return line_styles def get_road(self, tags: Dict[str, Any]) -> Optional[RoadMatcher]: """Get road matcher if tags are matched.""" for matcher in self.road_matchers: - if not matcher.is_matched(tags): + matching, _ = matcher.is_matched(tags) + if not matching: continue return matcher return None - def construct_text( - self, tags: Dict[str, str], draw_captions: str, processed: Set[str] - ) -> List[Label]: - """Construct labels for not processed tags.""" - texts: List[Label] = [] - - name = None - alt_name = None - if "name" in tags: - name = tags["name"] - processed.add("name") - elif "name:en" in tags: - if not name: - name = tags["name:en"] - processed.add("name:en") - processed.add("name:en") - if "alt_name" in tags: - if alt_name: - alt_name += ", " - else: - alt_name = "" - alt_name += tags["alt_name"] - processed.add("alt_name") - if "old_name" in tags: - if alt_name: - alt_name += ", " - else: - alt_name = "" - alt_name += "ex " + tags["old_name"] - - address: List[str] = get_address(tags, draw_captions, processed) - - if name: - texts.append(Label(name, Color("black"))) - if alt_name: - texts.append(Label(f"({alt_name})")) - if address: - texts.append(Label(", ".join(address))) - - if draw_captions == "main": - return texts - - texts += get_text(tags, processed) - - if "route_ref" in tags: - texts.append(Label(tags["route_ref"].replace(";", " "))) - processed.add("route_ref") - if "cladr:code" in tags: - texts.append(Label(tags["cladr:code"], size=7)) - processed.add("cladr:code") - if "website" in tags: - link = tags["website"] - if link[:7] == "http://": - link = link[7:] - if link[:8] == "https://": - link = link[8:] - if link[:4] == "www.": - link = link[4:] - if link[-1] == "/": - link = link[:-1] - link = link[:25] + ("..." if len(tags["website"]) > 25 else "") - texts.append(Label(link, Color("#000088"))) - processed.add("website") - for key in ["phone"]: - if key in tags: - texts.append(Label(tags[key], Color("#444444"))) - processed.add(key) - if "height" in tags: - texts.append(Label(f"↕ {tags['height']} m")) - processed.add("height") - for tag in tags: - if self.is_writable(tag) and tag not in processed: - texts.append(Label(tags[tag])) - return texts - - def is_area(self, tags: Dict[str, str]) -> bool: + def is_area(self, tags: Tags) -> bool: """Check whether way described by tags is area.""" for matcher in self.area_matchers: - if matcher.is_matched(tags): + matching, _ = matcher.is_matched(tags) + if matching: return True return False - def process_ignored( - self, tags: Dict[str, str], processed: Set[str] - ) -> None: + def process_ignored(self, tags: Tags, processed: Set[str]) -> None: """ Mark all ignored tag as processed. :param tags: input tag dictionary :param processed: processed set """ - [processed.add(tag) for tag in tags if self.is_no_drawable(tag)] + processed.update( + set(tag for tag in tags if self.is_no_drawable(tag, tags[tag])) + ) def get_shape_specification( self, structure: Union[str, Dict[str, Any]], extractor: ShapeExtractor, - color: Color = DEFAULT_COLOR, + groups: Dict[str, str] = None, + color: Optional[Color] = None, ) -> ShapeSpecification: """ Parse shape specification from structure, that is just shape string @@ -613,31 +649,33 @@ class Scheme: and offset (optional). """ shape: Shape = extractor.get_shape(DEFAULT_SHAPE_ID) - color: Color = color - offset: np.ndarray = np.array((0, 0)) + color: Color = ( + color if color is not None else Color(self.colors["default"]) + ) + offset: np.ndarray = np.array((0.0, 0.0)) flip_horizontally: bool = False flip_vertically: bool = False use_outline: bool = True - if isinstance(structure, str): - shape = extractor.get_shape(structure) - elif isinstance(structure, dict): - if "shape" in structure: - shape = extractor.get_shape(structure["shape"]) - else: - logging.error( - "Invalid shape specification: `shape` key expected." - ) - if "color" in structure: - color = self.get_color(structure["color"]) - if "offset" in structure: - offset = np.array(structure["offset"]) - if "flip_horizontally" in structure: - flip_horizontally = structure["flip_horizontally"] - if "flip_vertically" in structure: - flip_vertically = structure["flip_vertically"] - if "outline" in structure: - use_outline = structure["outline"] + structure: Dict[str, Any] + if "shape" in structure: + shape_id: str = structure["shape"] + if groups: + for key in groups: + shape_id = shape_id.replace(key, groups[key]) + shape = extractor.get_shape(shape_id) + else: + logging.error("Invalid shape specification: `shape` key expected.") + if "color" in structure: + color = self.get_color(structure["color"]) + if "offset" in structure: + offset = np.array(structure["offset"]) + if "flip_horizontally" in structure: + flip_horizontally = structure["flip_horizontally"] + if "flip_vertically" in structure: + flip_vertically = structure["flip_vertically"] + if "outline" in structure: + use_outline = structure["outline"] return ShapeSpecification( shape, diff --git a/map_machine/scheme/default.yml b/map_machine/scheme/default.yml index 9eeb71c..84ff89b 100644 --- a/map_machine/scheme/default.yml +++ b/map_machine/scheme/default.yml @@ -2,10 +2,17 @@ colors: # Entity - direction_view_color: "#E0F0FF" + default: "#444444" + extra: "#888888" + + direction_view_color: "#C8E8FF" direction_camera_color: "#0088FF" background_color: "#EEEEEE" + road_color: "#FFFFFF" + text_color: "#444444" + text_main_color: "#000000" + text_outline_color: "#FFFFFF" wheat_color: "#F0DCAA" wheat_border_color: "#F4D67F" @@ -31,8 +38,10 @@ colors: allotments_color: "#D0E0D0" beach_color: "#F0E0C0" boundary_color: "#880088" - building_border_color: "#E0D0C0" # "AAAAAA" - building_color: "#F8F0E8" # "D0D0C0" + building_border_color: "#E0D0C0" + building_color: "#F8F0E8" + building_construction_border_color: "#C4C0BC" + building_construction_color: "#D4D0CC" construction_color: "#CCCCCC" cycle_color: "#4444EE" desert_color: "#F0E0D0" @@ -43,6 +52,8 @@ colors: farmland_border_color: "#DDCC99" farmland_darker_color: "#998855" ferry_terminal_color: "#AABBDD" + foot_area_color: "#DDDDDD" + foot_area_border_color: "#BBBBBB" foot_border_color: "#FFFFFF" foot_color: "#B89A74" grass_border_color: "#BFD098" @@ -56,12 +67,13 @@ colors: orchard_border_color: "#98BC84" outline_color: "#FFFFFF" parking_color: "#DDCC99" + park_color: "#CFE0A8" pitch_color: "#AADDCC" pitch_border_color: "#88BBAA" platform_border_color: "#AAAAAA" platform_color: "#CCCCCC" - playground_border_color: "#663300" - playground_color: "#884400" + playground_border_color: "#FFAA88" + playground_color: "#FFDDCC" ridge_color: "#000000" road_border_color: "#CCCCCC" rock_color: "#DDDDDD" @@ -71,12 +83,125 @@ colors: track_color: "#A88A64" trunk_color: "#97612b" tree_color: "#98AC64" + wall_bottom_1_color: "#AAAAAA" + wall_bottom_2_color: "#C3C3C3" + wall_color: "#E8E8E8" + wall_construction_color: "#84807C" water_border_color: "#6688BB" water_color: "#AACCFF" wetland_color: "#BFE0D8" wood_border_color: "#A8BC74" wood_color: "#B8CC84" + runway_color: "#999399" + runway_border_color: {color: runway_color, darken: 0.25} + taxiway_color: "#AAA4AA" + taxiway_border_color: {color: taxiway_color, darken: 0.25} + + sell_color: "#880088" + craft_color: "#008800" + + # Colors not in W3C + + rose: "#FF007F" # Wikipedia + slate_blue: "#6A5ACD" # W3C slateblue + +colors_dark: # dark + + # Entity + + default: "#CCCCCC" + + direction_view_color: {color: "#C8E8FF", darken: 0.75} + direction_camera_color: "#0088FF" + + background_color: "#222222" + road_color: "#000000" + text_color: "#DDDDDD" + text_main_color: "#ffffff" + text_outline_color: "#000000" + + wheat_color: "#F0DCAA" + wheat_border_color: "#F4D67F" + wheat_dark_color: "#BF9340" + rye_color: "#E0CA96" + rye_dark_color: "#AE955D" + oat_color: "#EDDDB7" + oat_dark_color: "#C4894A" + barley_color: "#F3EEC4" + barley_border_color: "#D2CE9D" + barley_dark_color: "#908F62" + sunflower_dark_color: "#DEAC4A" + + motorway_border_color: "#CC8800" + motorway_color: "#FFAA33" + primary_border_color: {color: "#AA8800", darken: 0.3} + primary_color: {color: "#FFDD66", darken: 0.75} + secondary_border_color: {color: "#BB9911", darken: 0.3} + secondary_color: {color: "#FFEE77", darken: 0.75} + tertiary_border_color: {color: "#CCAA22", darken: 0.3} + tertiary_color: {color: "#FFFF88", darken: 0.75} + + allotments_color: "#D0E0D0" + beach_color: "#F0E0C0" + boundary_color: "#880088" + building_border_color: "#888888" + building_color: "#444444" + construction_color: {color: "#CCCCCC", darken: 0.75} + cycle_color: "#4444EE" + desert_color: "#F0E0D0" + decidious_color: "#FCAF3E" + emergency_color: "#DD2222" + evergreen_color: "#688C44" + farmland_color: "#FFEEBB" + farmland_border_color: "#DDCC99" + farmland_darker_color: "#998855" + ferry_terminal_color: "#AABBDD" + foot_area_color: "#222222" + foot_area_border_color: "#444444" + foot_border_color: "#000000" + foot_color: {color: "#B89A74", darken: 0.5} + grass_border_color: {color: "#BFD098", darken: 0.75} + grass_color: {color: "#CFE0A8", darken: 0.75} + hidden_color: "#FFFFFF" + indoor_border_color: "#C0B8B0" + indoor_color: "#E8E4E0" + meadow_border_color: "#BFD078" + meadow_color: "#CFE088" + orchard_color: "#B8DCA4" + orchard_border_color: "#98BC84" + outline_color: "#FFFFFF" + parking_color: {color: "#DDCC99", darken: 0.75} + park_color: {color: "#CFE0A8", darken: 0.75} + pitch_color: {color: "#AADDCC", darken: 0.75} + pitch_border_color: {color: "#88BBAA", darken: 0.75} + platform_border_color: "#AAAAAA" + platform_color: "#CCCCCC" + playground_border_color: {color: "#FFAA88", darken: 0.75} + playground_color: {color: "#FFDDCC", darken: 0.75} + ridge_color: "#000000" + road_border_color: "#444444" + rock_color: "#DDDDDD" + route_color: "#FFFFFF" + sand_color: "#E8E0C0" + scree_color: "#CCCCCC" + track_color: "#A88A64" + trunk_color: "#97612b" + tree_color: "#98AC64" + water_border_color: "#6688BB" + water_color: "#AACCFF" + wall_bottom_1_color: "#444444" + wall_bottom_2_color: "#222222" + wall_color_start: 0.0 + wetland_color: "#BFE0D8" + wood_border_color: {color: "#A8BC74", darken: 0.75} + wood_color: {color: "#B8CC84", darken: 0.75} + + runway_color: "#999399" + runway_border_color: {color: runway_color, darken: 0.25} + taxiway_color: "#AAA4AA" + taxiway_border_color: {color: taxiway_color, darken: 0.25} + sell_color: "#880088" # Colors not in W3C @@ -84,10 +209,26 @@ colors: rose: "#FF007F" # Wikipedia slate_blue: "#6A5ACD" # W3C slateblue +carto_colors: + building_border_color: {color: building_color, darken: 0.15} + building_color: "#d9d0c9" + cemetery_color: "#aacbaf" + commercial_color: "#f2dad9" + commercial_border_color: "#d1b2b0" + grass_color: "#cdebb0" + industrial_color: "#ebdbe8" + industrial_border_color: "#d1b2b0" + military_color: "#f55" + park_color: "#c8facc" + residential_color: "#e0dfdf" + residential_border_color: "#b9b9b9" + wood_color: "#add19e" + material_colors: bronze: "#CD7F32" concrete: "#AAAAAA" + glass: "#CCEEFF" node_icons: @@ -101,7 +242,7 @@ node_icons: draw: false - group: "Huge transport hubs" - start_zoom_level: 10 + start_zoom_level: 10.0 tags: - tags: {amenity: ferry_terminal} shapes: [anchor] @@ -117,7 +258,7 @@ node_icons: shapes: [rocket_on_launch_pad] - group: "Normal transport hubs" - start_zoom_level: 11 + start_zoom_level: 11.0 tags: - tags: {aeroway: launchpad} shapes: [rocket_flying] @@ -165,10 +306,12 @@ node_icons: shapes: [taxi] - group: "Big territory" - start_zoom_level: 12 + start_zoom_level: 12.0 tags: - tags: {leisure: fishing} shapes: [fishing_angle] + - tags: {historic: archaeological_site} + shapes: [amphora] - tags: {leisure: swimming_area} shapes: [swimming_area] - tags: {leisure: swimming_pool} @@ -207,7 +350,7 @@ node_icons: shapes: [{shape: pear, color: orchard_border_color}] - group: "Bigger objects" - start_zoom_level: 13 + start_zoom_level: 13.0 tags: - tags: {waterway: waterfall} shapes: [{shape: waterfall, color: water_border_color}] @@ -215,25 +358,26 @@ node_icons: shapes: [cliff] - tags: {natural: peak} shapes: [triangle_small] + - tags: {natural: saddle} + shapes: [saddle] - tags: {natural: crater} exception: {diameter: "*"} shapes: [crater] - - tags: - natural: volcano - shapes: [stratovolcano, {shape: smoke, offset: [0, -3]}] + - tags: {natural: volcano} + shapes: [stratovolcano, {shape: smoke_2, offset: [0, -3]}] - tags: {natural: volcano, volcano:type: stratovolcano} - shapes: [stratovolcano, {shape: smoke, offset: [0, -3]}] + shapes: [stratovolcano, {shape: smoke_2, offset: [0, -3]}] - tags: {natural: volcano, volcano:type: shield} - shapes: [shield_volcano, {shape: smoke, offset: [0, -1]}] + shapes: [shield_volcano, {shape: smoke_2, offset: [0, -1]}] - tags: {natural: volcano, volcano:type: scoria} - shapes: [volcanic_cone, {shape: smoke, offset: [0, -2]}] + shapes: [volcanic_cone, {shape: smoke_2, offset: [0, -2]}] - tags: {natural: volcano, volcano:status: active} - shapes: [stratovolcano, {shape: smoke, offset: [0, -3]}] + shapes: [stratovolcano, {shape: lava, offset: [0, -3]}] - tags: {natural: volcano, volcano:status: dormant} - shapes: [stratovolcano, {shape: smoke_2, offset: [1, -3]}] + shapes: [stratovolcano, {shape: smoke, offset: [1, -3]}] - tags: {natural: volcano, volcano:status: extinct} shapes: [stratovolcano] @@ -241,32 +385,32 @@ node_icons: natural: volcano volcano:type: stratovolcano volcano:status: active - shapes: [stratovolcano, {shape: smoke, offset: [0, -3]}] + shapes: [stratovolcano, {shape: lava, offset: [0, -3]}] - tags: natural: volcano volcano:type: shield volcano:status: active - shapes: [shield_volcano, {shape: smoke, offset: [0, -1]}] + shapes: [shield_volcano, {shape: lava, offset: [0, -1]}] - tags: natural: volcano volcano:type: scoria volcano:status: active - shapes: [volcanic_cone, {shape: smoke, offset: [0, -2]}] + shapes: [volcanic_cone, {shape: lava, offset: [0, -2]}] - tags: natural: volcano volcano:type: stratovolcano volcano:status: dormant - shapes: [stratovolcano, {shape: smoke_2, offset: [1, -3]}] + shapes: [stratovolcano, {shape: smoke, offset: [1, -3]}] - tags: natural: volcano volcano:type: shield volcano:status: dormant - shapes: [shield_volcano, {shape: smoke_2, offset: [1, -1]}] + shapes: [shield_volcano, {shape: smoke, offset: [1, -1]}] - tags: natural: volcano volcano:type: scoria volcano:status: dormant - shapes: [volcanic_cone, {shape: smoke_2, offset: [1, -2]}] + shapes: [volcanic_cone, {shape: smoke, offset: [1, -2]}] - tags: natural: volcano volcano:type: stratovolcano @@ -286,6 +430,8 @@ node_icons: - tags: {historic: castle} location_restrictions: {include: [jp]} shapes: [japan_castle] + - tags: {historic: fort} + shapes: [fort] - tags: {shop: mall} shapes: [bag] - tags: {shop: mall, building: "yes"} @@ -294,12 +440,17 @@ node_icons: shapes: [slide_and_water] - group: "Important big objects" - start_zoom_level: 14 + start_zoom_level: 14.0 tags: - tags: {amenity: fire_station} location_restrictions: {include: [jp]} shapes: [{shape: japan_fire_station, color: emergency_color}] + - tags: {amenity: courthouse} + shapes: [gavel] - tags: {amenity: police} + location_restrictions: {include: [jp]} + shapes: [japan_police_station] + - tags: {amenity: police, name:en: Koban} location_restrictions: {include: [jp]} shapes: [japan_koban] - tags: {building: townhall} @@ -312,8 +463,15 @@ node_icons: shapes: [medicine_bottle] - tags: {amenity: embassy} shapes: [waving_flag] + - tags: {office: diplomatic, diplomatic: embassy} + shapes: [waving_flag] + - tags: {man_made: monitoring_station, monitoring:weather: "yes"} + shapes: [japan_weather_station] + location_restrictions: {include: [jp]} - tags: {amenity: veterinary} shapes: [dog_and_cross] + - tags: {tourism: apartment} + shapes: [bed_with_floor_and_ceiling] - tags: {tourism: hotel} shapes: [bed] - tags: {building: hotel} @@ -322,8 +480,14 @@ node_icons: shapes: [two_beds] - tags: {tourism: motel} shapes: [{shape: car, offset: [0, 4]}, {shape: bed, offset: [0, -2]}] + - tags: {tourism: guest_house} + shapes: [bed_and_roof] - tags: {amenity: hospital} + location_restrictions: {include: world, exclude: [jp]} shapes: [greek_cross] + - tags: {amenity: hospital} + location_restrictions: {include: [jp]} + shapes: [japan_public_health_center] - tags: {amenity: clinic} shapes: [greek_cross_in_box] - tags: {amenity: doctors} @@ -343,24 +507,26 @@ node_icons: - tags: {amenity: car_sharing} shapes: [{shape: car, offset: [0, 3]}, {shape: sharing, offset: [0, -4]}] - tags: {amenity: car_wash} - shapes: [car_wash] + shapes: + - {shape: car, offset: [0, 3]} + - {shape: shower_head, offset: [0, -4]} # Place of worship - tags: {building: shrine, religion: shinto} shapes: [japan_shinto_shrine] - tags: {religion: christian} - shapes: [cross] + shapes: [latin_cross] - tags: {amenity: place_of_worship, religion: christian} - shapes: [cross] + shapes: [latin_cross] - tags: amenity: place_of_worship religion: christian denomination: catholic - shapes: [cross] + shapes: [latin_cross] - tags: amenity: place_of_worship religion: christian denomination: russian_orthodox - shapes: [russian_orthodox] + shapes: [russian_orthodox_cross] - tags: amenity: place_of_worship religion: christian @@ -372,11 +538,11 @@ node_icons: denomination: baptist shapes: [baptist] - tags: {amenity: place_of_worship, religion: muslim} - shapes: [muslim] + shapes: [crescent] - tags: {amenity: place_of_worship, religion: buddhist} shapes: [dharmachakra] - tags: {amenity: place_of_worship, religion: jewish} - shapes: [jewish] + shapes: [star_of_david] - tags: {historic: tomb, tomb: mausoleum} shapes: [mausoleum] - tags: {historic: "*"} @@ -385,10 +551,12 @@ node_icons: location_restrictions: {include: [jp]} - group: "Normal big objects" - start_zoom_level: 15 + start_zoom_level: 15.0 tags: - tags: {shop: supermarket} shapes: [supermarket_cart] + - tags: {shop: general} + shapes: [bag] - tags: {amenity: arts_centre} shapes: [picture] - tags: {amenity: bank} @@ -399,16 +567,24 @@ node_icons: shapes: [card_and_dice] - tags: {amenity: community_centre} shapes: [two_people_together] + - tags: {amenity: gym} + shapes: [dumbbell] + - tags: {amenity: social_facility} + shapes: [two_people_together] - tags: {amenity: internet_cafe} shapes: [at_in_square] - tags: {amenity: library} shapes: [book] + - tags: {amenity: marketplace} + shapes: [marketplace] - tags: {amenity: prison} shapes: [prison] - tags: {amenity: stripclub} shapes: [pole_dancer] - tags: {club: computer} shapes: [glider] + - tags: {leisure: hackerspace} + shapes: [glider] - tags: {man_made: survey_point} shapes: [survey_point] - tags: {leisure: amusement_arcade} @@ -425,6 +601,16 @@ node_icons: shapes: [bowling_ball] - tags: {leisure: dog_park} shapes: [dog] + - tags: {leisure: escape_game} + shapes: [{shape: maze, offset: [-2, 0]}, {shape: arrow_right_short, offset: [5, -1]}] + - tags: {leisure: maze} + shapes: [maze] + - tags: {attraction: maze} + shapes: [maze] + - tags: {maze: labyrinth} + shapes: [maze] + - tags: {tourism: maze} + shapes: [maze] - tags: {leisure: miniature_golf} shapes: [golf_club_and_ball] - tags: {leisure: sauna} @@ -444,7 +630,7 @@ node_icons: - tags: {leisure: playground} shapes: [toy_horse] - tags: {amenity: theatre} - shapes: [theatre] + shapes: [curtains] - tags: {amenity: bar} shapes: [cocktail_glass] - tags: {amenity: pub} @@ -455,18 +641,26 @@ node_icons: shapes: [steak_and_fork] - tags: {amenity: food_court} shapes: [food_court] + - tags: {craft: shoemaker} + shapes: [{shape: shoe, color: sell_color}] - tags: {shop: fishing} shapes: [{shape: fishing_angle, color: sell_color}] - tags: {shop: alcohol} shapes: [{shape: bottle, color: sell_color}] + - tags: {shop: antiques} + shapes: [{shape: amphora, color: sell_color}] - tags: {shop: art} shapes: [{shape: picture, color: sell_color}] - tags: {shop: bakery} shapes: [cupcake] + - tags: {shop: bag} + shapes: [{shape: bag, color: sell_color}] - tags: {shop: bed} shapes: [{shape: bed, color: sell_color}] - tags: {shop: beauty} shapes: [vanity_mirror] + - tags: {shop: cosmetics} + shapes: [vanity_mirror] - tags: {shop: bicycle} shapes: [{shape: bicycle, color: sell_color}] - tags: {shop: books} @@ -475,42 +669,72 @@ node_icons: shapes: [knives] - tags: {shop: car} shapes: [{shape: car, color: sell_color}] + - tags: {shop: chocolate} + shapes: [cupcake] - tags: {shop: coffee} shapes: [{shape: coffee_cup, color: sell_color}] + - tags: {shop: confectionery} + shapes: [cupcake] - tags: {shop: copyshop} shapes: [sheets] - tags: {shop: dairy} - shapes: [milk] + shapes: [aseptic_carton] - tags: {shop: doityourself} shapes: [wretch_and_hammer] + - tags: {shop: dry_cleaning} + shapes: [washing_machine] + - tags: {shop: farm} + shapes: [{shape: apple, color: sell_color}] - tags: {shop: fireplace} shapes: [{shape: fireplace, color: sell_color}] + - tags: {shop: florist} + shapes: [{shape: flower_in_pot, color: sell_color}] + - tags: {shop: furniture} + shapes: [{shape: drawer, color: sell_color}] + - tags: {shop: greengrocer} + shapes: [{shape: apple, color: sell_color}] - tags: {shop: hairdresser} shapes: [comb_and_scissors] - tags: {shop: hardware} shapes: [wretch_and_hammer] - tags: {shop: hifi} - shapes: [{shape: hifi, color: sell_color}] + shapes: [{shape: hi_fi, color: sell_color}] - tags: {shop: houseware} shapes: [{shape: pan, color: sell_color}] - tags: {shop: jewelry} shapes: [{shape: diamond, color: sell_color}] - tags: {shop: jewellery} shapes: [{shape: diamond, color: sell_color}] + - tags: {craft: jeweller} + shapes: [{shape: diamond, color: craft_color}] + - tags: {shop: laundry} + shapes: [washing_machine] - tags: {shop: massage} shapes: [massage] + - tags: {shop: medical_supply} + shapes: [{shape: medicine_bottle, color; sell_color}] - tags: {shop: mobile_phone} shapes: [{shape: phone, color: sell_color}] + - tags: {shop: newsagent} + shapes: [sheets] + - tags: {shop: optician} + shapes: [glasses] + - tags: {shop: pastry} + shapes: [cupcake] - tags: {shop: pet} shapes: [{shape: dog, color: sell_color}] - tags: {shop: photo} shapes: [{shape: photo_camera, color: sell_color}] + - tags: {shop: photography} + shapes: [{shape: photo_camera, color: sell_color}] - tags: {shop: shoes} shapes: [{shape: shoe, color: sell_color}] - tags: {shop: sports} shapes: [{shape: dumbbell, color: sell_color}] + - tags: {shop: travel_agency} + shapes: [globe] - tags: {shop: milk} - shapes: [milk] + shapes: [aseptic_carton] - tags: {shop: wine} shapes: [{shape: bottle_and_wine_glass, color: sell_color}] - tags: {building: store} @@ -519,6 +743,10 @@ node_icons: shapes: [ticket] - tags: {shop: tailor} shapes: [t_shirt_and_scissors] + - tags: {shop: tyres} + shapes: [{shape: tyre, color: sell_color}] + - tags: {shop: toys} + shapes: [{shape: toy_horse, color: sell_color}] - tags: {craft: tailor} shapes: [t_shirt_and_scissors] - tags: {shop: video} @@ -568,21 +796,93 @@ node_icons: shapes: [table] - group: "Big objects not for all" - start_zoom_level: 15 + start_zoom_level: 15.0 tags: + - tags: {building: container} + shapes: [building_container] + - tags: {building: houseboat} + shapes: [houseboat] + - tags: {building: apartments} - shapes: [apartments] + shapes: [apartments_2_story] + + - tags: {building: "*", building:levels: "1"} + shapes: [apartments_1_story] + - tags: {building: "*", building:levels: "1", roof:shape: gabled} + shapes: [apartments_1_story_gabled_roof] + - tags: {building: "*", building:levels: "1", roof:shape: hipped} + shapes: [apartments_1_story_gabled_roof] + - tags: {building: "*", building:levels: "1", roof:shape: pyramidal} + shapes: [apartments_1_story_gabled_roof] + - tags: {building: "*", building:levels: "1", roof:shape: skillion} + shapes: [apartments_1_story_skillion_roof] + + - tags: {building: "*", building:levels: "2"} + shapes: [apartments_2_story] + - tags: {building: "*", building:levels: "2", roof:shape: gabled} + shapes: [apartments_2_story_gabled_roof] + - tags: {building: "*", building:levels: "2", roof:shape: hipped} + shapes: [apartments_2_story_gabled_roof] + - tags: {building: "*", building:levels: "2", roof:shape: pyramidal} + shapes: [apartments_2_story_gabled_roof] + - tags: {building: "*", building:levels: "2", roof:shape: skillion} + shapes: [apartments_2_story_skillion_roof] + + - tags: {building: "*", building:levels: "3"} + shapes: [apartments_3_story] + - tags: {building: "*", building:levels: "3", roof:shape: gabled} + shapes: [apartments_3_story_gabled_roof] + - tags: {building: "*", building:levels: "3", roof:shape: hipped} + shapes: [apartments_3_story_gabled_roof] + - tags: {building: "*", building:levels: "3", roof:shape: pyramidal} + shapes: [apartments_3_story_gabled_roof] + - tags: {building: "*", building:levels: "3", roof:shape: skillion} + shapes: [apartments_3_story_skillion_roof] + + - tags: {building: "*", building:levels: "4"} + shapes: [apartments_4_story] + - tags: {building: "*", building:levels: "4", roof:shape: gabled} + shapes: [apartments_4_story_gabled_roof] + - tags: {building: "*", building:levels: "4", roof:shape: hipped} + shapes: [apartments_4_story_gabled_roof] + - tags: {building: "*", building:levels: "4", roof:shape: pyramidal} + shapes: [apartments_4_story_gabled_roof] + - tags: {building: "*", building:levels: "4", roof:shape: skillion} + shapes: [apartments_4_story_skillion_roof] + + - tags: {building: "*", building:levels: "5"} + shapes: [apartments_5_story] + - tags: {building: "*", building:levels: "5", roof:shape: gabled} + shapes: [apartments_5_story_gabled_roof] + - tags: {building: "*", building:levels: "5", roof:shape: hipped} + shapes: [apartments_5_story_gabled_roof] + - tags: {building: "*", building:levels: "5", roof:shape: pyramidal} + shapes: [apartments_5_story_gabled_roof] + - tags: {building: "*", building:levels: "5", roof:shape: skillion} + shapes: [apartments_5_story_skillion_roof] + + - tags: {building: construction} + shapes: [building_construction] + - tags: {building: apartments, construction: "yes"} + shapes: [building_construction] + - tags: {building: "yes", construction: "yes"} + shapes: [building_construction] + - tags: {building: kindergarten} shapes: [toy_horse] - tags: {amenity: kindergarten} shapes: [toy_horse] - tags: {building: kindergarten, amenity: kindergarten} shapes: [toy_horse] + - tags: {leisure: indoor_playground} + shapes: [toy_horse] - tags: {building: office} shapes: [briefcase] - tags: {amenity: school} location_restrictions: {include: [jp]} shapes: [japan_elementary_school] + - tags: {office: "yes"} + shapes: [briefcase] - tags: {office: company} shapes: [briefcase] - tags: {office: government} @@ -593,28 +893,50 @@ node_icons: shapes: [telephone] - group: "Not important big objects" - start_zoom_level: 15 + start_zoom_level: 15.0 tags: + - tags: {building: garage} + shapes: [garages] + - tags: {building: garages} + shapes: [garages] + - tags: {landuse: garages} + shapes: [garages] - tags: {man_made: communications_tower} location_restrictions: {include: [jp]} shapes: [japan_tv_tower] - - tags: {man_made: tower} - shapes: [tower] + - tags: {man_made: communications_tower} + shapes: [tower_communication] - tags: {man_made: telescope} shapes: [telescope_radio] - tags: {man_made: telescope, telescope:type: radio} shapes: [telescope_radio] - tags: {man_made: telescope, telescope:type: gamma} shapes: [telescope_gamma] - - tags: {building: garages} - shapes: [garages] - - tags: {landuse: garages} - shapes: [garages] - - tags: {building: garage} - shapes: [garages] + - tags: {man_made: telescope, telescope:type: optical} + shapes: [observatory] + - tags: {man_made: tower} + shapes: [tower] + - tags: {man_made: tower, tower:construction: dish} + shapes: [telescope_radio] + - tags: {man_made: tower, tower:construction: dish, telescope:type: radio} + shapes: [telescope_radio] + - tags: {man_made: tower, tower:construction: dish, telescope:type: gamma} + shapes: [telescope_gamma] + - tags: {man_made: crane} + shapes: [crane] + - tags: {man_made: crane, crane:type: gantry_crane} + shapes: [crane_gantry] + - tags: {man_made: crane, crane:type: floor-mounted_crane} + shapes: [crane] + - tags: {man_made: crane, crane:type: portal_crane} + shapes: [crane_portal] + - tags: {man_made: crane, crane:type: travel_lift} + shapes: [crane_travel_lift] + - tags: {man_made: crane, crane:type: tower_crane} + shapes: [crane] - group: "Emergency" - start_zoom_level: 15 + start_zoom_level: 15.0 tags: - tags: {emergency: defibrillator} shapes: [{shape: defibrillator, color: emergency_color}] @@ -628,7 +950,7 @@ node_icons: shapes: [{shape: sos_phone, color: emergency_color}] - group: "Transport-important middle objects" - start_zoom_level: 16 + start_zoom_level: 16.0 tags: - tags: {ford: "yes"} shapes: [ford] @@ -648,6 +970,8 @@ node_icons: shapes: [{shape: car, offset: [0, 4]}, {shape: car, offset: [0, -3]}] - tags: {highway: turning_circle} shapes: [circle_empty] + - tags: {highway: turning_loop} + shapes: [turning_loop] - tags: {highway: crossing} shapes: [crossing] - tags: {crossing: zebra} @@ -668,11 +992,26 @@ node_icons: shapes: [traffic_signals] - tags: {crossing_ref: toucan} shapes: [toucan_crossing] + - tags: {traffic_calming: bump} shapes: [bump] + - tags: {traffic_calming: mini_bumps} + shapes: [mini_bumps] + - tags: {traffic_calming: hump} + shapes: [hump] + - tags: {traffic_calming: table} + shapes: [traffic_table] + - tags: {traffic_calming: cushion} + shapes: [traffic_cushion] + - tags: {traffic_calming: rumble_strip} + shapes: [rumble_strip] + - tags: {traffic_calming: dip} + shapes: [dip] + - tags: {traffic_calming: double_dip} + shapes: [double_dip] - group: "Important middle objects" - start_zoom_level: 16 + start_zoom_level: 16.0 tags: - tags: {tourism: attraction, attraction: amusement_ride} shapes: [amusement_ride] @@ -680,9 +1019,13 @@ node_icons: shapes: [woman_and_man] - tags: {amenity: shelter} shapes: [shelter] + - tags: {man_made: obelisk} + shapes: [obelisk] + - tags: {historic: monument} + shapes: [monument] - group: "Normal middle objects" - start_zoom_level: 17 + start_zoom_level: 17.0 tags: - tags: {shop: kiosk} shapes: [kiosk] @@ -691,18 +1034,23 @@ node_icons: - tags: {amenity: shop, shop: kiosk} shapes: [kiosk] - tags: {amenity: stage} - shapes: [theatre] + shapes: [curtains] + - tags: {amenity: hunting_stand} + shapes: [hunting_stand] - tags: {natural: cave_entrance} shapes: [cave] - tags: {amenity: bureau_de_change} - shapes: [exchange] + shapes: + - {shape: exchange} + - {shape: dollar, offset: [-4, 3]} + - {shape: pound, offset: [5, -2]} - tags: {sport: skateboard} shapes: [skateboard] - tags: {pipeline: substation} shapes: [pipeline] - group: "Towers, poles, masts" - start_zoom_level: 15 + start_zoom_level: 15.0 tags: - tags: {building: ventilation_shaft} shapes: [ventilation] @@ -714,6 +1062,8 @@ node_icons: shapes: [transformer] - tags: {power: generator, generator:source: solar} shapes: [solar_panel] + - tags: {power: heliostat} + shapes: [solar_panel] - tags: {power: generator, generator:source: wind} shapes: [wind_turbine] - tags: {power: tower} @@ -728,6 +1078,8 @@ node_icons: shapes: [power_tower_4_level] - tags: {power: tower, design: donau} shapes: [power_tower_donau] + - tags: {power: tower, design: donau_inverse} + shapes: [power_tower_donau_inverse] - tags: {power: tower, design: barrel} shapes: [power_tower_barrel] - tags: {power: tower, design: asymmetric} @@ -758,8 +1110,12 @@ node_icons: shapes: [power_tower_portal] - tags: {power: tower, design: portal_two-level} shapes: [power_tower_portal_2_level] + - tags: {power: portal, design: portal_two-level} + shapes: [power_tower_portal_2_level] - tags: {power: tower, design: portal_three-level} shapes: [power_tower_portal_3_level] + - tags: {power: portal, design: portal_three-level} + shapes: [power_tower_portal_3_level] - tags: {power: pole} shapes: [power_pole_2_level] @@ -790,10 +1146,28 @@ node_icons: shapes: [tower_cooling] - tags: {man_made: tower, tower:type: defensive} shapes: [tower_defensive] + - tags: {man_made: tower, tower:type: diving} + shapes: [diving_platform] + - tags: {man_made: tower, tower:type: diving, tower:platforms: "1"} + shapes: [diving_platform] + - tags: {man_made: tower, tower:type: diving, tower:platforms: "2"} + shapes: [diving_2_platforms] + - tags: {man_made: tower, tower:type: diving, tower:platforms: "3"} + shapes: [diving_3_platforms] + - tags: {man_made: tower, tower:type: diving, tower:platforms: "4"} + shapes: [diving_4_platforms] - tags: {man_made: tower, tower:type: pagoda} shapes: [pagoda] + - tags: {man_made: tower, tower:type: observation} + shapes: [tower_observation] + - tags: {man_made: tower, tower:type: watchtower} + shapes: [tower_observation] + - tags: {man_made: tower, tower:type: minaret} + shapes: [minaret] - tags: {man_made: mast} shapes: [tube] + - tags: {man_made: stupa} + shapes: [stupa] - tags: {man_made: mast, tower:construction: guyed_tube} shapes: [tube_guyed] @@ -801,7 +1175,7 @@ node_icons: shapes: [tube] - tags: {man_made: mast, tower:construction: lattice} shapes: [lattice] - - tags: {man_made: mast, tower:construction: lattice_guyed} + - tags: {man_made: mast, tower:construction: guyed_lattice} shapes: [lattice_guyed] - tags: {man_made: mast, tower:type: lighting} shapes: @@ -813,6 +1187,16 @@ node_icons: - tube - {shape: wave_left, offset: [-3, -3]} - {shape: wave_right, offset: [3, -3]} + - tags: {man_made: mast, tower:type: siren} + shapes: + - tube + - {shape: siren_left, offset: [-3, -3]} + - {shape: siren_right, offset: [3, -3]} + - tags: {man_made: mast, tower:type: monitoring} + shapes: + - tube + - {shape: dish_antenna_left, offset: [-3, -3]} + - {shape: dish_antenna_right, offset: [3, -3]} - tags: man_made: mast tower:type: lighting @@ -829,6 +1213,22 @@ node_icons: - tube_guyed - {shape: wave_left, offset: [-3, -3]} - {shape: wave_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: siren + tower:construction: guyed_tube + shapes: + - tube_guyed + - {shape: siren_left, offset: [-3, -3]} + - {shape: siren_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: monitoring + tower:construction: guyed_tube + shapes: + - tube_guyed + - {shape: dish_antenna_left, offset: [-3, -3]} + - {shape: dish_antenna_right, offset: [3, -3]} - tags: man_made: mast tower:type: lighting @@ -845,6 +1245,22 @@ node_icons: - tube - {shape: wave_left, offset: [-3, -3]} - {shape: wave_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: siren + tower:construction: freestanding + shapes: + - tube + - {shape: siren_left, offset: [-3, -3]} + - {shape: siren_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: monitoring + tower:construction: freestanding + shapes: + - tube + - {shape: dish_antenna_left, offset: [-3, -3]} + - {shape: dish_antenna_right, offset: [3, -3]} - tags: man_made: mast tower:type: lighting @@ -861,6 +1277,22 @@ node_icons: - lattice_guyed - {shape: wave_left, offset: [-4, -3]} - {shape: wave_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: siren + tower:construction: guyed_lattice + shapes: + - lattice_guyed + - {shape: siren_left, offset: [-4, -3]} + - {shape: siren_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: monitoring + tower:construction: guyed_lattice + shapes: + - lattice_guyed + - {shape: dish_antenna_left, offset: [-4, -3]} + - {shape: dish_antenna_right, offset: [3, -3]} - tags: man_made: mast tower:type: lighting @@ -877,6 +1309,22 @@ node_icons: - lattice - {shape: wave_left, offset: [-4, -3]} - {shape: wave_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: siren + tower:construction: lattice + shapes: + - lattice + - {shape: siren_left, offset: [-4, -3]} + - {shape: siren_right, offset: [3, -3]} + - tags: + man_made: mast + tower:type: monitoring + tower:construction: lattice + shapes: + - lattice + - {shape: dish_antenna_left, offset: [-4, -3]} + - {shape: dish_antenna_right, offset: [3, -3]} - tags: {man_made: tower, tower:construction: guyed_tube} shapes: [tube_guyed] @@ -965,7 +1413,7 @@ node_icons: add_shapes: [phone] - group: "Moon small objects" - start_zoom_level: 0 + start_zoom_level: 0.0 tags: - tags: {man_made: rover} shapes: [lunokhod] @@ -1019,7 +1467,7 @@ node_icons: set_opacity: 0.5 - group: "Important small objects" - start_zoom_level: 17 + start_zoom_level: 17.0 tags: - tags: {highway: elevator} shapes: [elevator] @@ -1033,8 +1481,14 @@ node_icons: shapes: [plaque] - tags: {historic: memorial, memorial: statue} shapes: [statue] + - tags: {barrier: artwork, artwork_type: statue} + shapes: [statue] - tags: {historic: stone} shapes: [stone_with_inscription] + - tags: {historic: wayside_cross} + shapes: [cross_and_horizontal_bar] + - tags: {historic: wayside_shrine} + shapes: [wayside_shrine] - tags: {historic: memorial, memorial: stone} shapes: [stone_with_inscription] - tags: {historic: tomb} @@ -1048,32 +1502,52 @@ node_icons: shapes: [lift_gate] - tags: {barrier: turnstile} shapes: [turnstile] - - tags: {railway: level_crossing} - shapes: [x] + - tags: {barrier: log} + shapes: [wood] + - tags: {barrier: chain} + shapes: [chain_barrier] - tags: {railway: crossing} shapes: [x] + - tags: {railway: railway_crossing} + shapes: [x] + - tags: {railway: level_crossing} + shapes: [x] + - tags: {railway: signal} + shapes: [signal] - tags: {amenity: atm} shapes: [atm] - tags: {amenity: bicycle_parking} shapes: - {shape: bicycle, offset: [0, 2]} - {shape: p_small, offset: [-6, -3]} + - tags: {amenity: bicycle_parking, bicycle_parking: stands} + shapes: + - {shape: p_small, offset: [-5, -3]} + - bicycle_parking_stand + - tags: {amenity: bicycle_parking, bicycle_parking: wall_loops} + shapes: + - {shape: p_small, offset: [-5, -3]} + - bicycle_parking_wall_loops + - tags: {amenity: bicycle_parking, bicycle_parking: rack} + shapes: + - {shape: p_small, offset: [-5, -3]} + - bicycle_parking_rack - tags: {amenity: telephone} shapes: [telephone] - tags: {information: "*"} - shapes: [information] - replace_shapes: no - - tags: {tourism: "*"} - shapes: [historic] + shapes: [i] replace_shapes: no + # tags: {tourism: "*"} + # shapes: [historic] + # replace_shapes: no - tags: {tourism: information} - shapes: [information] + shapes: [i] - tags: {information: guidepost} shapes: [guidepost] - tags: {tourism: viewpoint} shapes: [binoculars] - tags: {information: board} - shapes: [information_board] + shapes: [i_in_square] - tags: {vending: admission_tickets} shapes: [vending_tickets] - tags: {vending: candles} @@ -1102,6 +1576,8 @@ node_icons: shapes: [picture] - tags: {tourism: artwork, artwork_type: statue} shapes: [statue] + - tags: {tourism: artwork, artwork_type: stone} + shapes: [stone_with_inscription] - tags: {exhibit: artwork, artwork_type: statue} shapes: [statue_exhibit] - tags: {tourism: artwork, artwork_type: sculpture} @@ -1118,7 +1594,7 @@ node_icons: shapes: {christmas_tree} - group: "Normal small objects" - start_zoom_level: 18 + start_zoom_level: 18.0 tags: - tags: {railway: switch} shapes: [y] @@ -1126,6 +1602,10 @@ node_icons: shapes: [binoculars_on_pole] - tags: {amenity: parking_space} shapes: [p_small] + - tags: {amenity: parking, parking: lane} + shapes: [p_small] + - tags: {amenity: parking, parking: street_side} + shapes: [p_small] - tags: {amenity: post_box} shapes: [envelope] - tags: {amenity: recycling} @@ -1156,6 +1636,8 @@ node_icons: shapes: [slide] - tags: {playground: roundabout} shapes: [roundabout] + - tags: {playground: sandpit} + shapes: [sandpit] - tags: {playground: seesaw} shapes: [seesaw] - tags: {playground: horizontal_bar} @@ -1166,9 +1648,11 @@ node_icons: shapes: [golf_tee] - tags: {golf: pin} shapes: [golf_pin] + - tags: {highway: traffic_mirror} + shapes: [side_mirror] - group: "Entrances" - start_zoom_level: 18 + start_zoom_level: 18.0 tags: - tags: {amenity: parking_entrance} shapes: @@ -1192,6 +1676,8 @@ node_icons: shapes: [entrance] - tags: {entrance: "yes"} shapes: [entrance] + - tags: {building: entrance} + shapes: [{shape: entrance, color: "#FF0000"}] - tags: {entrance: shop} shapes: [entrance] - tags: {entrance: exit} @@ -1204,7 +1690,7 @@ node_icons: shapes: [no_door] - group: "Not important small objects" - start_zoom_level: 18 + start_zoom_level: 18.0 tags: - tags: {amenity: bench} shapes: [bench] @@ -1212,6 +1698,16 @@ node_icons: shapes: [bench_backrest] - tags: {amenity: bench, backrest: "no"} shapes: [bench_no_backrest] + - tags: {amenity: bench, tourism: artwork, artwork_type: sculpture} + shapes: [bench_with_statue] + - tags: {amenity: bench, tourism: artwork, artwork_type: statue} + shapes: [bench_with_statue] + - tags: {historic: memorial, memorial: bench, amenity: bench} + shapes: [bench_with_inscription] + - tags: {historic: memorial, memorial: bench} + shapes: [bench_with_inscription] + - tags: {memorial: bench} + shapes: [bench_with_inscription] - tags: {amenity: clock} shapes: [clock] - tags: {amenity: fountain} @@ -1231,9 +1727,9 @@ node_icons: - tags: {amenity: bbq} shapes: [bbq] - tags: {leisure: firepit} - shapes: [firepit] + shapes: [fire_pit] - tags: {man_made: cross} - shapes: [cross] + shapes: [latin_cross] - tags: {man_made: flagpole} shapes: [flagpole] - tags: {man_made: manhole} @@ -1248,12 +1744,18 @@ node_icons: shapes: [street_cabinet] - tags: {man_made: surveillance} shapes: [cctv] + - tags: {man_made: surveillance, camera:type: dome, camera:mount: ceiling} + shapes: [cctv_dome_ceiling] + - tags: {man_made: surveillance, camera:type: dome, camera:mount: wall} + shapes: [cctv_dome_wall] - tags: {man_made: ventilation_shaft} shapes: [ventilation] - tags: {railway: ventilation_shaft} shapes: [ventilation] - tags: {advertising: billboard} shapes: [billboard] + - tags: {advertising: column} + shapes: [advertising_column] - tags: {natural: rock} shapes: [stone] - tags: {natural: stone} @@ -1267,54 +1769,46 @@ node_icons: shapes: [buffer_stop] - tags: {traffic_sign: city_limit} shapes: [city_limit_sign] - - tags: {traffic_sign: maxspeed, maxspeed: "30"} + - tags: {traffic_sign: maxspeed, maxspeed: "^(\\d)(\\d)$"} shapes: [ circle_11, - {shape: digit_3, offset: [-2, 0], color: "#FFFFFF"}, - {shape: digit_0, offset: [2, 0], color: "#FFFFFF"}, + {shape: digit_#maxspeed0, offset: [-2, 0], color: "#FFFFFF"}, + {shape: digit_#maxspeed1, offset: [2, 0], color: "#FFFFFF"}, ] - - tags: {traffic_sign: maxspeed, maxspeed: "40"} - shapes: [ - circle_11, - {shape: digit_4, offset: [-2, 0], color: "#FFFFFF"}, - {shape: digit_0, offset: [2, 0], color: "#FFFFFF"}, - ] - - tags: {traffic_sign: maxspeed, maxspeed: "50"} - shapes: [ - circle_11, - {shape: digit_5, offset: [-2, 0], color: "#FFFFFF"}, - {shape: digit_0, offset: [2, 0], color: "#FFFFFF"}, - ] - - tags: {traffic_sign: maxspeed, maxspeed: "60"} - shapes: [ - circle_11, - {shape: digit_6, offset: [-2, 0], color: "#FFFFFF"}, - {shape: digit_0, offset: [2, 0], color: "#FFFFFF"}, - ] - - tags: {traffic_sign: maxspeed, maxspeed: "40_mph"} + - tags: {traffic_sign: maxspeed, maxspeed: "^(\\d)(\\d) mph$"} shapes: [ speed_limit_mph, - {shape: digit_4, offset: [-2, 2]}, - {shape: digit_0, offset: [2, 2]}, + {shape: digit_#maxspeed0, offset: [-2, 2]}, + {shape: digit_#maxspeed1, offset: [2, 2]}, ] + - tags: {highway: milestone} + shapes: [milestone] - tags: {traffic_sign: stop} shapes: [stop] - tags: {highway: give_way} shapes: [triangle_down_hollow] - tags: {noexit: "yes"} - shapes: [noexit] + shapes: [t] - tags: {barrier: block} shapes: [block] - tags: {barrier: rock} shapes: [stone] - tags: {barrier: bollard} shapes: [bollard] + - tags: {barrier: kerb} + shapes: [kerb] + - tags: {tank_trap: czech_hedgehog} + shapes: [czech_hedgehog] + - tags: {tank_trap: dragons_teeth} + shapes: [dragons_teeth] + - tags: {tank_trap: toblerone} + shapes: [dragons_teeth] - group: "Trees" - start_zoom_level: 18 + start_zoom_level: 18.0 tags: - tags: {natural: tree} - shapes: [{shape: tree, color: tree_color}] + shapes: [{shape: tree, color: tree_color, outline: no}] - tags: {leaf_type: broadleaved} shapes: [{shape: tree_with_leaf, color: tree_color}] - tags: {leaf_type: needleleaved} @@ -1363,7 +1857,7 @@ node_icons: add_shapes: [{shape: pear, color: tree_color}] - group: "Indoor" - start_zoom_level: 18 + start_zoom_level: 18.0 tags: - tags: {door: "yes"} shapes: [entrance] @@ -1374,13 +1868,13 @@ node_icons: tags: - tags: {support: pole} over_icon: [support_pole] - under_icon: [clock, information_board] + under_icon: [clock, i_in_square] - tags: {support: wall_mounted} over_icon: [support_wall] - under_icon: [clock, information_board] + under_icon: [clock, i_in_square] - tags: {support: column} over_icon: [support_column] - under_icon: [clock, information_board] + under_icon: [clock, i_in_square] - tags: {amenity: "*", karaoke: "yes"} add_shapes: [microphone] - tags: {building: "*", "roof:shape": onion} @@ -1405,7 +1899,7 @@ node_icons: - tags: {bicycle: "no"} shapes: - {shape: bicycle, offset: [0, 2]} - - {shape: x_small, offset: [-5, -4]} + - {shape: x_4, offset: [-5, -4]} - tags: {internet_access: wlan, "internet_access:fee": "no"} add_shapes: - {shape: wlan, offset: [0, -3]} @@ -1421,12 +1915,16 @@ node_icons: add_shapes: [lock] - tags: {direction: clockwise} add_shapes: [clockwise] - - tags: {direction: contrclockwise} - add_shapes: [contrclockwise] + - tags: {direction: anticlockwise} + add_shapes: [counterclockwise] - tags: {atm: "yes"} add_shapes: [atm] - tags: {tactile_paving: "yes"} add_shapes: [tactile_paving] + - tags: {tactile_paving: "no"} + add_shapes: + - {shape: tactile_paving, offset: [0, 2]} + - {shape: x_5, offset: [0, -3]} - tags: {"payment:credit_cards": "yes"} add_shapes: [credit_card] @@ -1443,6 +1941,44 @@ node_icons: - tags: {recycling:glass_bottles: "yes"} add_shapes: [bottle] + - tags: {recycling:paper: "yes"} + add_shapes: [sheets] + - tags: {recycling:glass: "yes"} + add_shapes: [bottle_and_wine_glass] + - tags: {recycling:clothes: "yes"} + add_shapes: [t_shirt] + - tags: {recycling:shoes: "yes"} + add_shapes: [shoe] + - tags: {recycling:green_waste: "yes"} + add_shapes: [apple] + - tags: {recycling:paper_packaging: "yes"} + add_shapes: [sheets] + - tags: {recycling:newspaper: "yes"} + add_shapes: [sheets] + - tags: {recycling:magazines: "yes"} + add_shapes: [sheets] + - tags: {recycling:books: "yes"} + add_shapes: [book] + - tags: {recycling:wood: "yes"} + add_shapes: [{shape: wood, color: trunk_color}] + - tags: {recycling:glass_bottles:colour: "yes"} + add_shapes: [{shape: bottle, color: green}] + - tags: {recycling:cartons: "yes"} + add_shapes: [aseptic_carton] + - tags: {recycling:beverage_cartons: "yes"} + add_shapes: [aseptic_carton] + - tags: {recycling:organic: "yes"} + add_shapes: [apple] + - tags: {recycling:tetrapak: "yes"} + add_shapes: [aseptic_carton] + - tags: {recycling:tyres: "yes"} + add_shapes: [tyre] + - tags: {recycling:toys: "yes"} + add_shapes: [toy_horse] + - tags: {recycling:verre: "yes"} + add_shapes: [bottle_and_wine_glass] + - tags: {recycling:bags: "yes"} + add_shapes: [bag] - tags: {crossing:island: "yes"} add_shapes: [rectangle_vertical_rounded] @@ -1451,141 +1987,159 @@ node_icons: roads: - tags: {highway: motorway} - default_width: 15 + default_width: 7.0 border_color: motorway_border_color color: motorway_color priority: 41.8 - tags: {highway: trunk} - default_width: 13 - border_color: road_border_color - priority: 41 + default_width: 7.0 + border_color: motorway_border_color + color: motorway_color + priority: 41.0 + - tags: {highway: trunk_link} + default_width: 7.0 + border_color: motorway_border_color + color: motorway_color + priority: 41.0 - tags: {highway: primary} - default_width: 11 + default_width: 7.0 border_color: primary_border_color color: primary_color priority: 41.7 - tags: {highway: motorway_link} - default_width: 9 + default_width: 7.0 border_color: motorway_border_color color: motorway_color priority: 41.8 - tags: {highway: secondary} - default_width: 9 + default_width: 7.0 border_color: secondary_border_color priority: 41.6 color: secondary_color - tags: {highway: secondary_link} - default_width: 9 + default_width: 7.0 border_color: secondary_border_color priority: 41.6 color: secondary_color - tags: {highway: tertiary} - default_width: 7 + default_width: 7.0 border_color: tertiary_border_color priority: 41.5 color: tertiary_color - tags: {highway: tertiary_link} - default_width: 7 + default_width: 7.0 border_color: tertiary_border_color priority: 41.5 color: tertiary_color - tags: {highway: unclassified} - default_width: 5 + default_width: 5.0 border_color: road_border_color - priority: 41 + priority: 41.0 - tags: {highway: residential} - default_width: 5 + default_width: 5.0 border_color: road_border_color - priority: 41 + priority: 41.0 - tags: {highway: living_street} - default_width: 4 + default_width: 4.0 border_color: road_border_color - priority: 41 + priority: 41.0 - tags: {highway: service} exception: {service: parking_aisle} - default_width: 3 + default_width: 3.0 border_color: road_border_color - priority: 41 + priority: 41.0 - tags: {highway: service, service: parking_aisle} - default_width: 2 + default_width: 2.0 border_color: road_border_color - priority: 41 + priority: 41.0 - tags: {leisure: track} color: pitch_color border_color: pitch_border_color - default_width: 5 - priority: 21 + default_width: 5.0 + priority: 21.0 + + - tags: {highway: raceway} + color: pitch_color + border_color: pitch_border_color + default_width: 7.0 + priority: 21.0 ways: - tags: {man_made: bridge} style: {fill: "#AAAAAA"} - priority: 22 + priority: 22.0 - tags: {indoor: area} style: stroke: indoor_border_color - stroke-width: 1 + stroke-width: 1.0 fill: indoor_color - priority: 10 + priority: 10.0 - tags: {indoor: corridor} style: stroke: indoor_color - stroke-width: 1 + stroke-width: 1.0 fill: indoor_color - priority: 11 + priority: 11.0 - tags: {highway: corridor} style: stroke: "#00FF00" - stroke-width: 5 - priority: 11 + stroke-width: 5.0 + priority: 11.0 - tags: {indoor: "yes", area: "yes"} style: stroke: indoor_color - stroke-width: 1 + stroke-width: 1.0 fill: indoor_color - priority: 12 + priority: 12.0 - tags: {indoor: room, area: "yes"} style: stroke: indoor_color - stroke-width: 1 + stroke-width: 1.0 fill: indoor_color - priority: 12 + priority: 12.0 - tags: {indoor: elevator, area: "yes"} style: stroke: indoor_color - stroke-width: 1 + stroke-width: 1.0 fill: indoor_color - priority: 12 + priority: 12.0 - tags: {indoor: column} style: stroke: indoor_color - stroke-width: 1 + stroke-width: 1.0 fill: indoor_color - priority: 13 + priority: 13.0 - tags: {power: line} style: stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.2 - priority: 80 + priority: 80.0 - tags: {power: cable} style: stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.1 - priority: 80 + priority: 80.0 - tags: {golf: hole} style: stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.3 - priority: 80 + priority: 80.0 - tags: {man_made: pipeline} - style: {stroke: "#888888", stroke-width: 1, stroke-dasharray: "12,1.5"} - priority: 80 + style: + stroke: "#888888" + stroke-width: 1.0 + stroke-dasharray: "12.0,1.5" + priority: 80.0 - tags: {man_made: pipeline} - style: {stroke: "#888888", stroke-width: 3, stroke-dasharray: "1,10,1,1.5"} - priority: 80 + style: + stroke: "#888888" + stroke-width: 3.0 + stroke-dasharray: "1.0,10.0,1.0,1.5" + priority: 80.0 - tags: {highway: track} style: @@ -1593,151 +2147,189 @@ ways: stroke: track_color stroke-linecap: round stroke-linejoin: round - priority: 41 + priority: 41.0 - tags: {highway: footway} - exception: {area: "yes"} + exception: {area: "yes", type: "multipolygon"} style: - stroke-width: 3 + stroke-width: 3.0 stroke: foot_border_color stroke-linecap: round stroke-linejoin: round - priority: 41 + priority: 41.0 - tags: {highway: pedestrian} exception: {area: "yes"} style: - stroke-width: 3 + stroke-width: 3.0 stroke: foot_border_color stroke-linecap: round stroke-linejoin: round - priority: 41 + priority: 41.0 - tags: {highway: cycleway} exception: {area: "yes"} style: - stroke-width: 3 + stroke-width: 3.0 stroke: foot_border_color stroke-linecap: round stroke-linejoin: round - priority: 41 + priority: 41.0 - tags: {highway: steps} style: - stroke-width: 6 + stroke-width: 6.0 stroke: foot_border_color stroke-linecap: butt - tags: {highway: path} style: - stroke-width: 3 + stroke-width: 3.0 stroke: foot_border_color - priority: 41 + priority: 41.0 - tags: {highway: footway} - exception: {area: "yes"} + exception: {area: "yes", type: "multipolygon"} style: stroke-width: 1.5 - stroke-dasharray: 7,3 + stroke-dasharray: 7.0,3.0 stroke-linecap: round stroke-linejoin: round stroke: foot_color - priority: 42 + priority: 42.0 - tags: {highway: pedestrian} exception: {area: "yes"} style: stroke-width: 1.5 - stroke-dasharray: 7,3 + stroke-dasharray: 7.0,3.0 stroke-linecap: round stroke-linejoin: round stroke: foot_color - priority: 42 + priority: 42.0 - tags: {highway: footway, area: "yes"} style: - stroke: none - fill: "#DDDDDD" + stroke: foot_area_border_color + fill: foot_area_color stroke-linecap: round stroke-linejoin: round - priority: -55 # FIXME + priority: 55.0 + - tags: {highway: footway, type: "multipolygon"} + style: + stroke: foot_area_border_color + fill: foot_area_color + stroke-linecap: round + stroke-linejoin: round + priority: 55.0 - tags: {highway: pedestrian, area: "yes"} style: stroke: none - fill: "#DDDDDD" + fill: foot_area_color stroke-linecap: round stroke-linejoin: round - priority: -55 # FIXME + priority: -55.0 # FIXME - tags: {highway: cycleway} exception: {area: "yes"} style: - stroke-width: 1 + stroke-width: 1.0 stroke: cycle_color - stroke-dasharray: 8,2 + stroke-dasharray: 8.0,2.0 stroke-linecap: butt - priority: 42 + priority: 42.0 - tags: {highway: steps, conveying: "*"} style: - stroke-width: 5 - stroke-dasharray: 1.5,2 + stroke-width: 5.0 + stroke-dasharray: 1.5,2.0 stroke-linecap: butt stroke: "#888888" - priority: 42 + priority: 42.0 - tags: {highway: steps} exception: {conveying: "*"} style: - stroke-width: 5 - stroke-dasharray: 1.5,2 + stroke-width: 5.0 + stroke-dasharray: 1.5,2.0 stroke-linecap: butt stroke: foot_color - priority: 42 + priority: 42.0 - tags: {highway: path} style: stroke-width: 1.5 - stroke-dasharray: 5,3 + stroke-dasharray: 5.0,3.0 stroke-linecap: butt stroke: foot_color - priority: 42 + priority: 42.0 + + - tags: {aeroway: runway} + style: + stroke-width: 50.0 + stroke: runway_color + priority: 22.0 + - tags: {aeroway: taxiway} + style: + stroke-width: 50.0 + stroke: taxiway_color + priority: 21.0 + - tags: {aeroway: runway} + style: + stroke-width: 2.0 + stroke: "#DDDDDD" + stroke-dasharray: 40.0,20.0 + priority: 23.0 + - tags: {aeroway: taxiway} + style: + stroke-width: 1.0 + stroke: "#CCCCCC" + priority: 23.0 + - tags: {aeroway: parking_position} + style: + stroke-width: 1.0 + stroke: "#DDCC00" + priority: 23.0 + - tags: {area:aeroway: taxiway} + style: + fill: "#CCCCCC" + priority: 20.0 - tags: {natural: wood} style: fill: wood_color - priority: 21 + priority: 21.0 - tags: {natural: wetland} style: fill: wetland_color - priority: 21 + priority: 21.0 - tags: {natural: grassland} style: fill: grass_color stroke: grass_border_color - priority: 20 + priority: 20.0 - tags: {natural: scrub} style: fill: wood_color - priority: 21 + priority: 21.0 - tags: {natural: sand} style: fill: sand_color - priority: 20 + priority: 20.0 - tags: {natural: beach} style: fill: beach_color - priority: 20 + priority: 20.0 - tags: {natural: heath} style: fill: "#DDDDDD" - priority: 20 + priority: 20.0 - tags: {natural: glacier} style: fill: "#FFFFFF" - priority: 20 + priority: 20.0 - tags: {natural: desert} style: fill: desert_color - priority: 20 + priority: 20.0 - tags: {natural: forest} style: fill: wood_color - priority: 21 + priority: 21.0 - tags: {natural: tree_row} - priority: 21 + priority: 21.0 style: stroke: wood_color - stroke-width: 5 + stroke-width: 5.0 stroke-linecap: round stroke-linejoin: round - tags: {natural: water} @@ -1745,34 +2337,40 @@ ways: style: fill: water_color # stroke: water_border_color - # stroke-width: 1 - priority: 21 + # stroke-width: 1.0 + priority: 21.0 - tags: {natural: water, intermittent: "yes"} style: fill: water_color opacity: 0.5 # stroke: water_border_color - # stroke-width: 1 - priority: 21 + # stroke-width: 1.0 + priority: 21.0 - tags: {natural: coastline} style: # fill: water_color stroke: water_border_color - stroke-width: 1 - priority: 21 + stroke-width: 1.0 + priority: 21.0 - tags: {natural: ridge} style: - stroke-width: 2 + stroke-width: 2.0 opacity: 0.3 stroke: ridge_color - priority: 21 + priority: 21.0 - tags: {natural: bare_rock} style: fill: rock_color - tags: {natural: cliff} style: - stroke-width: 1 + stroke-width: 1.0 stroke: "#BBBBBB" + - tags: {natural: cliff} + parallel_offset: -2.5 + style: + stroke-width: 5.0 + stroke: "#BBBBBB" + stroke-dasharray: "1,10" - tags: {natural: scree} style: fill: scree_color @@ -1780,48 +2378,51 @@ ways: - tags: {landuse: allotments} style: fill: allotments_color - priority: 20 + priority: 20.0 - tags: {landuse: conservation} style: fill: grass_color - priority: 20 + priority: 20.0 - tags: {landuse: construction} style: fill: construction_color - tags: {landuse: farmland} style: {fill: farmland_color, stroke: farmland_border_color} - priority: 20 + priority: 20.0 + - tags: {landuse: farmyard} + style: {fill: farmland_color, stroke: farmland_border_color} # FIXME + priority: 20.0 - tags: {landuse: farmland, crop: wheat} style: {fill: wheat_color, stroke: wheat_border_color} - priority: 20 + priority: 20.0 - tags: {landuse: farmland, crop: barley} style: {fill: barley_color, stroke: barley_border_color} - priority: 20 + priority: 20.0 - tags: {landuse: farmland, crop: rye} style: {fill: rye_color, stroke: rye_dark_color} - priority: 20 + priority: 20.0 - tags: {landuse: forest} style: fill: wood_color - priority: 20 + priority: 20.0 - tags: {landuse: garages} style: fill: parking_color - priority: 21 + priority: 21.0 - tags: {landuse: grass} style: fill: grass_color stroke: grass_border_color - priority: 20 + priority: 20.0 - tags: {landuse: orchard} style: fill: orchard_color - priority: 21 + priority: 21.0 - tags: {landuse: meadow} style: fill: meadow_color stroke: meadow_border_color - priority: 20 + priority: 20.0 # Hidden land use @@ -1829,43 +2430,49 @@ ways: style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {landuse: commercial} style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {landuse: industrial} style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {landuse: military} style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {landuse: railway} style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {landuse: residential} style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {power: substation} style: fill: hidden_color opacity: 0.05 + priority: 1.0 - tags: {amenity: ferry_terminal} style: fill: ferry_terminal_color - priority: 50 + priority: 50.0 - tags: {amenity: parking} style: fill: parking_color opacity: 0.5 - tags: {amenity: parking_space} style: - stroke: "#000000" - opacity: 0.2 + stroke: "#FFFFFF" - tags: {aeroway: landingpad} style: @@ -1876,6 +2483,14 @@ ways: fill: "#440044" opacity: 0.1 + - tags: {waterway: river} + style: + stroke: water_color + stroke-width: 2.5 + - tags: {waterway: canal} + style: + stroke: water_color + stroke-width: 2.0 - tags: {waterway: stream} style: stroke: water_color @@ -1884,71 +2499,103 @@ ways: style: fill: water_color stroke: water_border_color - stroke-width: 1 + stroke-width: 1.0 - tags: {waterway: ditch} style: fill: water_color stroke: water_color - stroke-width: 2 + stroke-width: 2.0 - tags: {railway: subway} style: stroke-width: 3.5 opacity: 0.7 stroke: "#AAAAAA" - priority: 41 + priority: 41.0 - tags: {railway: rail} style: - stroke-width: 3 + stroke-width: 3.0 stroke: "#BBBBBB" - priority: 42 + priority: 42.0 + - tags: {railway: light_rail} + style: + stroke-width: 3.0 + stroke: "#CCCCCC" + priority: 42.0 + - tags: {railway: monorail} + style: + stroke-width: 3.0 + stroke: "#CCCCCC" + priority: 42.0 + - tags: {railway: funicular} + style: + stroke-width: 3.0 + stroke: "#CCCCCC" + priority: 42.0 - tags: {railway: narrow_gauge} style: - stroke-width: 3 - stroke: "#BBBBBB" - priority: 42 + stroke-width: 3.0 + stroke: "#DDDDDD" + priority: 42.0 - tags: {railway: tram} style: - stroke-width: 3 + stroke-width: 3.0 stroke: "#BBBBBB" - priority: 42 + priority: 42.0 + - tags: {railway: rail} style: - stroke-width: 1 + stroke-width: 1.0 stroke: "#444444" - priority: 43 + priority: 43.0 + - tags: {railway: light_rail} + style: + stroke-width: 1.0 + stroke: "#444444" + priority: 43.0 + - tags: {railway: monorail} + style: + stroke-width: 1.0 + stroke: "#444444" + priority: 43.0 + - tags: {railway: funicular} + style: + stroke-width: 1.0 + stroke: "#444444" + priority: 43.0 - tags: {railway: narrow_gauge} style: - stroke-width: 1 + stroke-width: 1.0 stroke: "#444444" - priority: 43 + priority: 43.0 - tags: {railway: tram} style: - stroke-width: 1 + stroke-width: 1.0 stroke: "#444444" - priority: 43 + priority: 43.0 + - tags: {railway: platform} style: fill: platform_color - stroke-width: 1 + stroke-width: 1.0 stroke: platform_border_color - priority: 41 + priority: 41.0 - tags: {route: ferry} style: - stroke-width: 1 - stroke-dasharray: 3,3 + stroke-width: 1.0 + stroke-dasharray: 3.0,3.0 stroke-linecap: butt stroke: route_color - priority: 42 + priority: 42.0 - tags: {leisure: garden} style: fill: grass_color - priority: 21 + priority: 21.0 - tags: {leisure: park} style: - fill: grass_color + fill: park_color opacity: 0.5 - tags: {landuse: recreation_ground} style: @@ -1966,99 +2613,115 @@ ways: style: fill: pitch_color stroke: pitch_border_color - stroke-width: 1 - priority: 21 + stroke-width: 1.0 + priority: 21.0 - tags: {leisure: bleachers} style: fill: pitch_color stroke: pitch_border_color - stroke-width: 1 - priority: 21 + stroke-width: 1.0 + priority: 21.0 - tags: {leisure: track, area: "yes"} style: fill: pitch_color stroke: pitch_border_color - stroke-width: 1 - priority: 21 + stroke-width: 1.0 + priority: 21.0 - tags: {leisure: fitness_station} style: fill: pitch_color stroke: pitch_border_color - stroke-width: 1 - priority: 21 + stroke-width: 1.0 + priority: 21.0 - tags: {leisure: playground} style: fill: playground_color - opacity: 0.2 - priority: 21 + stroke: playground_border_color + priority: 21.0 - tags: {leisure: swimming_pool} style: fill: water_color stroke: water_border_color - stroke-width: 1 + stroke-width: 1.0 - tags: {barrier: hedge} style: fill: none stroke: wood_color - stroke-width: 4 - priority: 40 + stroke-width: 4.0 + priority: 40.0 - tags: {barrier: city_wall} style: fill: none stroke: "#000000" - stroke-width: 1 - opacity: 0.35 - priority: 40 + stroke-width: 2.0 + opacity: 0.5 + priority: 40.0 - tags: {barrier: wall} style: fill: none stroke: "#000000" - stroke-width: 1 + stroke-width: 1.5 + opacity: 0.4 + priority: 40.0 + - tags: {man_made: embankment} + style: + fill: none + stroke: "#000000" + stroke-width: 1.0 opacity: 0.3 - priority: 40 + priority: 40.0 + - tags: {man_made: embankment} + parallel_offset: -1.5 + style: + fill: none + stroke: "#000000" + stroke-width: 3.0 + opacity: 0.3 + stroke-dasharray: "1,7" + priority: 40.0 - tags: {barrier: fence} style: fill: none stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.25 - priority: 40 + priority: 40.0 - tags: {barrier: retaining_wall} style: fill: none stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.25 - priority: 40 + priority: 40.0 - tags: {barrier: handrail} style: fill: none stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.2 - priority: 40 + priority: 40.0 - tags: {barrier: kerb} style: fill: none stroke: "#000000" - stroke-width: 1 + stroke-width: 1.0 opacity: 0.15 - priority: 40 + priority: 40.0 - tags: {border: "*"} style: stroke: "#FF0000" stroke-width: 0.5 - stroke-dasharray: 10,20 + stroke-dasharray: 10.0,20.0 - tags: {"area:highway": "*"} - tags: {boundary: "*"} # style: # stroke: boundary_color # stroke-width: 0.3 - # stroke-dasharray: 10,5 - priority: 60 + # stroke-dasharray: 10.0,5.0 + priority: 60.0 area_tags: - tags: {aeroway: "*"} @@ -2071,7 +2734,7 @@ area_tags: - tags: {power: "compensator"} - tags: {power: "substation"} -tags_to_write: +keys_to_write: - "STIF:zone" - "alt_name" - "artist_name" @@ -2171,14 +2834,13 @@ prefix_to_write: - "name" - "old_name" - "operator" - - "ref" - "route_ref" - "species" - "taxon" - "website" - "wikipedia" -tags_to_skip: +keys_to_skip: - "FIXME" - "attribution" - "building:levels" @@ -2188,8 +2850,9 @@ tags_to_skip: - "curve_geometry" - "diameter_crown" - "fixme" - - "junction" + - "import_uuid" - "indoor" + - "junction" - "layer" - "level" - "level:ref" @@ -2210,3 +2873,6 @@ prefix_to_skip: - "source" - "razed" - "removed" + +tags_to_skip: + highway: motorway_junction diff --git a/map_machine/slippy/__init__.py b/map_machine/slippy/__init__.py new file mode 100644 index 0000000..80830f0 --- /dev/null +++ b/map_machine/slippy/__init__.py @@ -0,0 +1,3 @@ +""" +Tiles generation for slippy maps. +""" diff --git a/map_machine/server.py b/map_machine/slippy/server.py similarity index 79% rename from map_machine/server.py rename to map_machine/slippy/server.py index c5d117e..45d0cba 100644 --- a/map_machine/server.py +++ b/map_machine/slippy/server.py @@ -9,21 +9,20 @@ from typing import List, Optional, Tuple import cairosvg -from map_machine.tile import Tile +from map_machine.map_configuration import MapConfiguration +from map_machine.slippy.tile import Tile from map_machine.workspace import workspace __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -class _Handler(SimpleHTTPRequestHandler): - """ - HTTP request handler that process sloppy map tile requests. - """ +class TileServerHandler(SimpleHTTPRequestHandler): + """HTTP request handler that process sloppy map tile requests.""" cache: Path = Path("cache") update_cache: bool = False - options = None + options: Optional[argparse.Namespace] = None def __init__( self, @@ -31,6 +30,7 @@ class _Handler(SimpleHTTPRequestHandler): client_address: Tuple[str, int], server: HTTPServer, ) -> None: + # TODO: delete? super().__init__(request, client_address, server) def do_GET(self) -> None: @@ -50,7 +50,11 @@ class _Handler(SimpleHTTPRequestHandler): if self.update_cache: if not png_path.exists(): if not svg_path.exists(): - tile.draw(tile_path, self.cache, self.options) + tile.draw( + tile_path, + self.cache, + MapConfiguration(zoom_level=zoom_level), + ) with svg_path.open(encoding="utf-8") as input_file: cairosvg.svg2png( file_obj=input_file, write_to=str(png_path) @@ -66,11 +70,11 @@ class _Handler(SimpleHTTPRequestHandler): return -def ui(options: argparse.Namespace) -> None: +def run_server(options: argparse.Namespace) -> None: """Command-line interface for tile server.""" server: Optional[HTTPServer] = None try: - handler = _Handler + handler = TileServerHandler handler.cache = Path(options.cache) handler.options = options server: HTTPServer = HTTPServer(("", options.port), handler) diff --git a/map_machine/tile.py b/map_machine/slippy/tile.py similarity index 88% rename from map_machine/tile.py rename to map_machine/slippy/tile.py index 5c042db..5422179 100644 --- a/map_machine/tile.py +++ b/map_machine/slippy/tile.py @@ -15,14 +15,14 @@ import numpy as np import svgwrite from PIL import Image -from map_machine.boundary_box import BoundaryBox from map_machine.constructor import Constructor -from map_machine.flinger import Flinger -from map_machine.icon import ShapeExtractor +from map_machine.geometry.boundary_box import BoundaryBox +from map_machine.geometry.flinger import Flinger from map_machine.map_configuration import MapConfiguration from map_machine.mapper import Map -from map_machine.osm_getter import NetworkError, get_osm -from map_machine.osm_reader import OSMData +from map_machine.osm.osm_getter import NetworkError, get_osm +from map_machine.osm.osm_reader import OSMData +from map_machine.pictogram.icon import ShapeExtractor from map_machine.scheme import Scheme from map_machine.workspace import workspace @@ -55,9 +55,9 @@ class Tile: :param zoom_level: zoom level in OpenStreetMap terminology """ lat_rad: np.ndarray = np.radians(coordinates[0]) - n: float = 2.0 ** zoom_level - x: int = int((coordinates[1] + 180.0) / 360.0 * n) - y: int = int((1.0 - np.arcsinh(np.tan(lat_rad)) / np.pi) / 2.0 * n) + scale: float = 2.0**zoom_level + x: int = int((coordinates[1] + 180.0) / 360.0 * scale) + y: int = int((1.0 - np.arcsinh(np.tan(lat_rad)) / np.pi) / 2.0 * scale) return cls(x, y, zoom_level) def get_coordinates(self) -> np.ndarray: @@ -66,21 +66,23 @@ class Tile: Code from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames """ - n: float = 2.0 ** self.zoom_level - lon_deg: float = self.x / n * 360.0 - 180.0 - lat_rad: float = np.arctan(np.sinh(np.pi * (1 - 2 * self.y / n))) + scale: float = 2.0**self.zoom_level + lon_deg: float = self.x / scale * 360.0 - 180.0 + lat_rad: float = np.arctan(np.sinh(np.pi * (1 - 2 * self.y / scale))) lat_deg: np.ndarray = np.degrees(lat_rad) return np.array((lat_deg, lon_deg)) - def get_boundary_box(self) -> Tuple[np.ndarray, np.ndarray]: + def get_boundary_box(self) -> BoundaryBox: """ Get geographical boundary box of the tile: north-west and south-east points. """ - return ( - self.get_coordinates(), - Tile(self.x + 1, self.y + 1, self.zoom_level).get_coordinates(), - ) + point_1: np.ndarray = self.get_coordinates() + point_2: np.ndarray = Tile( + self.x + 1, self.y + 1, self.zoom_level + ).get_coordinates() + + return BoundaryBox(point_1[1], point_2[0], point_2[1], point_1[0]) def get_extended_boundary_box(self) -> BoundaryBox: """Same as get_boundary_box, but with extended boundaries.""" @@ -139,8 +141,8 @@ class Tile: """ try: osm_data: OSMData = self.load_osm_data(cache_path) - except NetworkError as e: - raise NetworkError(f"Map is not loaded. {e.message}") + except NetworkError as error: + raise NetworkError(f"Map is not loaded. {error.message}") self.draw_with_osm_data(osm_data, directory_name, configuration) @@ -171,7 +173,7 @@ class Tile: icon_extractor: ShapeExtractor = ShapeExtractor( workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) - scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) + scheme: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) constructor: Constructor = Constructor( osm_data, flinger, scheme, icon_extractor, configuration ) @@ -196,13 +198,11 @@ class Tile: assert zoom_level >= self.zoom_level tiles: List["Tile"] = [] - n: int = 2 ** (zoom_level - self.zoom_level) - for i in range(n): - for j in range(n): + scale: int = 2 ** (zoom_level - self.zoom_level) + for i in range(scale): + for j in range(scale): tile: Tile = Tile( - n * self.x + i, - n * self.y + j, - zoom_level, + scale * self.x + i, scale * self.y + j, zoom_level ) tiles.append(tile) return tiles @@ -210,9 +210,7 @@ class Tile: @dataclass class Tiles: - """ - Collection of tiles. - """ + """Collection of tiles.""" tiles: List[Tile] tile_1: Tile # Left top tile. @@ -278,6 +276,7 @@ class Tiles: :param configuration: drawing configuration """ osm_data: OSMData = self.load_osm_data(cache_path) + for tile in self.tiles: file_path: Path = tile.get_file_name(directory) if not file_path.exists(): @@ -286,6 +285,7 @@ class Tiles: logging.debug(f"File {file_path} already exists.") output_path: Path = file_path.with_suffix(".png") + if not output_path.exists(): with file_path.open(encoding="utf-8") as input_file: cairosvg.svg2png( @@ -305,6 +305,7 @@ class Tiles: cache_path: Path, configuration: MapConfiguration, osm_data: OSMData, + redraw: bool = False, ) -> None: """ Draw one PNG image with all tiles and split it into a set of separate @@ -314,13 +315,16 @@ class Tiles: :param cache_path: directory for temporary OSM files :param configuration: drawing configuration :param osm_data: OpenStreetMap data + :param redraw: update cache """ - if self.tiles_exist(directory): + if self.tiles_exist(directory) and not redraw: return - self.draw_image_from_osm_data(cache_path, configuration, osm_data) - + self.draw_image_from_osm_data( + cache_path, configuration, osm_data, redraw + ) input_path: Path = self.get_file_path(cache_path).with_suffix(".png") + with input_path.open("rb") as input_file: image: Image = Image.open(input_file) @@ -365,11 +369,12 @@ class Tiles: cache_path: Path, configuration: MapConfiguration, osm_data: OSMData, + redraw: bool = False, ) -> None: """Draw all tiles using OSM data.""" output_path: Path = self.get_file_path(cache_path) - if not output_path.exists(): + if not output_path.exists() or redraw: top, left = self.tile_1.get_coordinates() bottom, right = Tile( self.tile_2.x + 1, @@ -385,7 +390,7 @@ class Tiles: extractor: ShapeExtractor = ShapeExtractor( workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) - scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) + scheme: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) constructor: Constructor = Constructor( osm_data, flinger, scheme, extractor, configuration ) @@ -404,7 +409,8 @@ class Tiles: logging.debug(f"File {output_path} already exists.") png_path: Path = self.get_file_path(cache_path).with_suffix(".png") - if not png_path.exists(): + + if not png_path.exists() or redraw: with output_path.open(encoding="utf-8") as input_file: cairosvg.svg2png(file_obj=input_file, write_to=str(png_path)) logging.info(f"SVG file is rasterized to {png_path}.") @@ -447,9 +453,9 @@ def parse_zoom_level(zoom_level_specification: str) -> List[int]: result: List[int] = [] for part in parts: if "-" in part: - from_, to = part.split("-") - from_zoom_level: int = parse(from_) - to_zoom_level: int = parse(to) + start, end = part.split("-") + from_zoom_level: int = parse(start) + to_zoom_level: int = parse(end) if from_zoom_level > to_zoom_level: raise ScaleConfigurationException("Wrong range.") result += range(from_zoom_level, to_zoom_level + 1) @@ -459,7 +465,7 @@ def parse_zoom_level(zoom_level_specification: str) -> List[int]: return result -def ui(options: argparse.Namespace) -> None: +def generate_tiles(options: argparse.Namespace) -> None: """Simple user interface for tile generation.""" directory: Path = workspace.get_tile_path() @@ -486,8 +492,8 @@ def ui(options: argparse.Namespace) -> None: ) try: osm_data: OSMData = min_tile.load_osm_data(Path(options.cache)) - except NetworkError as e: - raise NetworkError(f"Map is not loaded. {e.message}") + except NetworkError as error: + raise NetworkError(f"Map is not loaded. {error.message}") for zoom_level in zoom_levels: tile: Tile = Tile.from_coordinates( @@ -498,8 +504,8 @@ def ui(options: argparse.Namespace) -> None: options, zoom_level ) tile.draw_with_osm_data(osm_data, directory, configuration) - except NetworkError as e: - logging.fatal(e.message) + except NetworkError as error: + logging.fatal(error.message) elif options.tile: zoom_level, x, y = map(int, options.tile.split("/")) tile: Tile = Tile(x, y, zoom_level) @@ -516,8 +522,8 @@ def ui(options: argparse.Namespace) -> None: min_tiles: Tiles = Tiles.from_boundary_box(boundary_box, min_zoom_level) try: osm_data: OSMData = min_tiles.load_osm_data(Path(options.cache)) - except NetworkError as e: - raise NetworkError(f"Map is not loaded. {e.message}") + except NetworkError as error: + raise NetworkError(f"Map is not loaded. {error.message}") for zoom_level in zoom_levels: if EXTEND_TO_BIGGER_TILE: diff --git a/map_machine/text.py b/map_machine/text.py index 19d02a3..91abd15 100644 --- a/map_machine/text.py +++ b/map_machine/text.py @@ -2,60 +2,51 @@ OSM address tag processing. """ from dataclasses import dataclass -from typing import Any, Dict, List, Set +from typing import Any, Dict, List, Optional, Set from colour import Color +from map_machine.map_configuration import LabelMode +from map_machine.osm.osm_reader import Tags +from map_machine.scheme import Scheme + __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" DEFAULT_FONT_SIZE: float = 10.0 -DEFAULT_COLOR: Color = Color("#444444") @dataclass class Label: - """ - Text label. - """ + """Text label.""" text: str - fill: Color = DEFAULT_COLOR + fill: Color + out_fill: Color size: float = DEFAULT_FONT_SIZE def get_address( - tags: Dict[str, Any], draw_captions_mode: str, processed: Set[str] + tags: Dict[str, Any], processed: Set[str], label_mode: LabelMode ) -> List[str]: """ Construct address text list from the tags. :param tags: OSM node, way or relation tags - :param draw_captions_mode: captions mode ("all", "main", or "no") :param processed: set of processed tag keys + :param label_mode: captions mode """ address: List[str] = [] - if draw_captions_mode == "address": - if "addr:postcode" in tags: - address.append(tags["addr:postcode"]) - processed.add("addr:postcode") - if "addr:country" in tags: - address.append(tags["addr:country"]) - processed.add("addr:country") - if "addr:city" in tags: - address.append(tags["addr:city"]) - processed.add("addr:city") - if "addr:street" in tags: - street = tags["addr:street"] - if street.startswith("улица "): - street = "ул. " + street[len("улица ") :] - address.append(street) - processed.add("addr:street") + tag_names: List[str] = ["housenumber"] + if label_mode == LabelMode.ADDRESS: + tag_names += ["postcode", "country", "city", "street"] - if "addr:housenumber" in tags: - address.append(tags["addr:housenumber"]) - processed.add("addr:housenumber") + for tag_name in tag_names: + key: str = f"addr:{tag_name}" + if key in tags: + address.append(tags[key]) + processed.add(key) return address @@ -80,31 +71,141 @@ def format_frequency(value: str) -> str: return f"{value} " -def get_text(tags: Dict[str, Any], processed: Set[str]) -> List[Label]: - """Get text representation of writable tags.""" - texts: List[Label] = [] - values: List[str] = [] - - if "voltage:primary" in tags: - values.append(tags["voltage:primary"]) - processed.add("voltage:primary") - - if "voltage:secondary" in tags: - values.append(tags["voltage:secondary"]) - processed.add("voltage:secondary") - - if "voltage" in tags: - values = tags["voltage"].split(";") - processed.add("voltage") - - if values: - texts.append(Label(", ".join(map(format_voltage, values)))) - - if "frequency" in tags: - text: str = ", ".join( - map(format_frequency, tags["frequency"].split(";")) +@dataclass +class TextConstructor: + def __init__(self, scheme: Scheme) -> None: + self.scheme: Scheme = scheme + self.default_color: Color = self.scheme.get_color("text_color") + self.main_color: Color = self.scheme.get_color("text_main_color") + self.default_out_color: Color = self.scheme.get_color( + "text_outline_color" ) - texts.append(Label(text)) - processed.add("frequency") - return texts + def label(self, text: str, size: float = DEFAULT_FONT_SIZE): + return Label( + text, self.default_color, self.default_out_color, size=size + ) + + def get_text( + self, tags: Dict[str, Any], processed: Set[str] + ) -> List[Label]: + """Get text representation of writable tags.""" + texts: List[Label] = [] + values: List[str] = [] + + if "voltage:primary" in tags: + values.append(tags["voltage:primary"]) + processed.add("voltage:primary") + + if "voltage:secondary" in tags: + values.append(tags["voltage:secondary"]) + processed.add("voltage:secondary") + + if "voltage" in tags: + values = tags["voltage"].split(";") + processed.add("voltage") + + if values: + texts.append(self.label(", ".join(map(format_voltage, values)))) + + if "frequency" in tags: + text: str = ", ".join( + map(format_frequency, tags["frequency"].split(";")) + ) + texts.append(self.label(text)) + processed.add("frequency") + + return texts + + def construct_text( + self, + tags: Tags, + processed: Set[str], + label_mode: LabelMode, + ) -> List[Label]: + """Construct list of labels from OSM tags.""" + + texts: List[Label] = [] + + name: Optional[str] = None + alternative_name: Optional[str] = None + + if "name" in tags: + name = tags["name"] + processed.add("name") + elif "name:en" in tags: + if not name: + name = tags["name:en"] + processed.add("name:en") + processed.add("name:en") + if "alt_name" in tags: + if alternative_name: + alternative_name += ", " + else: + alternative_name = "" + alternative_name += tags["alt_name"] + processed.add("alt_name") + if "old_name" in tags: + if alternative_name: + alternative_name += ", " + else: + alternative_name = "" + alternative_name += "ex " + tags["old_name"] + + address: List[str] = get_address(tags, processed, label_mode) + + if name: + texts.append(Label(name, self.main_color, self.default_out_color)) + if alternative_name: + texts.append(self.label(f"({alternative_name})")) + if address: + texts.append(self.label(", ".join(address))) + + if label_mode == LabelMode.MAIN: + return texts + + texts += self.get_text(tags, processed) + + if "route_ref" in tags: + texts.append(self.label(tags["route_ref"].replace(";", " "))) + processed.add("route_ref") + + if "cladr:code" in tags: + texts.append(self.label(tags["cladr:code"], size=7.0)) + processed.add("cladr:code") + + if "website" in tags: + link = tags["website"] + if link[:7] == "http://": + link = link[7:] + if link[:8] == "https://": + link = link[8:] + if link[:4] == "www.": + link = link[4:] + if link[-1] == "/": + link = link[:-1] + link = link[:25] + ("..." if len(tags["website"]) > 25 else "") + texts.append(Label(link, Color("#000088"), self.default_out_color)) + processed.add("website") + + for key in ["phone"]: + if key in tags: + texts.append( + Label(tags[key], Color("#444444"), self.default_out_color) + ) + processed.add(key) + + if "height" in tags: + texts.append(self.label(f"↕ {tags['height']} m")) + processed.add("height") + + for tag in tags: + if self.scheme.is_writable(tag, tags[tag]) and tag not in processed: + texts.append( + Label( + tags[tag], + self.default_color, + self.default_out_color, + ) + ) + return texts diff --git a/map_machine/ui/__init__.py b/map_machine/ui/__init__.py new file mode 100644 index 0000000..ef42fdf --- /dev/null +++ b/map_machine/ui/__init__.py @@ -0,0 +1,3 @@ +""" +User interface. +""" diff --git a/map_machine/ui.py b/map_machine/ui/cli.py similarity index 69% rename from map_machine/ui.py rename to map_machine/ui/cli.py index c885989..b785808 100644 --- a/map_machine/ui.py +++ b/map_machine/ui/cli.py @@ -7,7 +7,7 @@ from typing import Dict, List from map_machine import __version__ from map_machine.map_configuration import BuildingMode, DrawingMode, LabelMode -from map_machine.osm_reader import STAGES_OF_DECAY +from map_machine.osm.osm_reader import STAGES_OF_DECAY __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -15,7 +15,7 @@ __email__ = "me@enzet.ru" BOXES: str = " ▏▎▍▌▋▊▉" BOXES_LENGTH: int = len(BOXES) -COMMANDS: Dict[str, List[str]] = { +COMMAND_LINES: Dict[str, List[str]] = { "render": ["render", "-b", "10.000,20.000,10.001,20.001"], "render_with_tooltips": [ "render", @@ -28,6 +28,26 @@ COMMANDS: Dict[str, List[str]] = { "element": ["element", "--node", "amenity=bench,material=wood"], "tile": ["tile", "--coordinates", "50.000,40.000"], } +COMMANDS: List[str] = [ + "render", + "server", + "tile", + "element", + "mapcss", + "icons", + "taginfo", +] + +BOUNDARY_BOX_WARNING: str = ( + "if the first value is negative, use `=` sign or enclose the value with " + "quotes and use space before `-`, e.g. `-b=-84.752,39.504,-84.749,39.508` " + 'or `-b " -84.752,39.504,-84.749,39.508"`' +) +COORDINATES_WARNING: str = ( + "if the first value is negative, use `=` sign or enclose the value with " + "quotes and use space before `-`, e.g. `-c=-84.752,39.504` or `-c " + '" -84.752,39.504"`' +) def parse_arguments(args: List[str]) -> argparse.Namespace: @@ -43,28 +63,62 @@ def parse_arguments(args: List[str]) -> argparse.Namespace: ) subparser = parser.add_subparsers(dest="command") - render_parser = subparser.add_parser("render", help="draw SVG map") + render_parser = subparser.add_parser( + "render", + description="Render SVG map. Use --boundary-box to specify geo " + "boundaries, --input to specify OSM XML or JSON input file, or " + "--coordinates and --size to specify central point and resulting image " + "size.", + help="draw SVG map", + ) add_render_arguments(render_parser) add_map_arguments(render_parser) tile_parser = subparser.add_parser( - "tile", help="generate PNG tiles for slippy maps" + "tile", + description="Generate SVG and PNG 256 × 256 px tiles for slippy maps. " + "You can use server command to run server in order to display " + "generated tiles as a map (e.g. with Leaflet).", + help="generate SVG and PNG tiles for slippy maps", ) add_tile_arguments(tile_parser) add_map_arguments(tile_parser) - add_server_arguments(subparser.add_parser("server", help="run tile server")) + add_server_arguments( + subparser.add_parser( + "server", + description="Run in order to display generated tiles as a map " + "(e.g. with Leaflet).", + help="run tile server", + ) + ) add_element_arguments( subparser.add_parser( - "element", help="draw OSM element: node, way, relation" + "element", + description="Draw map element separately.", + help="draw OSM element: node, way, relation", ) ) add_mapcss_arguments( - subparser.add_parser("mapcss", help="write MapCSS file") + subparser.add_parser( + "mapcss", + description="Write directory with MapCSS file and generated " + "Röntgen icons.", + help="write MapCSS file", + ) ) - subparser.add_parser("icons", help="draw icons") - subparser.add_parser("taginfo", help="write Taginfo JSON file") + subparser.add_parser( + "icons", + description="Generate Röntgen icons as a grid and as separate SVG " + "icons", + help="draw Röntgen icons", + ) + subparser.add_parser( + "taginfo", + description="Generate JSON file for Taginfo project.", + help="write Taginfo JSON file", + ) arguments: argparse.Namespace = parser.parse_args(args[1:]) @@ -77,16 +131,17 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None: "--buildings", metavar="", default="flat", - choices=(x.value for x in BuildingMode), + choices=(mode.value for mode in BuildingMode), help="building drawing mode: " - + ", ".join(x.value for x in BuildingMode), + + ", ".join(mode.value for mode in BuildingMode), ) parser.add_argument( "--mode", default="normal", metavar="", - choices=(x.value for x in DrawingMode), - help="map drawing mode: " + ", ".join(x.value for x in DrawingMode), + choices=(mode.value for mode in DrawingMode), + help="map drawing mode: " + + ", ".join(mode.value for mode in DrawingMode), ) parser.add_argument( "--overlap", @@ -101,8 +156,9 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None: dest="label_mode", default="main", metavar="", - choices=(x.value for x in LabelMode), - help="label drawing mode: " + ", ".join(x.value for x in LabelMode), + choices=(mode.value for mode in LabelMode), + help="label drawing mode: " + + ", ".join(mode.value for mode in LabelMode), ) parser.add_argument( "--level", @@ -152,9 +208,27 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None: default=True, ) parser.add_argument( - "--no-roofs", - dest="roofs", - help="don't draw building roofs", + "--building-colors", + help="paint walls (if isometric mode is enabled) and roofs with " + "specified colors", + action="store_true", + default=False, + ) + parser.add_argument( + "--no-building-colors", + help="don't paint walls (if isometric mode is enabled) and roofs with " + "specified colors", + action="store_false", + ) + parser.add_argument( + "--show-overlapped", + help="show hidden nodes with a dot", + action="store_true", + default=False, + ) + parser.add_argument( + "--no-show-overlapped", + help="don't show hidden nodes with a dot", action="store_false", ) @@ -165,7 +239,8 @@ def add_tile_arguments(parser: argparse.ArgumentParser) -> None: "-c", "--coordinates", metavar=",", - help="coordinates of any location inside the tile", + help="coordinates of any location inside the tile; " + + COORDINATES_WARNING, ) parser.add_argument( "-t", @@ -182,8 +257,8 @@ def add_tile_arguments(parser: argparse.ArgumentParser) -> None: parser.add_argument( "-b", "--boundary-box", - help="construct the minimum amount of tiles that cover requested " - "boundary box", + help="construct the minimum amount of tiles that cover the requested " + "boundary box; " + BOUNDARY_BOX_WARNING, metavar=",,,", ) parser.add_argument( @@ -200,7 +275,7 @@ def add_tile_arguments(parser: argparse.ArgumentParser) -> None: "--input", dest="input_file_name", metavar="", - help="input OSM XML file name (if not specified, file will be " + help="input OSM XML file name (if not specified, the file will be " "downloaded using OpenStreetMap API)", ) @@ -252,8 +327,7 @@ def add_render_arguments(parser: argparse.ArgumentParser) -> None: "-b", "--boundary-box", metavar=",,,", - help="geo boundary box; if first value is negative, enclose the value " - "with quotes and use space before `-`", + help="geo boundary box; " + BOUNDARY_BOX_WARNING, ) parser.add_argument( "--cache", @@ -267,13 +341,14 @@ def add_render_arguments(parser: argparse.ArgumentParser) -> None: type=float, metavar="", help="OSM zoom level", - default=18, + default=18.0, ) parser.add_argument( "-c", "--coordinates", metavar=",", - help="coordinates of any location inside the tile", + help="coordinates of any location inside the tile; " + + COORDINATES_WARNING, ) parser.add_argument( "-s", @@ -338,9 +413,6 @@ def progress_bar( subsequently) :param text: short description """ - if number == 0: - sys.stdout.write(text + "...\n") - return if number == -1: sys.stdout.write(f"100 % {length * '█'}▏{text}\n") elif number % step == 0: @@ -349,6 +421,7 @@ def progress_bar( fill_length: int = int(parts / BOXES_LENGTH) box: str = BOXES[int(parts - fill_length * BOXES_LENGTH)] sys.stdout.write( - f"{str(int(int(ratio * 1000) / 10)):>3} % {fill_length * '█'}{box}" + f"{str(int(int(ratio * 1000.0) / 10.0)):>3} % " + f"{fill_length * '█'}{box}" f"{int(length - fill_length - 1) * ' '}▏{text}\n\033[F" ) diff --git a/map_machine/ui/completion.py b/map_machine/ui/completion.py new file mode 100644 index 0000000..2260ec5 --- /dev/null +++ b/map_machine/ui/completion.py @@ -0,0 +1,90 @@ +""" +Creating fish shell autocompletion commands. + +See https://fishshell.com/docs/current/completions.html +""" +import argparse +from pathlib import Path +from typing import Any + +from map_machine.ui import cli +from map_machine.ui.cli import COMMANDS + + +class ArgumentParser(argparse.ArgumentParser): + """Argument parser that generates fish shell autocompletion commands.""" + + def __init__(self, *args, **kwargs) -> None: + self.arguments: list[dict[str, Any]] = [] + super().__init__(*args, **kwargs) + + def add_argument(self, *args, **kwargs) -> None: + """Just store argument with options.""" + super().add_argument(*args, **kwargs) + argument: dict[str, Any] = {"arguments": args} + + for key, value in kwargs.items(): + argument[key] = value + + self.arguments.append(argument) + + def get_complete(self, command: str) -> str: + """Return fish complete command.""" + result: str = "" + + for argument in self.arguments: + result += "complete -c map-machine" + result += f' -n "__fish_seen_subcommand_from {command}"' + if len(argument["arguments"]) == 2: + result += f" -s {argument['arguments'][0][1:]}" + result += f" -l {argument['arguments'][1][2:]}" + else: + result += f" -l {argument['arguments'][0][2:]}" + if "help" in argument: + result += f' -d "{argument["help"]}"' + result += "\n" + + return result + + +def completion_commands() -> str: + """Print fish completion commands.""" + commands: str = " ".join(COMMANDS) + result: str = "" + result += f"set -l commands {commands}\n" + result += "complete -c map-machine -f\n" + result += ( + f'complete -c map-machine -n "not __fish_seen_subcommand_from ' + f'$commands" -a "{commands}"\n' + ) + for command in COMMANDS: + if command in ["icons", "taginfo"]: + continue + parser: ArgumentParser = ArgumentParser() + if command == "render": + cli.add_render_arguments(parser) + cli.add_map_arguments(parser) + elif command == "server": + cli.add_server_arguments(parser) + elif command == "tile": + cli.add_tile_arguments(parser) + cli.add_map_arguments(parser) + elif command == "element": + cli.add_element_arguments(parser) + elif command == "mapcss": + cli.add_mapcss_arguments(parser) + else: + raise NotImplementedError( + f"no separate function for parser creation for {command}" + ) + result += parser.get_complete(command) + "\n" + + return result + + +if __name__ == "__main__": + completions_path: Path = ( + Path.home() / ".config/fish/completions/map-machine.fish" + ) + with completions_path.open("w+") as output_file: + output_file.write(completion_commands()) diff --git a/map_machine/util.py b/map_machine/util.py index d548e17..e92efd7 100644 --- a/map_machine/util.py +++ b/map_machine/util.py @@ -10,9 +10,7 @@ __email__ = "me@enzet.ru" @dataclass class MinMax: - """ - Minimum and maximum. - """ + """Minimum and maximum.""" min_: Any = None max_: Any = None @@ -28,7 +26,7 @@ class MinMax: def center(self) -> Any: """Get middle point between minimum and maximum.""" - return (self.min_ + self.max_) / 2 + return (self.min_ + self.max_) / 2.0 def is_empty(self) -> bool: """Check if interval is empty.""" diff --git a/map_machine/workspace.py b/map_machine/workspace.py index 6377747..d76d337 100644 --- a/map_machine/workspace.py +++ b/map_machine/workspace.py @@ -17,25 +17,25 @@ def check_and_create(directory: Path) -> Path: class Workspace: - """ - Project file and directory paths and generated files and directories. - """ + """Project file and directory paths and generated files and directories.""" # Project directories and files, that are the part of the repository. - SCHEME_PATH: Path = HERE / Path("scheme") + SCHEME_PATH: Path = HERE / "scheme" DEFAULT_SCHEME_PATH: Path = SCHEME_PATH / "default.yml" - ICONS_PATH: Path = HERE / Path("icons/icons.svg") - ICONS_CONFIG_PATH: Path = HERE / Path("icons/config.json") - GITHUB_TEST_PATH: Path = Path(".github/workflows/test.yml") + ICONS_PATH: Path = HERE / "icons" / "icons.svg" + ICONS_CONFIG_PATH: Path = HERE / "icons" / "config.json" + ICONS_LICENSE_PATH: Path = HERE / "icons" / "LICENSE" + + DOCUMENTATION_PATH: Path = Path("doc") + GRID_PATH: Path = DOCUMENTATION_PATH / "grid.svg" # Generated directories and files. MAPCSS_ICONS_DIRECTORY_NAME: str = "icons" def __init__(self, output_path: Path) -> None: - self.output_path: Path = output_path - check_and_create(output_path) + self.output_path: Path = check_and_create(output_path) self._icons_by_id_path: Path = output_path / "icons_by_id" self._icons_by_name_path: Path = output_path / "icons_by_name" diff --git a/requirements.txt b/requirements.txt index 8d891b4..8f14138 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ colour>=0.1.5 numpy>=1.18.1 Pillow>=8.2.0 portolan>=1.0.1 -pycairo +pycairo>=1.20.1 pytest>=6.2.2 PyYAML>=4.2b1 setuptools>=51.0.0 diff --git a/setup.py b/setup.py index f9877ff..ce7cc06 100644 --- a/setup.py +++ b/setup.py @@ -14,13 +14,21 @@ from map_machine import ( REQUIREMENTS, ) -with Path("README.md").open() as input_file: +with Path("README.md").open(encoding="utf-8") as input_file: long_description: str = input_file.read() setup( name="map-machine", version=__version__, - packages=["map_machine"], + packages=[ + "map_machine", + "map_machine.feature", + "map_machine.geometry", + "map_machine.osm", + "map_machine.pictogram", + "map_machine.slippy", + "map_machine.ui", + ], url=__url__, project_urls={ "Bug Tracker": f"{__url__}/issues", diff --git a/tests/__init__.py b/tests/__init__.py index eccd2af..24516e9 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,7 +3,7 @@ Tests for Map Machine project. """ from pathlib import Path -from map_machine.icon import ShapeExtractor +from map_machine.pictogram.icon import ShapeExtractor from map_machine.scheme import Scheme from map_machine.workspace import Workspace @@ -12,7 +12,7 @@ __email__ = "me@enzet.ru" workspace: Workspace = Workspace(Path("temp")) -SCHEME: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH) +SCHEME: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) SHAPE_EXTRACTOR: ShapeExtractor = ShapeExtractor( workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH ) diff --git a/tests/data/tag_tests.json b/tests/data/tag_tests.json new file mode 100644 index 0000000..10b6ea0 --- /dev/null +++ b/tests/data/tag_tests.json @@ -0,0 +1,10 @@ +[ + {"tags": {"addr:housenumber": "5"}, "labels": ["5"]}, + { + "tags": {"amenity": "cafe", "name": "Nero"}, + "icons": [["coffee_cup"]], "labels": ["Nero"] + }, + {"tags": {"building": "yes"}, "icons": [["building"]]}, + {"tags": {"natural": "tree"}, "icons": [["tree"]]}, + {"tags": {"man_made": "surveillance"}, "icons": [["cctv"]]} +] diff --git a/tests/test_boundary_box.py b/tests/test_boundary_box.py index 7df256e..2e710a4 100644 --- a/tests/test_boundary_box.py +++ b/tests/test_boundary_box.py @@ -1,7 +1,7 @@ """ Test boundary box. """ -from map_machine.boundary_box import BoundaryBox +from map_machine.geometry.boundary_box import BoundaryBox __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -25,3 +25,23 @@ def test_round_coordinates() -> None: ).round() assert box.get_format() == "10.067,46.093,10.070,46.096" + + +def test_boundary_box_parsing() -> None: + """Test parsing boundary box from text.""" + assert BoundaryBox.from_text("-0.1,-0.1,0.1,0.1") == BoundaryBox( + -0.1, -0.1, 0.1, 0.1 + ) + + # Negative horizontal boundary. + assert BoundaryBox.from_text("0.1,-0.1,-0.1,0.1") is None + + # Negative vertical boundary. + assert BoundaryBox.from_text("-0.1,0.1,0.1,-0.1") is None + + # Wrong format. + assert BoundaryBox.from_text("wrong") is None + assert BoundaryBox.from_text("-O.1,-0.1,0.1,0.1") is None + + # Too big boundary box. + assert BoundaryBox.from_text("-20,-20,20,20") is None diff --git a/tests/test_command_line.py b/tests/test_command_line.py index 5fa2e31..5298e1b 100644 --- a/tests/test_command_line.py +++ b/tests/test_command_line.py @@ -12,23 +12,32 @@ from typing import List from xml.etree import ElementTree from xml.etree.ElementTree import Element -from map_machine.ui import COMMANDS +from map_machine.ui.cli import COMMAND_LINES + +LOG: bytes = ( + b"INFO Constructing ways...\n" + b"INFO Constructing nodes...\n" + b"INFO Drawing ways...\n" + b"INFO Drawing main icons...\n" + b"INFO Drawing extra icons...\n" + b"INFO Drawing texts...\n" +) def error_run(arguments: List[str], message: bytes) -> None: """Run command that should fail and check error message.""" - p = Popen(["map-machine"] + arguments, stderr=PIPE) - _, error = p.communicate() - assert p.returncode != 0 - assert error == message + with Popen(["map-machine"] + arguments, stderr=PIPE) as pipe: + _, error = pipe.communicate() + assert pipe.returncode != 0 + assert error == message def run(arguments: List[str], message: bytes) -> None: """Run command that should fail and check error message.""" - p = Popen(["map-machine"] + arguments, stderr=PIPE) - _, error = p.communicate() - assert p.returncode == 0 - assert error == message + with Popen(["map-machine"] + arguments, stderr=PIPE) as pipe: + _, error = pipe.communicate() + assert pipe.returncode == 0 + assert error == message def test_wrong_render_arguments() -> None: @@ -43,15 +52,15 @@ def test_wrong_render_arguments() -> None: def test_render() -> None: """Test `render` command.""" run( - COMMANDS["render"] + ["--cache", "tests/data"], - b"INFO Writing output SVG to out/map.svg...\n", + COMMAND_LINES["render"] + ["--cache", "tests/data"], + LOG + b"INFO Writing output SVG to out/map.svg...\n", ) - with Path("out/map.svg").open() as output_file: + with Path("out/map.svg").open(encoding="utf-8") as output_file: root: Element = ElementTree.parse(output_file).getroot() # 4 expected elements: `defs`, `rect` (background), `g` (outline), - # `g` (icon). - assert len(root) == 4 + # `g` (icon), 4 `text` elements (credits). + assert len(root) == 8 assert len(root[3][0]) == 0 assert root.get("width") == "186.0" assert root.get("height") == "198.0" @@ -60,15 +69,15 @@ def test_render() -> None: def test_render_with_tooltips() -> None: """Test `render` command.""" run( - COMMANDS["render_with_tooltips"] + ["--cache", "tests/data"], - b"INFO Writing output SVG to out/map.svg...\n", + COMMAND_LINES["render_with_tooltips"] + ["--cache", "tests/data"], + LOG + b"INFO Writing output SVG to out/map.svg...\n", ) - with Path("out/map.svg").open() as output_file: + with Path("out/map.svg").open(encoding="utf-8") as output_file: root: Element = ElementTree.parse(output_file).getroot() # 4 expected elements: `defs`, `rect` (background), `g` (outline), - # `g` (icon). - assert len(root) == 4 + # `g` (icon), 4 `text` elements (credits). + assert len(root) == 8 assert len(root[3][0]) == 1 assert root[3][0][0].text == "natural: tree" assert root.get("width") == "186.0" @@ -78,9 +87,10 @@ def test_render_with_tooltips() -> None: def test_icons() -> None: """Test `icons` command.""" run( - COMMANDS["icons"], + COMMAND_LINES["icons"], + b"INFO Icons are written to out/icons_by_name and out/icons_by_id.\n" b"INFO Icon grid is written to out/icon_grid.svg.\n" - b"INFO Icons are written to out/icons_by_name and out/icons_by_id.\n", + b"INFO Icon grid is written to doc/grid.svg.\n", ) assert (Path("out") / "icon_grid.svg").is_file() @@ -93,30 +103,32 @@ def test_icons() -> None: def test_mapcss() -> None: """Test `mapcss` command.""" run( - COMMANDS["mapcss"], + COMMAND_LINES["mapcss"], b"INFO MapCSS 0.2 scheme is written to out/map_machine_mapcss.\n", ) + out_path: Path = Path("out") / "map_machine_mapcss" - assert (Path("out") / "map_machine_mapcss").is_dir() - assert (Path("out") / "map_machine_mapcss" / "icons").is_dir() - assert ( - Path("out") / "map_machine_mapcss" / "icons" / "apple.svg" - ).is_file() - assert (Path("out") / "map_machine_mapcss" / "map_machine.mapcss").is_file() + assert out_path.is_dir() + assert out_path.is_dir() + assert (out_path / "icons" / "apple.svg").is_file() + assert (out_path / "map_machine.mapcss").is_file() + assert (out_path / "icons" / "LICENSE").is_file() def test_element() -> None: """Test `element` command.""" - run(COMMANDS["element"], b"INFO Element is written to out/element.svg.\n") - + run( + COMMAND_LINES["element"], + b"INFO Element is written to out/element.svg.\n", + ) assert (Path("out") / "element.svg").is_file() def test_tile() -> None: """Test `tile` command.""" run( - COMMANDS["tile"] + ["--cache", "tests/data"], - b"INFO Tile is drawn to out/tiles/tile_18_160199_88904.svg.\n" + COMMAND_LINES["tile"] + ["--cache", "tests/data"], + LOG + b"INFO Tile is drawn to out/tiles/tile_18_160199_88904.svg.\n" b"INFO SVG file is rasterized to out/tiles/tile_18_160199_88904.png.\n", ) diff --git a/tests/test_completion.py b/tests/test_completion.py new file mode 100644 index 0000000..513adc3 --- /dev/null +++ b/tests/test_completion.py @@ -0,0 +1,10 @@ +""" +Test Fish shell completion. +""" +from map_machine.ui.completion import completion_commands + + +def test_completion() -> None: + """Test Fish shell completion generation.""" + commands: str = completion_commands() + assert commands.startswith("set -l") diff --git a/tests/test_direction.py b/tests/test_direction.py index 410864d..274b12b 100644 --- a/tests/test_direction.py +++ b/tests/test_direction.py @@ -3,7 +3,7 @@ Test direction processing. """ import numpy as np -from map_machine.direction import DirectionSet, parse_vector +from map_machine.feature.direction import DirectionSet, parse_vector, Sector __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -41,3 +41,14 @@ def test_main_direction() -> None: assert DirectionSet("70").is_right() is True assert DirectionSet("270").is_right() is False assert DirectionSet("180").is_right() is None + + +def test_sector_parsing() -> None: + """Test constructing sector from the string representation.""" + Sector("0", angle=0) + Sector("90", angle=0) + Sector("-90", angle=0) + + sector: Sector = Sector("0-180") + assert np.allclose(sector.start, [0, -1]) + assert np.allclose(sector.end, [0, 1]) diff --git a/tests/test_elements.py b/tests/test_elements.py new file mode 100644 index 0000000..d80a7ba --- /dev/null +++ b/tests/test_elements.py @@ -0,0 +1,225 @@ +""" +Draw test nodes, ways, and relations. +""" +import logging +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import numpy as np +from svgwrite import Drawing + +from map_machine.geometry.boundary_box import BoundaryBox +from map_machine.constructor import Constructor +from map_machine.geometry.flinger import Flinger +from map_machine.pictogram.icon import ShapeExtractor +from map_machine.map_configuration import MapConfiguration +from map_machine.mapper import Map +from map_machine.osm.osm_reader import OSMData, OSMNode, OSMWay +from map_machine.scheme import Scheme +from map_machine.workspace import Workspace + +workspace: Workspace = Workspace(Path("temp")) + +SCHEME: Scheme = Scheme.from_file(workspace.DEFAULT_SCHEME_PATH) +SHAPE_EXTRACTOR: ShapeExtractor = ShapeExtractor( + workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH +) +DEFAULT_ZOOM: float = 18.0 + + +HIGHWAY_VALUES: List[str] = [ + "motorway", + "trunk", + "primary", + "secondary", + "tertiary", + "unclassified", + "residential", + "service", + "service_minor", + "road", + "pedestrian", + "living_street", + "bridleway", + "cycleway", + "footway", + "steps", + "path", + "track", + "raceway", +] + +AEROWAY_VALUES: List[str] = [ + "runway", + "taxiway", +] + +RAILWAY_TAGS: List[Dict[str, str]] = [ + {"railway": "rail"}, + {"railway": "light_rail"}, + {"railway": "monorail"}, + {"railway": "funicular"}, + {"railway": "narrow_gauge"}, + {"railway": "subway"}, + {"railway": "subway", "color": "red"}, + {"railway": "subway", "color": "blue"}, +] + +ROAD_WIDTHS_AND_FEATURES: List[Dict[str, str]] = [ + {"width": "4"}, + {"width": "8"}, + {"width": "12"}, + {"width": "16"}, + {"bridge": "yes", "width": "4"}, + {"bridge": "yes", "width": "8"}, + {"tunnel": "yes", "width": "4"}, + {"tunnel": "yes", "width": "8"}, + {"ford": "yes", "width": "4"}, + {"ford": "yes", "width": "8"}, + {"embankment": "yes", "width": "4"}, + {"embankment": "yes", "width": "8"}, +] + + +ROAD_LANES_AND_FEATURES: List[Dict[str, str]] = [ + {"lanes": "1"}, + {"lanes": "2"}, + {"lanes": "3"}, + {"lanes": "4"}, + {"bridge": "yes", "lanes": "1"}, + {"bridge": "yes", "lanes": "2"}, + {"tunnel": "yes", "lanes": "1"}, + {"tunnel": "yes", "lanes": "2"}, + {"ford": "yes", "lanes": "1"}, + {"ford": "yes", "lanes": "2"}, + {"embankment": "yes", "lanes": "1"}, + {"embankment": "yes", "lanes": "2"}, +] + + +# See https://wiki.openstreetmap.org/wiki/Proposed_features/placement + +PLACEMENT_FEATURES_1: List[Dict[str, str]] = [ + {"lanes": "1"}, + {"lanes": "2", "placement": "middle_of:1"}, + {"lanes": "4", "placement": "middle_of:2"}, + {"placement": "transition"}, + {"lanes": "3", "placement": "right_of:1"}, # or placement=left_of:2 +] + +PLACEMENT_FEATURES_2: List[Dict[str, str]] = [ + {"lanes": "2"}, + # or placement:backward=left_of:1 + {"lanes": "3", "placement:forward": "left_of:1"}, + {"lanes": "3", "placement": "transition"}, + {"lanes": "4", "placement:backward": "middle_of:1"}, + {"lanes": "3"}, +] + + +class Grid: + """Creating map with elements ordered in grid.""" + + def __init__(self) -> None: + self.x_step: float = 0.0002 + self.y_step: float = 0.0003 + self.x_start: float = 0.0 + self.index: int = 0 + self.nodes: Dict[OSMNode, Tuple[int, int]] = {} + self.max_j: float = 0 + self.max_i: float = 0 + + def add_node(self, tags: Dict[str, str], i: int, j: int) -> OSMNode: + """Add OSM node to the grid.""" + self.index += 1 + node: OSMNode = OSMNode( + tags, + self.index, + np.array((-i * self.y_step, j * self.x_step)), + ) + self.nodes[node] = (j, i) + self.max_j = max(self.max_j, j * self.x_step) + self.max_i = max(self.max_i, i * self.y_step) + return node + + def get_boundary_box(self) -> BoundaryBox: + """Compute resulting boundary box with margin of one grid step.""" + return BoundaryBox( + -self.x_step, + -self.max_i - self.y_step, + self.max_j + self.x_step, + self.y_step, + ) + + +def road_features( + types: List[Dict[str, str]], features: List[Dict[str, str]], path: Path +) -> None: + """Draw test image with different road features.""" + osm_data: OSMData = OSMData() + grid: Grid = Grid() + + for i, type_ in enumerate(types): + previous: Optional[OSMNode] = None + + for j in range(len(features) + 1): + node: OSMNode = grid.add_node({}, i, j) + + if previous: + tags: Dict[str, str] = dict(type_) + tags |= dict(features[j - 1]) + way: OSMWay = OSMWay( + tags, i * (len(features) + 1) + j, [previous, node] + ) + osm_data.add_way(way) + previous = node + + draw(osm_data, path, grid.get_boundary_box()) + + +def draw( + osm_data: OSMData, + output_path: Path, + boundary_box: BoundaryBox, + zoom: float = DEFAULT_ZOOM, +) -> None: + """Draw map.""" + configuration: MapConfiguration = MapConfiguration(level="all") + + flinger: Flinger = Flinger(boundary_box, zoom, osm_data.equator_length) + svg: Drawing = Drawing(output_path.name, flinger.size) + constructor: Constructor = Constructor( + osm_data, flinger, SCHEME, SHAPE_EXTRACTOR, configuration + ) + constructor.construct() + map_: Map = Map(flinger, svg, SCHEME, configuration) + map_.draw(constructor) + + with output_path.open("w") as output_file: + svg.write(output_file) + logging.info(f"Map is drawn to {output_path}.") + + +if __name__ == "__main__": + logging.basicConfig(format="%(levelname)s %(message)s", level=logging.INFO) + + highway_tags: List[Dict[str, str]] = [ + {"highway": value} for value in HIGHWAY_VALUES + ] + aeroway_tags: List[Dict[str, str]] = [ + {"aeroway": value} for value in AEROWAY_VALUES + ] + + road_features( + highway_tags, ROAD_LANES_AND_FEATURES, Path("out") / "lanes.svg" + ) + road_features( + highway_tags + RAILWAY_TAGS + aeroway_tags, + ROAD_WIDTHS_AND_FEATURES, + Path("out") / "width.svg", + ) + road_features( + highway_tags, + PLACEMENT_FEATURES_1 + [{"highway": "none"}] + PLACEMENT_FEATURES_2, + Path("out") / "placement.svg", + ) diff --git a/tests/test_flinger.py b/tests/test_flinger.py index 9819a03..ee764d0 100644 --- a/tests/test_flinger.py +++ b/tests/test_flinger.py @@ -3,7 +3,7 @@ Test coordinates computation. """ import numpy as np -from map_machine.flinger import ( +from map_machine.geometry.flinger import ( osm_zoom_level_to_pixels_per_meter, pseudo_mercator, ) diff --git a/tests/test_icons.py b/tests/test_icons.py index cd18024..c39eaa9 100644 --- a/tests/test_icons.py +++ b/tests/test_icons.py @@ -1,37 +1,46 @@ """ Test icon generation for nodes. """ -from typing import Dict, Set +from typing import Dict, List, Set, Tuple -import pytest +from pathlib import Path +from typing import Optional -from map_machine.grid import IconCollection -from map_machine.icon import IconSet +from colour import Color + +from map_machine.pictogram.icon import IconSet, ShapeSpecification, Icon +from map_machine.pictogram.icon_collection import IconCollection from tests import SCHEME, SHAPE_EXTRACTOR, workspace __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -@pytest.fixture -def init_collection() -> IconCollection: - """Create collection of all possible icon sets.""" - return IconCollection.from_scheme(SCHEME, SHAPE_EXTRACTOR) +COLLECTION: IconCollection = IconCollection.from_scheme(SCHEME, SHAPE_EXTRACTOR) +DEFAULT_COLOR: Color = SCHEME.get_default_color() +EXTRA_COLOR: Color = SCHEME.get_extra_color() +WHITE: Color = Color("white") -def test_grid(init_collection: IconCollection) -> None: +def test_grid() -> None: """Test grid drawing.""" - init_collection.draw_grid(workspace.output_path / "grid.svg") + COLLECTION.draw_grid(workspace.output_path / "grid.svg") -def test_icons_by_id(init_collection: IconCollection) -> None: +def test_icons_by_id() -> None: """Test individual icons drawing.""" - init_collection.draw_icons(workspace.get_icons_by_id_path()) + path: Path = workspace.get_icons_by_id_path() + COLLECTION.draw_icons(path, workspace.ICONS_LICENSE_PATH) + assert (path / "tree.svg").is_file() + assert (path / "LICENSE").is_file() -def test_icons_by_name(init_collection: IconCollection) -> None: +def test_icons_by_name() -> None: """Test drawing individual icons that have names.""" - init_collection.draw_icons(workspace.get_icons_by_name_path(), by_name=True) + path: Path = workspace.get_icons_by_name_path() + COLLECTION.draw_icons(path, workspace.ICONS_LICENSE_PATH, by_name=True) + assert (path / "Röntgen tree.svg").is_file() + assert (path / "LICENSE").is_file() def get_icon(tags: Dict[str, str]) -> IconSet: @@ -46,8 +55,48 @@ def test_no_icons() -> None: Tags that has no description in scheme and should be visualized with default shape. """ - icon = get_icon({"aaa": "bbb"}) + icon: IconSet = get_icon({"aaa": "bbb"}) assert icon.main_icon.is_default() + assert icon.main_icon.shape_specifications[0].color == DEFAULT_COLOR + + +def test_no_icons_but_color() -> None: + """ + Tags that has no description in scheme, but have `colour` tag and should be + visualized with default shape with the given color. + """ + icon: IconSet = get_icon({"aaa": "bbb", "colour": "#424242"}) + assert icon.main_icon.is_default() + assert icon.main_icon.shape_specifications[0].color == Color("#424242") + + +def check_icon_set( + icon: IconSet, + main_specification: List[Tuple[str, Optional[Color]]], + extra_specifications: List[List[Tuple[str, Optional[Color]]]], +) -> None: + """Check icon set using simple specification.""" + if not main_specification: + assert icon.main_icon.is_default() + else: + assert not icon.main_icon.is_default() + assert len(main_specification) == len( + icon.main_icon.shape_specifications + ) + for index, shape in enumerate(main_specification): + shape_specification: ShapeSpecification = ( + icon.main_icon.shape_specifications[index] + ) + assert shape_specification.shape.id_ == shape[0] + assert shape_specification.color == Color(shape[1]) + + assert len(extra_specifications) == len(icon.extra_icons) + for i, extra_specification in enumerate(extra_specifications): + extra_icon: Icon = icon.extra_icons[i] + assert len(extra_specification) == len(extra_icon.shape_specifications) + for j, shape in enumerate(extra_specification): + assert extra_icon.shape_specifications[j].shape.id_ == shape[0] + assert extra_icon.shape_specifications[j].color == Color(shape[1]) def test_icon() -> None: @@ -55,42 +104,64 @@ def test_icon() -> None: Tags that should be visualized with single main icon and without extra icons. """ - icon = get_icon({"natural": "tree"}) - assert not icon.main_icon.is_default() - assert not icon.extra_icons + icon: IconSet = get_icon({"natural": "tree"}) + check_icon_set(icon, [("tree", Color("#98AC64"))], []) def test_icon_1_extra() -> None: """ Tags that should be visualized with single main icon and single extra icon. """ - icon = get_icon({"barrier": "gate", "access": "private"}) - assert not icon.main_icon.is_default() - assert len(icon.extra_icons) == 1 + icon: IconSet = get_icon({"barrier": "gate", "access": "private"}) + check_icon_set( + icon, [("gate", DEFAULT_COLOR)], [[("lock_with_keyhole", EXTRA_COLOR)]] + ) def test_icon_2_extra() -> None: """ Tags that should be visualized with single main icon and two extra icons. """ - icon = get_icon({"barrier": "gate", "access": "private", "bicycle": "yes"}) - assert not icon.main_icon.is_default() - assert len(icon.extra_icons) == 2 + icon: IconSet = get_icon( + {"barrier": "gate", "access": "private", "bicycle": "yes"} + ) + check_icon_set( + icon, + [("gate", DEFAULT_COLOR)], + [ + [("bicycle", EXTRA_COLOR)], + [("lock_with_keyhole", EXTRA_COLOR)], + ], + ) def test_no_icon_1_extra() -> None: """ Tags that should be visualized with default main icon and single extra icon. """ - icon = get_icon({"access": "private"}) - assert icon.main_icon.is_default() - assert len(icon.extra_icons) == 1 + icon: IconSet = get_icon({"access": "private"}) + check_icon_set(icon, [], [[("lock_with_keyhole", EXTRA_COLOR)]]) def test_no_icon_2_extra() -> None: """ Tags that should be visualized with default main icon and two extra icons. """ - icon = get_icon({"access": "private", "bicycle": "yes"}) - assert icon.main_icon.is_default() - assert len(icon.extra_icons) == 2 + icon: IconSet = get_icon({"access": "private", "bicycle": "yes"}) + check_icon_set( + icon, + [], + [[("bicycle", EXTRA_COLOR)], [("lock_with_keyhole", EXTRA_COLOR)]], + ) + + +def test_icon_regex() -> None: + """ + Tags that should be visualized with default main icon and single extra icon. + """ + icon: IconSet = get_icon({"traffic_sign": "maxspeed", "maxspeed": "42"}) + check_icon_set( + icon, + [("circle_11", DEFAULT_COLOR), ("digit_4", WHITE), ("digit_2", WHITE)], + [], + ) diff --git a/tests/test_label.py b/tests/test_label.py index 7b54345..3dcbec9 100644 --- a/tests/test_label.py +++ b/tests/test_label.py @@ -3,7 +3,8 @@ Test label generation for nodes. """ from typing import Dict, List, Set -from map_machine.text import Label +from map_machine.map_configuration import LabelMode +from map_machine.text import Label, TextConstructor from tests import SCHEME __author__ = "Sergey Vartanov" @@ -13,7 +14,8 @@ __email__ = "me@enzet.ru" def construct_labels(tags: Dict[str, str]) -> List[Label]: """Construct labels from OSM node tags.""" processed: Set[str] = set() - return SCHEME.construct_text(tags, "all", processed) + text_constructor: TextConstructor = TextConstructor(SCHEME) + return text_constructor.construct_text(tags, processed, LabelMode.ALL) def test_1_label() -> None: diff --git a/tests/test_mapcss.py b/tests/test_mapcss.py index d93537c..b64abc6 100644 --- a/tests/test_mapcss.py +++ b/tests/test_mapcss.py @@ -18,7 +18,8 @@ def test_mapcss() -> None: selector = writer.add_selector("node", matcher) assert ( selector - == """node[natural="tree"] { + == """\ +node[natural="tree"] { icon-image: "icons/tree.svg"; } """ diff --git a/tests/test_osm_reader.py b/tests/test_osm_reader.py index 8aa1330..dc10edc 100644 --- a/tests/test_osm_reader.py +++ b/tests/test_osm_reader.py @@ -3,7 +3,7 @@ Test OSM XML parsing. """ import numpy as np -from map_machine.osm_reader import ( +from map_machine.osm.osm_reader import ( OSMData, OSMNode, OSMRelation, diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 7f8fbf6..387a571 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -1,11 +1,16 @@ -from map_machine import REQUIREMENTS +""" +Check whether `requirements.txt` contains all requirements from `setup.py`. +""" from pathlib import Path from typing import List +from map_machine import REQUIREMENTS + def test_requirements() -> None: + """Test whether `requirements.txt` has the same packages as `setup.py`.""" requirements: List[str] - with Path("requirements.txt").open() as requirements_file: + with Path("requirements.txt").open(encoding="utf-8") as requirements_file: requirements = list( map(lambda x: x[:-1], requirements_file.readlines()) ) diff --git a/tests/test_scheme.py b/tests/test_scheme.py new file mode 100644 index 0000000..a353edf --- /dev/null +++ b/tests/test_scheme.py @@ -0,0 +1,24 @@ +""" +Test scheme parsing. +""" +from typing import Any + +from map_machine.scheme import Scheme + + +def test_verification() -> None: + """Test verification process of tags in scheme.""" + + tags: dict[str, Any] = { + "colors": {"default": "#444444"}, + "node_icons": [{"tags": [{"tags": {"a": "b"}}]}], + } + assert Scheme(tags).node_matchers[0].verify() is True + + # Tag value should be string, not integer. + + tags: dict[str, Any] = { + "colors": {"default": "#444444"}, + "node_icons": [{"tags": [{"tags": {"a": 0}}]}], + } + assert Scheme(tags).node_matchers[0].verify() is False diff --git a/tests/test_tag.py b/tests/test_tag.py index 7d08354..324de33 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -3,7 +3,7 @@ Tests for length tag parsing. """ from typing import Optional -from map_machine.osm_reader import Tagged +from map_machine.osm.osm_reader import Tagged __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" diff --git a/tests/test_taginfo.py b/tests/test_taginfo.py new file mode 100644 index 0000000..37be0f9 --- /dev/null +++ b/tests/test_taginfo.py @@ -0,0 +1,17 @@ +""" +Test Taginfo project generation. +""" +from pathlib import Path + +from map_machine.doc.taginfo import TaginfoProjectFile +from tests import SCHEME + + +def test_taginfo() -> None: + """Test Taginfo project generation.""" + output_file_path: Path = Path("temp") / "taginfo.json" + + project_file: TaginfoProjectFile = TaginfoProjectFile( + output_file_path, SCHEME + ) + assert project_file.structure["project"]["name"] == "Map Machine" diff --git a/tests/test_vector.py b/tests/test_vector.py index d644e6a..946aba9 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -3,7 +3,7 @@ Test vector operations. """ import numpy as np -from map_machine.vector import compute_angle, turn_by_angle +from map_machine.geometry.vector import compute_angle, turn_by_angle __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -13,16 +13,18 @@ ROOT: float = np.sqrt(2) def test_compute_angle() -> None: """Test angle computing for all angles between 0 and 2π with step π / 4.""" - assert np.allclose(compute_angle((1, 0)), 0) - assert np.allclose(compute_angle((ROOT, ROOT)), np.pi * 0.25) - assert np.allclose(compute_angle((0, 1)), np.pi * 0.5) - assert np.allclose(compute_angle((-ROOT, ROOT)), np.pi * 0.75) - assert np.allclose(compute_angle((-1, 0)), np.pi) - assert np.allclose(compute_angle((-ROOT, -ROOT)), np.pi * 1.25) - assert np.allclose(compute_angle((0, -1)), np.pi * 1.5) - assert np.allclose(compute_angle((ROOT, -ROOT)), np.pi * 1.75) + assert np.allclose(compute_angle(np.array((1, 0))), 0) + assert np.allclose(compute_angle(np.array((ROOT, ROOT))), np.pi * 0.25) + assert np.allclose(compute_angle(np.array((0, 1))), np.pi * 0.5) + assert np.allclose(compute_angle(np.array((-ROOT, ROOT))), np.pi * 0.75) + assert np.allclose(compute_angle(np.array((-1, 0))), np.pi) + assert np.allclose(compute_angle(np.array((-ROOT, -ROOT))), np.pi * 1.25) + assert np.allclose(compute_angle(np.array((0, -1))), np.pi * 1.5) + assert np.allclose(compute_angle(np.array((ROOT, -ROOT))), np.pi * 1.75) def test_turn_by_compute_angle() -> None: """Test turing one angle by another.""" - assert np.allclose(turn_by_angle((1, 0), np.pi / 2), np.array((0, 1))) + assert np.allclose( + turn_by_angle(np.array((1, 0)), np.pi / 2), np.array((0, 1)) + ) diff --git a/tests/test_zoom_level.py b/tests/test_zoom_level.py index d0a1584..5cf8b02 100644 --- a/tests/test_zoom_level.py +++ b/tests/test_zoom_level.py @@ -1,7 +1,10 @@ """ Test zoom level specification parsing. """ -from map_machine.tile import ScaleConfigurationException, parse_zoom_level +from map_machine.slippy.tile import ( + ScaleConfigurationException, + parse_zoom_level, +) __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru"