Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3433135790 | ||
|
|
eb3268cf78 | ||
|
|
4da950961b | ||
|
|
529ab7cd5f | ||
|
|
857f64ce81 |
+7
-7
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(garmin2osmand VERSION 0.8.0 LANGUAGES CXX)
|
||||
project(garmin2osmand VERSION 0.95.0 LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
find_package(PNG REQUIRED)
|
||||
@@ -15,18 +15,18 @@ else()
|
||||
target_compile_options(garmin2osmand PRIVATE -Wall -Wextra -Wpedantic -O2)
|
||||
endif()
|
||||
enable_testing()
|
||||
add_test(NAME help COMMAND garmin2osmand --help)
|
||||
add_test(NAME help COMMAND garmin2osm --help)
|
||||
|
||||
find_package(Python3 COMPONENTS Interpreter QUIET)
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME classic_and_gmp_end_to_end COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_smoke.py $<TARGET_FILE:garmin2osmand>)
|
||||
add_test(NAME native_vector_export COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_vector.py $<TARGET_FILE:garmin2osm>)
|
||||
add_test(NAME bgmountains_legend_audit COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_legend_audit.py)
|
||||
add_test(NAME legacy_native_vector_parser COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_vector.py $<TARGET_FILE:garmin2osm>)
|
||||
add_test(NAME v092_native_osm_translation COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_v092.py)
|
||||
endif()
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME osmand_obf_pipeline_xml COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_obf_pipeline.py)
|
||||
add_test(NAME v093_memory_and_mapping_regressions COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_v093.py)
|
||||
endif()
|
||||
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME bgmountains_v08_display_mappings COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_v08_config.py)
|
||||
add_test(NAME v095_cli_config_and_obf_orchestration COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_v095.py)
|
||||
add_test(NAME native_registry_audit COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/test_registry_audit.py)
|
||||
endif()
|
||||
|
||||
@@ -1,103 +1,169 @@
|
||||
# BGMountains Garmin IMG → OSM → OsmAnd OBF v0.8
|
||||
# BGMountains Garmin IMG → native OsmAnd OBF — v0.95
|
||||
|
||||
**This release fixes the v0.7 icon-visibility blind spot and anonymous POIs.**
|
||||
**This is the integrated source release.** It replaces the v0.92/v0.93 Python
|
||||
pipeline and v0.94 standalone CSVs. Do not combine scripts from older ZIPs:
|
||||
those versions accept different flags and do not apply the 79 name-conditional
|
||||
rules. This archive has **one POI config and one line config**.
|
||||
|
||||
- Every one of the **263 validated retained Garmin POI types** has at least one conventional OSM semantic/display tag in `config/poi-overrides-all-types.csv`, in addition to raw `bgm_poi`/`garmin:*` provenance tags.
|
||||
- **87 codes** occur at least 100 times in the source catalog (409,154 source POIs). **46** use conventional feature categories recognized by stock OsmAnd in principle; **41** use an explicitly marked, synthetic `tourism=information;information=board` **local-OsmAnd display fallback**. Some additional lower-frequency codes also need fallback. This DOES NOT assert that the physical object is an information board.
|
||||
- Missing original IMG names become stable Bulgarian descriptive names, e.g. `Вода - Каптаж - 1THF1SDNTKJ`, with `bgm:generated_name=yes`. Original nonempty names are preserved.
|
||||
- `--jobs N` enables parallel extraction, **default one thread**.
|
||||
- New `tools/audit_icon_coverage.py` verifies source OSM classification coverage, names and produces per-type coordinate report. It **does not** prove OBF index presence or visual display; use an OBF inspector and OsmAnd for that.
|
||||
- Map-derived icon art and datasets remain governed by BGMountains' applicable copyright conditions. These synthetic POI tags are **not appropriate for uploading unchanged to the real OpenStreetMap database**.
|
||||
## Quick start (Ubuntu/Debian)
|
||||
|
||||
## Quick build (Ubuntu/Debian)
|
||||
|
||||
```sh
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake libpng-dev libsqlite3-dev python3 openjdk-21-jre-headless unzip
|
||||
sudo apt install -y cmake g++ make libpng-dev libsqlite3-dev python3 openjdk-21-jre-headless unzip
|
||||
unzip BGMountains-garmin2osm-v0.95-integrated.zip
|
||||
cd BGMountains-garmin2osm-v0.95
|
||||
```
|
||||
|
||||
Use **exactly** this from the release root (where `scripts/` and `config/` are):
|
||||
|
||||
```bash
|
||||
nohup python3 scripts/build_bgmountains.py \
|
||||
--img "$HOME/gmapsupp.img" \
|
||||
--poi-config config/BGMountains-native-poi-mapping-v094.csv \
|
||||
--line-config config/line-mapping.csv \
|
||||
--out bg-mountains-v095-build \
|
||||
--download-creator \
|
||||
--jobs 8 \
|
||||
--java-opts '-Xms512M -Xmx25G' > bg-v095.log 2>&1 &
|
||||
```
|
||||
|
||||
The `--poi-config` and `--line-config` arguments are **implemented and tested**.
|
||||
Other supported flags: `--creator /path/to/OsmAndMapCreator`, `--bbox W,S,E,N`,
|
||||
`--prepare-only`, `--keep-raw`, `--template`, `--attach-routes`,
|
||||
`--disk-headroom-gb N`, `--ignore-disk-check`, `--jobs N` (default 1).
|
||||
|
||||
**Check your actual disk space first.** Full-country MapCreator compilation has
|
||||
previously required over 25 GiB of temporary disk space in addition to the
|
||||
source OSM (6+ GiB) on your machine. The script uses a conservative estimate
|
||||
`max(2 GiB, 5 × native-OSM-size + 1 GiB)`, unless `--disk-headroom-gb`
|
||||
requires more. These figures are not upper bounds. Choose an output filesystem
|
||||
with generous free space. `-Xmx25G` controls heap RAM, **not disk usage**.
|
||||
|
||||
## Safe regional test, no Java or extra download
|
||||
|
||||
```bash
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img /absolute/path/to/gmapsupp.img \
|
||||
--out ./BGMountains-out \
|
||||
--download-creator
|
||||
```
|
||||
|
||||
`--download-creator` is for the official OsmAndMapCreator, and the pipeline downloads the original official `poi_types.xml` and `rendering_types.xml` the first time as needed. Network access is required for these unless you already supply the files. **This runtime did not have MapCreator, so full OBF compilation was not executed here.**
|
||||
|
||||
Already installed OsmAndMapCreator:
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py --img ./gmapsupp.img --out ./BGMountains-out \
|
||||
--creator /path/to/OsmAndMapCreator
|
||||
```
|
||||
|
||||
Opt-in parallelism: add `--jobs 4`. The output directory must be fresh: the converter refuses to overwrite an existing OSM or staging files.
|
||||
|
||||
Prepare and validate the OSM without invoking the Java OBF builder:
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py --img ./gmapsupp.img --out ./BGMountains-preview \
|
||||
--img "$HOME/gmapsupp.img" \
|
||||
--poi-config config/BGMountains-native-poi-mapping-v094.csv \
|
||||
--line-config config/line-mapping.csv \
|
||||
--bbox '25.05,42.43,25.16,42.54' \
|
||||
--jobs 8 \
|
||||
--out ./regional-test \
|
||||
--prepare-only
|
||||
```
|
||||
|
||||
**IMPORTANT: You must REBUILD the OSM and OBF.** This update cannot modify an already compiled `.obf` or give v0.7 output its new tags in place.
|
||||
Inspect `regional-test/BGMountains-native.osm`, `semantic-audit.json`,
|
||||
`route-report.json`, `extract.log`, `translate.log`, `audit.log`. The tool
|
||||
must exit nonzero on a failed conversion subprocess. You can instead pass an
|
||||
existing MapCreator with `--creator ...` (omit `--prepare-only`) for a small
|
||||
real OBF build.
|
||||
|
||||
## What the configuration files mean
|
||||
## Two editable configuration files
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `config/poi-overrides-all-types.csv` | **Active** 1,871-row config, including 263 retained types with revised tags. `mode=replace` replaces old hardcoded semantics, retaining provenance/name. All non-present legacy type rows remain for historical comparison. |
|
||||
| `config/poi-overrides-all-types-v07-user-upload.csv` | Exact backup of the CSV you supplied. |
|
||||
| `config/poi-labels-bg.csv` | New 263-row Bulgarian fallback name prefixes, editable. |
|
||||
| `config/poi-icon-audit.csv` | All 263 types, count, original tags vs new tags, classification confidence and sample coordinates. |
|
||||
| `config/osmand-icons.csv` | Optional **custom render style** icon names/visibility; never controls the default `tourism=information` fallback injected through the active overrides. |
|
||||
| `data/BGMountains-frequent-poi-types-100plus-v08.csv` | All 87 frequent types, with reviewed tags, classification mode and coordinates. |
|
||||
| `tools/audit_icon_coverage.py` | Independent POI OSM field audit; one row per present type. |
|
||||
### 1. `config/BGMountains-native-poi-mapping-v094.csv`
|
||||
|
||||
**How to edit a type:** find `code=0x6500` in active overrides. Edit the **tags** column, leaving a `key=value;key=value` list. Change `mode=replace` only if you understand how the C++ built-in classifications merge. Set a Bulgarian text prefix in `config/poi-labels-bg.csv`. Run again from scratch to rebuild OSM then OBF.
|
||||
1,871 base hex-code entries plus 79 **name-specific** exceptions (1,950 data
|
||||
rows). Columns include: `code`, `source_count`, `legend_bg`, `legend_en`,
|
||||
`meaning_confidence`, `osm_tags`, `label_bg`, `priority`, `zoom_hint`, `action`,
|
||||
`note`, examples, and optional `name_contains`. For conditional matching,
|
||||
set `name_contains` in an additional row with the same `code`. The matcher uses
|
||||
case-sensitive substring matching on the **original Garmin name**, with rules
|
||||
applied in file order after the base rule. `replace` changes the converter's
|
||||
semantic defaults; `append` retains previous tags; `raw` leaves unspecified
|
||||
features unclassified until private-map fallback; `drop` discards the feature.
|
||||
|
||||
### Why 0x6500 is not `amenity=drinking_water`
|
||||
The Python pipeline automatically creates `cpp-poi-override.csv` and
|
||||
`cpp-name-labels.csv` **inside the build output directory**. These are generated
|
||||
intermediates, **not third and fourth user configuration files**. There is no
|
||||
need to manually supply them or an icon pack to OsmAnd.
|
||||
|
||||
The original legend labels this a **каптаж** (water intake/catchment), not necessarily a drinking-water source. v0.8 records `man_made=water_intake` but adds an explicit *presentation-only* `tourism=information;information=board;bgm:synthetic_icon=yes` for common OsmAnd rendering. The "information board" is NOT a physical claim. The default Bulgarian name prefix is `Вода - Каптаж`. If you want `Вода - Резервоар`, just edit the label CSV; do not misrepresent a small intake as an artificial lake (`natural=water;water=reservoir`).
|
||||
Important native examples:
|
||||
|
||||
`0x6602` already has `tourism=viewpoint` and does not use a synthetic board marker. Its failure to appear in a particular existing OBF could be an old build, incomplete POI indexing, or display suppression—only inspecting that actual OBF can distinguish these.
|
||||
- `0x6414`: `amenity=drinking_water;man_made=water_tap` (legacy v18 dual
|
||||
classification); named dry sources such as `(Сух.)` remove drinking-water
|
||||
classification and get `drinking_water=no`.
|
||||
- `0x6500`: `man_made=water_intake;amenity=water_point;drinking_water=unknown`
|
||||
for **private-map index compatibility**, not evidence that its water is
|
||||
potable, available or accessible. Named shaft (`Шах...`) exceptions remove
|
||||
the compatibility alias.
|
||||
- `0x6511`: `natural=spring` (potability not asserted).
|
||||
- `0x6402`: `building=yes` by default, explicitly named `Заслон` or `Хижа`
|
||||
may become shelter/hut (availability/access not verified).
|
||||
- `0x661d`: agricultural structures remain agricultural unless specifically
|
||||
identified otherwise.
|
||||
- `0x6600`: keep numeric spot height `ele=*`, **no fake settlement**.
|
||||
- Unknown POIs: `place=locality`, `Неуточнено - <stable hash>` if unnamed,
|
||||
and `bgm:synthetic_fallback=place_locality`; this private-map fallback **does
|
||||
not guarantee Nearby POI indexing**.
|
||||
|
||||
### Stable anonymous names
|
||||
`priority` and `zoom_hint` are documented classification hints only. They do
|
||||
**not force stock OsmAnd to show an icon at zoom 12**. Likewise `garmin:type`
|
||||
remains a provenance tag, not a display rule. Only stock OsmAnd classifications
|
||||
and rendering apply to the resulting OBF.
|
||||
|
||||
For an unnamed POI, `name = Bulgarian_label + " - " + FNV1a_base36_11(mapset|type|quantized_lat|quantized_lon)`. The output is deterministic for an identical IMG and coordinates across threads and builds. A very small collision probability remains; the coordinates/mapset/type remain available. These generated names are for an offline map, **not actual surveyed names**.
|
||||
### 2. `config/line-mapping.csv`
|
||||
|
||||
### Test against your three sample coordinates
|
||||
50 Garmin line types (20 currently have `route_mode=relation`, 30 preserve
|
||||
source geometry). Columns include Garmin `code`, legend, color,
|
||||
`osm_way_tags`, `route_mode`, `osmc_symbol`, `network`, confidence and priority.
|
||||
Selected source overlay segments are grouped into native
|
||||
`type=route;route=hiking` relations with `osmc:symbol`. Garmin source color is
|
||||
preserved as relation `colour=*`. The script does not create duplicate routable
|
||||
paths from decoration lines. Opt-in `--attach-routes` matches overlays to real
|
||||
existing path geometries, but can use significant memory; it is off by default.
|
||||
**Existing physical path routing remains in the data.** Exact phone-side color,
|
||||
icon and at-zoom visibility still depend on stock OsmAnd.
|
||||
|
||||
| Type | Coordinates | Expected tags | v0.8 generated name |
|
||||
|---|---|---|---|
|
||||
| `0x6602` | `42.0346141,24.6528697` | `tourism=viewpoint` | `Панорама - Гледка - 25IVP49H823` |
|
||||
| `0x6500` | `42.0347643,24.6522689` | `man_made=water_intake;tourism=information;information=board` | `Вода - Каптаж - 1THF1SDNTKJ` |
|
||||
| `0x6500` | `42.0340991,24.6520114` | Same | `Вода - Каптаж - ED03D455VTZ` |
|
||||
## New or unknown IMG maps: generate configuration templates
|
||||
|
||||
These examples were directly extracted from the 422MiB IMG into a Plovdiv test OSM and compared to a source manifest. All 57 POIs in that test passed exact semantic-tag checks, with **zero missing and extra**. Serial vs `--jobs 4` POI identities/tags were equal.
|
||||
|
||||
## Review before copying `.obf` into OsmAnd
|
||||
|
||||
Pipeline artifacts in `--out`:
|
||||
- `BGMountains.osm` — exact source XML for the OBF builder
|
||||
- `source-pois.csv`, `poi-types.csv` — extraction manifest and type counts
|
||||
- `validation.json`, `mismatches.csv` — original-source to OSM audit
|
||||
- `poi-coverage.csv`, `poi-coverage.json` — export display mapping coverage
|
||||
- `osmand-config/poi_types.bgmountains.xml`, `rendering_types.bgmountains.xml` — registrations added to **complete upstream XML**
|
||||
- `osmand-config/BGMountains-custom.render.xml` — optional style; exact Garmin TYP art is NOT embedded into stock OsmAnd
|
||||
- `*.obf` plus `mapcreator.log`, `obf-poi-index.txt` (only after a successful Java compilation)
|
||||
|
||||
The `tourism=information` fallback should give OsmAnd a native searchable POI category in an appropriate compatible build. **Neither an OSM tag nor an indexed OBF proves a symbol will be drawn at every zoom**: the map style, profile, POI filters, icon availability, density/collision and selected offline map still affect visibility. Test at zoom 17–20 with POI layers enabled. Avoid having an old overlapping BGMountains OBF selected at the same time.
|
||||
|
||||
To inspect your built file with OsmAndMapCreator:
|
||||
|
||||
```sh
|
||||
/path/to/OsmAndMapCreator/inspector.sh -vpoi -vpoiobjects \
|
||||
-bbox=24.64,42.05,24.665,42.02 ./BGMountains-out/*.obf \
|
||||
> ./BGMountains-out/plovdiv-obf-inspector.txt
|
||||
```bash
|
||||
python3 scripts/build_bgmountains.py --img /path/to/unknown.img \
|
||||
--out ./template-newmap --bbox '23.0,42.0,24.0,43.0' --template
|
||||
```
|
||||
|
||||
Inspect output for the three named POIs. **There is no tested generic parser of OsmAndMapCreator's inspector text in this package**, and we have not verified a full-country OBF built from v0.8 in this runtime. If OSM has the POI but the inspector doesn't, focus on indexing or generated XML; if inspector has it but the screen doesn't, focus on style/POI display/filter/zoom.
|
||||
Produces `poi-mapping.TEMPLATE.csv` and `line-mapping.TEMPLATE.csv` with
|
||||
observed types and sample coordinates. Fill them manually and run with
|
||||
`--poi-config ./template-newmap/poi-mapping.TEMPLATE.csv` and
|
||||
`--line-config ./template-newmap/line-mapping.TEMPLATE.csv`. **Do not assume
|
||||
Garmin codes have the same meaning in unrelated maps.** If you need a complete
|
||||
catalog for a new national map, generate its templates without `--bbox`, but
|
||||
this can require significant time and intermediate disk space.
|
||||
|
||||
### Local-only licensing warning
|
||||
## Validation and provenance
|
||||
|
||||
Do not upload the generated synthetic display tags to OpenStreetMap or assert they accurately describe physical information boards. The original IMG, derived icons and extracts can have third-party copyright restrictions. The source-only code path should be distributed separately from map-derived sample assets until rights are confirmed.
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build -j 4
|
||||
ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
There is also `tools/audit_osmand_poi_mapping.py`, which takes the official
|
||||
version-matched OsmAnd `poi_types.xml` and produces a registry compatibility
|
||||
report, **not a claim of final icon visibility**:
|
||||
|
||||
```bash
|
||||
python3 tools/audit_osmand_poi_mapping.py \
|
||||
--mapping config/BGMountains-native-poi-mapping-v094.csv \
|
||||
--poi-types /path/to/poi_types.xml \
|
||||
--report native-poi-audit.csv --json native-poi-audit.json
|
||||
```
|
||||
|
||||
Source: https://github.com/osmandapp/OsmAnd-resources/blob/master/poi/poi_types.xml
|
||||
|
||||
## Tested, and NOT tested
|
||||
|
||||
- C++ Release build, 6 CTest test groups, conditional POI CSV generation,
|
||||
CLI options, and simulated MapCreator orchestration passed.
|
||||
- Original 422 MiB IMG: regional `--jobs 8` and serial preparation succeeded
|
||||
with identical 427 POI coordinate/name/tag records and 6 route relations.
|
||||
- Separate region: `(Сух.)` water-tap conditional correctly produced
|
||||
`man_made=water_tap;drinking_water=no`, and no `amenity=drinking_water`.
|
||||
- **A real OsmAndMapCreator binary was not available here**; its host was
|
||||
unreachable. The OBF orchestration unit test uses a **mock**, so native
|
||||
nationwide OBF compilation and phone rendering are not verified. Do not
|
||||
mistake that mock for real OBF validation.
|
||||
|
||||
License: the source bundle contains historically BGMountains-derived
|
||||
classification information. Treat that data as private-map material under
|
||||
applicable source licenses; the package is **not** authorization to republish
|
||||
BGMountains geography or to import its features into the public OSM database.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
See README_V093.md for current diagnosis and changes. Historical v0.92 notes were superseded.
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
# BGMountains Garmin IMG → native OSM → OsmAnd OBF — v0.92
|
||||
|
||||
**v0.92** fixes the v0.8–v0.9 synthetic marker/marina fallback approach. It aims to create a **native OsmAnd map** without Garmin TYP icon PNGs or a custom `.render.xml` on the phone.
|
||||
|
||||
The **only two user-editable translation files** are:
|
||||
|
||||
1. `config/poi-mapping.csv` (Garmin point type → native OSM tag(s), Bulgarian naming prefix, legend, priority, historic icon pointer, example coordinates)
|
||||
2. `config/line-mapping.csv` (Garmin extended line type → OSM way tags, color-marked hiking relation policy and `osmc:symbol`)
|
||||
|
||||
Everything under the generated build/output directory is an **intermediate or report**, not a third configuration. You don't have to supply `poi_types.xml`, `rendering_types.xml`, an icons CSV or a custom rendering style.
|
||||
|
||||
## Requirements
|
||||
|
||||
Ubuntu/Debian: `sudo apt install build-essential cmake libpng-dev libsqlite3-dev python3 openjdk-21-jre-headless unzip`
|
||||
|
||||
To **compile an OBF**, provide an official [OsmAndMapCreator](https://osmand.net/docs/technical/map-creation/create-offline-maps-yourself/) installation containing `utilities.sh`, or use the automatic download option (downloads from OsmAnd). **Python, CMake and `make` by themselves do NOT build OBF**.
|
||||
|
||||
## Full map: one command
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img '/path/to/gmapsupp.img' \
|
||||
--out './output' \
|
||||
--creator '/path/to/OsmAndMapCreator'
|
||||
```
|
||||
|
||||
If you don't have MapCreator:
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img '/path/to/gmapsupp.img' --out './output' --download-creator
|
||||
```
|
||||
|
||||
It compiles C++, extracts the Garmin IMG to OSM, uses the two mapping files, assigns stable Bulgarian hashes to unnamed POIs, produces route relations, conservatively attaches matched overlays to actual `highway=*` ways, performs a semantic QA audit, and runs OsmAndMapCreator. The **only file to copy onto your phone is the resulting `.obf`**. Enable hiking routes and relevant POI filters in OsmAnd as necessary. No third-party icons or renderer file is included or required.
|
||||
|
||||
For a fast test that stops before the Java OBF stage:
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img '/path/to/gmapsupp.img' \
|
||||
--out './vihren-test' --bbox '23.36,41.70,23.42,41.76' --prepare-only
|
||||
```
|
||||
|
||||
Parallelism remains **disabled by default** (`--jobs 1`). For opt-in concurrent IMG extraction append `--jobs 4`. The Python mapping, relation, QA and Java steps are not parallelized by this switch.
|
||||
|
||||
Use `--no-route-attachment` to keep source overlay geometries as hiking-relation members instead of attaching matched real path/road ways. `--keep-raw` retains the intermediate IMG-native XML for debugging.
|
||||
|
||||
## Editing the 2 CSV files
|
||||
|
||||
### `config/poi-mapping.csv`
|
||||
|
||||
| Column | What it means |
|
||||
|---|---|
|
||||
| `code` | Garmin POI type, in hex; never changed in provenance |
|
||||
| `source_count` | Count from source inventory; informational only |
|
||||
| `legend_bg`, `legend_en` | Original BGMountains legend meaning (where verified) |
|
||||
| `meaning_confidence` | `legend` / `source_inferred`; review unknowns |
|
||||
| `osm_tags` | `k=v;k=v` for **truthful standard** OSM semantics; no fake category for icons |
|
||||
| `label_bg` | Prefix for otherwise nameless objects, with deterministic alphanumeric suffix |
|
||||
| `priority` | `critical`, `normal` or `terrain`; currently emits `bgm:priority` metadata |
|
||||
| `zoom_hint` | Desirable zoom target for future style/QA work, **not an OsmAnd override** |
|
||||
| `action` | `replace` removes earlier semantics, `append` keeps them, `raw` keeps provenance only, `drop` discards the POI |
|
||||
| `legacy_icon` | `TYP:<hex>` reference for older Garmin icon, for auditing only |
|
||||
| `example_lat`, `example_lon`, `example_name` | Sample place from original source inventory to inspect |
|
||||
|
||||
If a source code is known, edit `osm_tags` and set `action=replace`. Example: `0x6414` → `amenity=drinking_water`. If it isn't known, leave it as `raw` until you inspect the old legend and sample coordinates. Nothing will invent a marina, information board or drinking tap for an unknown symbol. You may append multiple compatible tags separated by semicolons.
|
||||
|
||||
**Water/shelter priority rules already populated**: `0x6414` drinking tap, `0x6511` spring, `0x6500` water intake, `0x6417` well, `0x6609` hut, `0x660a` basic refuge, `0x661e` shelter and `0x6402` building. For requested naming, unnamed `0x6402` uses the prefix **„Заслон - Сграда“**; its factual OSM classification stays `building=yes` (a generic building must not falsely be declared an actual lifesaving shelter). Verify shelter suitability on the ground before relying on it.
|
||||
|
||||
`0x6600` numeric elevation labels preserve `ele=*` instead of being misclassified as peaks, buildings or missing POI categories. `0x650b` winter waymarkers and `0x660b` trail markers remain Garmin-sourced nodes rather than incorrect water/marina/guidepost POIs.
|
||||
|
||||
### `config/line-mapping.csv`
|
||||
|
||||
| Column | Meaning |
|
||||
|---|---|
|
||||
| `code`, `source_count` | Garmin extended polyline code/count |
|
||||
| `legend_bg`, `legacy_color` | Known description and source TYP base color |
|
||||
| `osm_way_tags` | Additional truthful `key=value;key=value` tags on source ways |
|
||||
| `route_mode` | `relation` builds OSM hiking routes; `preserve` keeps raw way without assuming it is a trail |
|
||||
| `osmc_symbol` | Standard OSMC syntax, e.g. `red:white:red_bar` |
|
||||
| `network` | `lwn` (local walking), change if there is evidence for a larger network |
|
||||
| `route_confidence` | `color_only` means inferred from color but pattern not independently established |
|
||||
| `priority` | Descriptive category only, not stock OsmAnd zoom control |
|
||||
|
||||
Colored candidate hiking overlays become `type=route` + `route=hiking` + `osmc:symbol=*` relations. All retain the original `garmin:type` and source line metadata. Matching route overlay lines to existing physical `highway=*` geometry avoids generating fake navigable duplicate paths. A physical road segment is used only if at least 90% of an overlay's segments match and the physical way extends at most 10% beyond it; unmatched overlays remain relation members, flagged in `route-attachment.json`.
|
||||
|
||||
**A source color is not conclusive proof of a painted bar pattern**. All automatic bar-style assignments from color-only evidence explicitly say `route_confidence=color_only`; check the original map and choose the right `osmc_symbol` for each code before treating that detail as verified. See [OSM trail markings](https://wiki.openstreetmap.org/wiki/Trail_markings) and [OSMC symbol syntax](https://wiki.openstreetmap.org/wiki/Key:osmc:symbol).
|
||||
|
||||
## New IMG / future map: generate blank templates
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img '/path/to/another-garmin.img' \
|
||||
--out './new-map-template' --template
|
||||
```
|
||||
|
||||
The result contains `poi-mapping.TEMPLATE.csv` with **all discovered types, counts and sample POI coordinates**, and `line-mapping.TEMPLATE.csv` with all discovered extended line types/counts. Unknowns start as `action=raw`, `route_mode=preserve` and **without semantic tags**. You must fill them based on that map's own legend and examples; BGMountains mappings must not be assumed to apply to another Garmin map. To build it, point `--poi-config` / `--line-config` to the new two reviewed files.
|
||||
|
||||
## Outputs / validation
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| `BGMountains-native.osm` | Final translated, standard-tagged OSM intermediate |
|
||||
| `source-pois.csv` | Per-point source type, mapset, coordinates and name |
|
||||
| `discovered-pois.csv` | Observed type inventory with up to 5 sample coordinates |
|
||||
| `route-report.json` | Coloured overlays/route relation counts |
|
||||
| `route-attachment.json` | Reused physical path vs unmatched overlay counts |
|
||||
| `semantic-audit.json` | Detailed native category statistics, unknown types and example positions |
|
||||
| `extract.log`, `translate.log`, `audit.log` | Pipeline logs |
|
||||
| `mapcreator.log` | Java OBF build log when compilation is attempted |
|
||||
| `obf-inspection.txt` | BinaryInspector POI summary (if inspector is installed) |
|
||||
| `*.obf` | Final file for OsmAnd (only if MapCreator succeeds) |
|
||||
|
||||
Tests:
|
||||
|
||||
```sh
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build -j 2
|
||||
ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
## What native stock OsmAnd cannot guarantee
|
||||
|
||||
- OsmAnd's **own rendering style controls icon selection, label collision and minimum zoom**; `zoom_hint=12` does not change it. To force all water/hut icons to zoom 11 or 12, a custom OsmAnd style/app configuration is unavoidable. v0.92 deliberately does **not** install one.
|
||||
- Not every valid OSM tag has a stock icon, especially water intakes, bridge points, elevation points and trail blazes. We prefer correct/metadata-only tags over false marinas/boards.
|
||||
- A valid hiking relation is not a guarantee that stock OsmAnd renders every segment. Inspect `route-attachment.json` and verify on-device.
|
||||
- The OSM file parser and XML/route structural tests do **not** establish that the POIs survived final OBF indexing. Use `mapcreator.log`, `obf-inspection.txt` and actual OsmAnd app checks. We cannot claim a complete nationwide OBF was compiled/tested in this environment unless MapCreator is available.
|
||||
- Map-derived sample coordinates and legend crosswalk are subject to BGMountains data licensing; do not relicense or import them to public OSM automatically.
|
||||
|
||||
**v0.92 does not use the Garmin icon PNGs or `BGMountains-outdoor.render.xml`**. You may remove older BGMountains OBFS from OsmAnd to avoid duplicates before installing the new one.
|
||||
@@ -0,0 +1,76 @@
|
||||
# BGMountains v0.93 — bounded-memory translation and private-map POI fallback
|
||||
|
||||
This is a **source patch release**, building on v0.92. All user translation choices live in exactly two files:
|
||||
|
||||
1. `config/poi-mapping.csv`: Garmin hex → OSM semantic tags; Bulgarian legend name and old TYP code; `action`, priority, source count and examples.
|
||||
2. `config/line-mapping.csv`: Garmin line hex → OSM way tags, `osmc:symbol`, route relation choice, marking color and confidence.
|
||||
|
||||
`cpp-poi-override.csv` and `cpp-name-labels.csv` are generated **internal intermediates**, not control files; do not edit them.
|
||||
|
||||
## Direct diagnosis of the supplied nohup
|
||||
|
||||
The same appended logfile shows **different historical attempts**: an early `java.lang.OutOfMemoryError: Java heap space` in OsmAndMapCreator, followed in the last attempt by `java.io.IOException: No space left on device` while `IndexCreator.extractOsmToNodesDB` built the Java/SQLite intermediate database. Earlier `poi-coverage.log` saying `PASS` refers only to OSM semantic checks. It is not a successful OBF creation signal.
|
||||
|
||||
## Changes
|
||||
|
||||
- Streaming OSM parser clears the XML **root**, preventing an accumulating empty Element for every object.
|
||||
- Expensive optional `attach_native_routes.py` step is opt-in (`--attach-routes`); default keeps existing routable highway network unchanged, and generates hiking-route relations from Garmin colored overlays. Without attachment, those relations describe overlay geometry; they are not proof of route-based navigation along the existing path ways.
|
||||
- Unclassified POIs now receive `place=locality`, `bgm:synthetic_fallback=place_locality`, `bgm:classification=unverified`, and a generated Bulgarian name `Неуточнено - <stable hash>` **when the source name was empty or previously synthetic**. Genuine source names remain untouched. Raw `garmin:type` and mapset information are preserved.
|
||||
- **Exception:** `0x6600` numeric spot heights and other elevation annotations (`ele=*`) are not fabricated as localities. They remain elevations rather than false place icons. Known native tags are never overwritten by the fallback.
|
||||
- Native categories for water, drinking water, shelters/huts, bridges and more remain sourced from existing two mapping files. No extracted Garmin artwork or phone-side custom stylesheet is required.
|
||||
- Semantic audit differentiates actual unclassified records, synthetic locality fallbacks and non-POI elevation annotations, and includes example coordinates. `PASS` still does not prove an Android icon renders.
|
||||
- Conservative free-disk preflight before invoking MapCreator, based on `max(2 GiB, 5*OSM size + 1 GiB)` free in the output filesystem. The estimate is based on the reported >25 GiB scratch need for ~6 GiB OSM; it is *not a guarantee*.
|
||||
- JVM heap via `JAVA_TOOL_OPTIONS` as well as `JAVA_OPTS`, for MapCreator builds that ignore the latter; use e.g. `--java-opts '-Xms512M -Xmx6G'` (subject to host RAM). **A large heap cannot solve out-of-disk.**
|
||||
- OBF success only if a newly generated nonempty `.obf` exists, not a stale file from an earlier run.
|
||||
|
||||
## Quick regional test — no Java, no large output
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img '/path/to/gmapsupp.img' \
|
||||
--bbox '23.30,41.67,23.50,41.84' \
|
||||
--out './regional-test' \
|
||||
--prepare-only
|
||||
|
||||
cat regional-test/semantic-audit.json
|
||||
cat regional-test/route-report.json
|
||||
```
|
||||
|
||||
`--jobs 1` is the default. `--jobs 4` is optional; on the source IMG, serial and parallel runs had equal POI counts and candidate colored overlay counts but a slight discrepancy in connected route groups (78 vs 82). **Do not assume identical routing topology with parallel extraction yet.**
|
||||
|
||||
To test a truly unfamiliar map and generate configuration templates:
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py --img another.img --bbox 'W,S,E,N' \
|
||||
--out ./new-map --template
|
||||
# Review: new-map/poi-mapping.TEMPLATE.csv and line-mapping.TEMPLATE.csv
|
||||
```
|
||||
|
||||
The templates are intentionally blank for unknown source meanings; when used by the translation pipeline, unknown POIs will receive the private `place=locality` fallback. A new map should not blindly reuse BGMountains meanings.
|
||||
|
||||
## Full OBF build — only on a machine with enough free scratch/RAM
|
||||
|
||||
```sh
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img '/path/to/gmapsupp.img' \
|
||||
--out '/large-volume/bgmountains-v093' \
|
||||
--creator '/path/to/OsmAndMapCreator' \
|
||||
--java-opts '-Xms512M -Xmx8G'
|
||||
```
|
||||
|
||||
Or first use `--prepare-only` and later `--osm '/path/to/IMG-raw.osm'` to skip the C++ extraction **if** that raw original file was saved (`--keep-raw`). `--osm` expects the *raw* IMG-derived OSM, not the already translated native OSM.
|
||||
|
||||
The preflight checks output filesystem free space; for shared temp partitions, also check `df -h /tmp "$PWD"` and `df -i`. An OBF may still fail if actual scratch is elsewhere. Only override the preflight (`--ignore-disk-check`) when you have independently confirmed scratch capacity.
|
||||
|
||||
## Limitations / honesty
|
||||
|
||||
- `place=locality` is a **synthetic label hack chosen for this PRIVATE map**, not a correct definition of all unknown source objects and not suitable for direct uploading into OSM.
|
||||
- Stock OsmAnd decides icon set, zoom threshold, collision suppression and Nearby POI search category. A native OSM tag increases compatibility but cannot guarantee every icon is shown or listed in Nearby without a mobile test.
|
||||
- Route colors represented through native OSM hiking relations using `osmc:symbol` for identified Garmin colored overlay types. Color/pattern interpretations with limited source evidence remain explicitly provisional.
|
||||
- No full-country OBF creation has been performed as part of this patch validation, per the user's storage constraints. The final national MapCreator step remains to be verified on the user's workstation.
|
||||
|
||||
## Regression verification
|
||||
|
||||
- Unit tests: `python3 -m unittest discover -s tests -p 'test_v093.py' -v`
|
||||
- Previous v0.92 tests: `python3 -m unittest discover -s tests -p 'test_v092.py' -v`
|
||||
- Regional real IMG: 1,915 POIs, 296 elevation annotations, 150 synthetic localities, zero unclassified after fallback, 273 route-overlay candidates. Single-worker result has 78 connected route groups. The v0.93 code has not been validated end-to-end against stock OsmAnd on Android.
|
||||
@@ -0,0 +1,117 @@
|
||||
code,name_contains,count,old_mode,new_mode,old_tags,new_tags,basis,special_case
|
||||
0x2a07,,227,replace,replace,bgm:landmark=unknown;bgm:unclassified=yes,amenity=fast_food,snacks/bakeries and kiosks from sampled names; bistro exceptions,no
|
||||
0x2a0e,,525,replace,replace,shop=yes,amenity=cafe,sampled names cafe/tea/confectionery,no
|
||||
0x2c06,,57,replace,replace,bgm:unclassified=yes,amenity=university,"sampled names universities/faculties, one medical exception",no
|
||||
0x2c07,,20,replace,replace,bgm:unclassified=yes,tourism=zoo,sampled names zoos/animal parks,no
|
||||
0x2c08,,1328,replace,replace,leisure=horse_riding,leisure=sports_centre,sports venues of varied kinds: not horse-riding by default; name overrides for specific sports,no
|
||||
0x2d01,,68,replace,replace,bgm:unclassified=yes,amenity=theatre,"sampled names drama theatres/opera, cinemas as exceptions",no
|
||||
0x2d02,,434,replace,replace,bgm:landmark=unknown;bgm:unclassified=yes,amenity=bar,"sampled names bars/clubs, cafe/restaurant exceptions",no
|
||||
0x2d03,,57,replace,replace,bgm:unclassified=yes,amenity=cinema,"sampled names cinemas, theatre exception",no
|
||||
0x2e03,,502,replace,replace,shop=yes,shop=supermarket,sampled names Billa/Lidl/Kaufland supermarkets,no
|
||||
0x2e04,,66,replace,replace,bgm:unclassified=yes,amenity=marketplace,"samples markets and malls, mall override",no
|
||||
0x2e0a,,159,replace,replace,shop=car,shop=yes,"mixed automobile, winery, stationery, toys; shop=car incorrect globally",no
|
||||
0x2e0b,,52,replace,replace,bgm:unclassified=yes,shop=electronics,sampled technology/electronics shops,no
|
||||
0x2f08,,123,replace,replace,bgm:landmark=unknown;bgm:unclassified=yes,railway=station;station=subway,station names correspond to Sofia metro stations; source is metro,no
|
||||
0x2f0a,,4,replace,replace,amenity=place_of_worship;religion=jewish;garmin:legend=synagogue,amenity=place_of_worship;religion=jewish,sampled names all synagogues,no
|
||||
0x2f0b,,1499,replace,replace,leisure=track,amenity=parking,"source examples overwhelmingly parking, old leisure=track incorrect; name exceptions",no
|
||||
0x3001,,170,replace,replace,bgm:unclassified=yes,amenity=police,"mixed police/fire brigades, name exceptions",no
|
||||
0x3007,,132,replace,replace,man_made=pumping_station,amenity=courthouse,source courts/prosecutors and pump stations; name exceptions,no
|
||||
0x4700,,101,replace,replace,garmin:legend=port;bgm:legend=port;bgm:needs_geographic_review=yes,harbour=yes;garmin:legend=port;bgm:needs_geographic_review=yes,"source port symbol, inland samples mean do NOT map to leisure=marina",no
|
||||
0x6402,,6186,replace,replace,tourism=wilderness_hut;building=yes;garmin:legend=building,building=yes;garmin:legend=building,"generic building: source includes offices, substations, military buildings; NO blanket shelter tag",no
|
||||
0x6403,,1729,replace,replace,landuse=cemetery;garmin:legend=graveyard_tombstone,historic=wayside_shrine;garmin:legend=graveyard_tombstone,source mostly votive/obrok sites and individual graves; NOT every point a cemetery; name exceptions,no
|
||||
0x6405,,260,replace,replace,tourism=wilderness_hut;building=hut;garmin:legend=hunting_lodge,building=hut;garmin:legend=hunting_lodge;bgm:access=unknown,hunting lodge is not necessarily a public wilderness hut,no
|
||||
0x640a,,2095,replace,replace,hazard=yes;garmin:legend=danger;bgm:unclassified=yes,hazard=yes;garmin:legend=danger,"hazard is semantic, remove incorrect bgm:unclassified",no
|
||||
0x6413,,76,replace,replace,garmin:legend=tunnel;bgm:unclassified=yes,tunnel=yes;garmin:legend=tunnel,"point tunnel indicator only, does not create road segment tunnel",no
|
||||
0x6414,,32922,replace,replace,amenity=drinking_water;garmin:legend=drinking_water,amenity=drinking_water;man_made=water_tap;garmin:legend=drinking_water,"legend + v18 dual index; potable status implied by original symbol, inspect dry sources",no
|
||||
0x6419,,113,replace,replace,power=generator;generator:source=solar,power=substation,substations common; no global solar-generator claim,no
|
||||
0x6500,,31214,replace,replace,man_made=water_intake;garmin:legend=reservoir,man_made=water_intake;amenity=water_point;drinking_water=unknown;garmin:legend=reservoir;bgm:compat=water_point_unverified,PRIVATE-ONLY v18 category recovery: catchment does not prove public potable water; verify access/quality,no
|
||||
0x6502,,1864,replace,replace,ford=yes;garmin:legend=river_passage;bgm:unclassified=yes,ford=yes;garmin:legend=river_passage,"ford is semantic, remove incorrect bgm:unclassified",no
|
||||
0x6503,,760,replace,replace,bgm:trail_junction=yes;garmin:legend=crossroad;bgm:unclassified=yes,bgm:trail_junction=yes;garmin:legend=crossroad,do not fabricate guideposts where only a route intersection is indicated,no
|
||||
0x650b,,8767,replace,replace,trailblazed=yes;garmin:legend=winter_marking;bgm:unclassified=yes,tourism=information;information=trail_blaze;hiking=yes;garmin:legend=winter_marking,accurate point trail mark; not guaranteed searchable POI in stock OsmAnd,no
|
||||
0x6600,,66206,replace,append,bgm:spot_height=yes;garmin:legend=triangulation;bgm:unclassified=yes,bgm:spot_height=yes;garmin:legend=triangulation,"retain source ele metadata, DO NOT assign place=locality",no
|
||||
0x6601,,165,replace,replace,bgm:landmark=natural_phenomenon;garmin:legend=natural_phenomenon;bgm:unclassified=yes,tourism=attraction;bgm:landmark=natural_phenomenon;garmin:legend=natural_phenomenon,"natural phenomenon point, generic visitor interest provisional",no
|
||||
0x6603,,26366,replace,append,man_made=survey_point;garmin:legend=triangulation,man_made=survey_point;garmin:legend=triangulation,retain source elevation and annotation; APPEND mode only,no
|
||||
0x660a,,275,replace,replace,amenity=shelter;shelter_type=basic_hut;garmin:legend=refuge,amenity=shelter;garmin:legend=refuge,"source refuge; no assumption of heating, beds or 24h access",no
|
||||
0x660b,,2612,replace,replace,trailblazed=yes;garmin:legend=mark;bgm:unclassified=yes,tourism=information;information=trail_blaze;hiking=yes;garmin:legend=mark,accurate point blaze; trailblazed belongs on paths not isolated nodes,no
|
||||
0x661c,,1169,replace,replace,bgm:landmark=mound;bgm:unclassified=yes,bgm:landmark=mound,named mounds not all archaeological tumuli or hills; terrain annotation,no
|
||||
0x661d,,1412,replace,replace,tourism=wilderness_hut;building=agricultural,building=agricultural;bgm:landmark=livestock_building,"source is primarily agricultural/sheepfold, not public hiking hut",no
|
||||
0x661e,,979,replace,replace,amenity=shelter,amenity=shelter;garmin:legend=shelter,roofed shelter/canopy; do not assume overnight lodging,no
|
||||
0x6414,Сух,32922,,replace,,man_made=water_tap;drinking_water=no;garmin:legend=drinking_water;bgm:needs_review=yes,"V094 CONDITIONAL: named dry fountain, suppress implied potable water",yes
|
||||
0x6414,сух,32922,,replace,,man_made=water_tap;drinking_water=no;garmin:legend=drinking_water;bgm:needs_review=yes,"V094 CONDITIONAL: named dry fountain, suppress implied potable water",yes
|
||||
0x6414,(Сух,32922,,replace,,man_made=water_tap;drinking_water=no;garmin:legend=drinking_water;bgm:needs_review=yes,"V094 CONDITIONAL: named dry fountain, suppress implied potable water",yes
|
||||
0x6414,пресъх,32922,,replace,,man_made=water_tap;drinking_water=no;garmin:legend=drinking_water;bgm:needs_review=yes,"V094 CONDITIONAL: named dry fountain, suppress implied potable water",yes
|
||||
0x6500,Шах,31214,,replace,,man_made=water_intake;garmin:legend=reservoir;drinking_water=unknown,"V094 CONDITIONAL: water installation shaft, remove v18 water-point compatibility",yes
|
||||
0x6500,шах,31214,,replace,,man_made=water_intake;garmin:legend=reservoir;drinking_water=unknown,"V094 CONDITIONAL: water installation shaft, remove v18 water-point compatibility",yes
|
||||
0x6500,шах.,31214,,replace,,man_made=water_intake;garmin:legend=reservoir;drinking_water=unknown,"V094 CONDITIONAL: water installation shaft, remove v18 water-point compatibility",yes
|
||||
0x6500,Фонтан,31214,,replace,,amenity=fountain;garmin:legend=reservoir;drinking_water=unknown,"V094 CONDITIONAL: named fountain, do not assume potable",yes
|
||||
0x6500,фонтан,31214,,replace,,amenity=fountain;garmin:legend=reservoir;drinking_water=unknown,"V094 CONDITIONAL: named fountain, do not assume potable",yes
|
||||
0x6402,Заслон,6186,,replace,,amenity=shelter;building=yes;garmin:legend=building,V094 CONDITIONAL: building explicitly named a shelter; access unverified,yes
|
||||
0x661d,Заслон,1412,,replace,,amenity=shelter;building=hut;bgm:access=unknown,V094 CONDITIONAL: agricultural category explicitly named shelter; access unknown,yes
|
||||
0x6405,Заслон,260,,replace,,amenity=shelter;building=hut;garmin:legend=hunting_lodge;bgm:access=unknown,V094 CONDITIONAL: hunting hut specifically called shelter; access unknown,yes
|
||||
0x6402,заслон,6186,,replace,,amenity=shelter;building=yes;garmin:legend=building,V094 CONDITIONAL: building explicitly named a shelter; access unverified,yes
|
||||
0x661d,заслон,1412,,replace,,amenity=shelter;building=hut;bgm:access=unknown,V094 CONDITIONAL: agricultural category explicitly named shelter; access unknown,yes
|
||||
0x6405,заслон,260,,replace,,amenity=shelter;building=hut;garmin:legend=hunting_lodge;bgm:access=unknown,V094 CONDITIONAL: hunting hut specifically called shelter; access unknown,yes
|
||||
0x6402,Хижа,6186,,replace,,tourism=alpine_hut;building=yes;garmin:legend=building,V094 CONDITIONAL: building explicitly named mountain hut; independently confirm public availability,yes
|
||||
0x661d,Хижа,1412,,replace,,tourism=alpine_hut;building=yes,"V094 CONDITIONAL: agricultural category named hut, verify accommodation",yes
|
||||
0x6402,хижа,6186,,replace,,tourism=alpine_hut;building=yes;garmin:legend=building,V094 CONDITIONAL: building explicitly named mountain hut; independently confirm public availability,yes
|
||||
0x661d,хижа,1412,,replace,,tourism=alpine_hut;building=yes,"V094 CONDITIONAL: agricultural category named hut, verify accommodation",yes
|
||||
0x2c08,Стадион,1328,,replace,,leisure=stadium,V094 CONDITIONAL: stadium name,yes
|
||||
0x2c08,стадион,1328,,replace,,leisure=stadium,V094 CONDITIONAL: stadium name,yes
|
||||
0x2c08,Детска площадка,1328,,replace,,leisure=playground,V094 CONDITIONAL: children playground name,yes
|
||||
0x2c08,детска площадка,1328,,replace,,leisure=playground,V094 CONDITIONAL: children playground name,yes
|
||||
0x2c08,ледена пързалка,1328,,replace,,leisure=ice_rink,V094 CONDITIONAL: ice rink name,yes
|
||||
0x2c08,Ледена пързалка,1328,,replace,,leisure=ice_rink,V094 CONDITIONAL: ice rink name,yes
|
||||
0x2c08,игрище,1328,,replace,,leisure=pitch,V094 CONDITIONAL: sports pitch name,yes
|
||||
0x2c08,Игрище,1328,,replace,,leisure=pitch,V094 CONDITIONAL: sports pitch name,yes
|
||||
0x2c08,Колодрум,1328,,replace,,leisure=track,V094 CONDITIONAL: velodrome name,yes
|
||||
0x2c08,колодрум,1328,,replace,,leisure=track,V094 CONDITIONAL: velodrome name,yes
|
||||
0x2a07,Бистро,227,,replace,,amenity=restaurant,V094 CONDITIONAL: named bistro,yes
|
||||
0x2a07,бистро,227,,replace,,amenity=restaurant,V094 CONDITIONAL: named bistro,yes
|
||||
0x2a07,Хижа,227,,replace,,tourism=alpine_hut,V094 CONDITIONAL: named hiking hut,yes
|
||||
0x2a07,хижа,227,,replace,,tourism=alpine_hut,V094 CONDITIONAL: named hiking hut,yes
|
||||
0x2d02,Кафе,434,,replace,,amenity=cafe,V094 CONDITIONAL: explicit cafe,yes
|
||||
0x2d02,кафе,434,,replace,,amenity=cafe,V094 CONDITIONAL: explicit cafe,yes
|
||||
0x2d02,Ресторант,434,,replace,,amenity=restaurant,V094 CONDITIONAL: named restaurant,yes
|
||||
0x2d02,ресторант,434,,replace,,amenity=restaurant,V094 CONDITIONAL: named restaurant,yes
|
||||
0x2d02,Grill,434,,replace,,amenity=restaurant,V094 CONDITIONAL: named restaurant,yes
|
||||
0x2d02,грил,434,,replace,,amenity=restaurant,V094 CONDITIONAL: named restaurant,yes
|
||||
0x2d01,Кино,68,,replace,,amenity=cinema,V094 CONDITIONAL: explicit cinema,yes
|
||||
0x2d01,кино,68,,replace,,amenity=cinema,V094 CONDITIONAL: explicit cinema,yes
|
||||
0x2d01,Cinema,68,,replace,,amenity=cinema,V094 CONDITIONAL: explicit cinema,yes
|
||||
0x2d01,синема,68,,replace,,amenity=cinema,V094 CONDITIONAL: explicit cinema,yes
|
||||
0x2d03,театър,57,,replace,,amenity=theatre,V094 CONDITIONAL: explicit theatre,yes
|
||||
0x2d03,Театър,57,,replace,,amenity=theatre,V094 CONDITIONAL: explicit theatre,yes
|
||||
0x2e04,Мол,66,,replace,,shop=mall,V094 CONDITIONAL: named shopping mall,yes
|
||||
0x2e04,МОЛ,66,,replace,,shop=mall,V094 CONDITIONAL: named shopping mall,yes
|
||||
0x2e04,мол,66,,replace,,shop=mall,V094 CONDITIONAL: named shopping mall,yes
|
||||
0x2e04,Mall,66,,replace,,shop=mall,V094 CONDITIONAL: named shopping mall,yes
|
||||
0x2e06,Хранителни,348,,replace,,shop=convenience,V094 CONDITIONAL: named food/convenience store,yes
|
||||
0x2e06,хранителни,348,,replace,,shop=convenience,V094 CONDITIONAL: named food/convenience store,yes
|
||||
0x2e06,магазин,348,,replace,,shop=convenience,V094 CONDITIONAL: named food/convenience store,yes
|
||||
0x2e06,Минимаркет,348,,replace,,shop=convenience,V094 CONDITIONAL: named food/convenience store,yes
|
||||
0x3007,Помпена,132,,replace,,man_made=pumping_station,V094 CONDITIONAL: named water pumping station,yes
|
||||
0x3007,помпена,132,,replace,,man_made=pumping_station,V094 CONDITIONAL: named water pumping station,yes
|
||||
0x3007,Пречиствателна,132,,replace,,man_made=wastewater_plant,V094 CONDITIONAL: named treatment facility; subtype may need verification,yes
|
||||
0x3007,пречиствателна,132,,replace,,man_made=wastewater_plant,V094 CONDITIONAL: named treatment facility; subtype may need verification,yes
|
||||
0x3007,затвор,132,,replace,,amenity=prison,V094 CONDITIONAL: named prison,yes
|
||||
0x3007,Затвор,132,,replace,,amenity=prison,V094 CONDITIONAL: named prison,yes
|
||||
0x3001,Пожарна,170,,replace,,amenity=fire_station,V094 CONDITIONAL: named fire station,yes
|
||||
0x3001,пожарна,170,,replace,,amenity=fire_station,V094 CONDITIONAL: named fire station,yes
|
||||
0x6419,ВЕЦ,113,,replace,,power=plant,V094 CONDITIONAL: named generation site; source-specific classification,yes
|
||||
0x6419,фотоволтаична,113,,replace,,power=plant,V094 CONDITIONAL: named generation site; source-specific classification,yes
|
||||
0x6419,Фотоволтаична,113,,replace,,power=plant,V094 CONDITIONAL: named generation site; source-specific classification,yes
|
||||
0x2f0b,Екопътека,1499,,replace,,place=locality;bgm:needs_review=yes,V094 CONDITIONAL: not a confirmed parking lot; no false parking icon,yes
|
||||
0x2f0b,екопътека,1499,,replace,,place=locality;bgm:needs_review=yes,V094 CONDITIONAL: not a confirmed parking lot; no false parking icon,yes
|
||||
0x2f0b,Поляна,1499,,replace,,place=locality;bgm:needs_review=yes,V094 CONDITIONAL: not a confirmed parking lot; no false parking icon,yes
|
||||
0x2f0b,Подземен,1499,,replace,,amenity=parking;parking=underground,V094 CONDITIONAL: parking explicitly underground,yes
|
||||
0x2f0b,подземен,1499,,replace,,amenity=parking;parking=underground,V094 CONDITIONAL: parking explicitly underground,yes
|
||||
0x6403,Гроб,1729,,replace,,historic=tomb;garmin:legend=graveyard_tombstone,V094 CONDITIONAL: individual grave not a cemetery or shrine,yes
|
||||
0x6403,гроб,1729,,replace,,historic=tomb;garmin:legend=graveyard_tombstone,V094 CONDITIONAL: individual grave not a cemetery or shrine,yes
|
||||
0x641c,Автогара,3120,,replace,,amenity=bus_station;garmin:legend=bus_stop_station,V094 CONDITIONAL: bus station explicitly named,yes
|
||||
0x640e,Автогара,977,,replace,,amenity=bus_station;garmin:legend=bus_stop_station,V094 CONDITIONAL: bus station explicitly named,yes
|
||||
0x641c,автогара,3120,,replace,,amenity=bus_station;garmin:legend=bus_stop_station,V094 CONDITIONAL: bus station explicitly named,yes
|
||||
0x640e,автогара,977,,replace,,amenity=bus_station;garmin:legend=bus_stop_station,V094 CONDITIONAL: bus station explicitly named,yes
|
||||
0x2c06,ВМА,57,,replace,,amenity=hospital,V094 CONDITIONAL: Military Medical Academy hospital,yes
|
||||
0x2b00,Къщи за гости,1558,,replace,,tourism=guest_house,V094 CONDITIONAL: guest house explicitly named,yes
|
||||
0x2b00,къща за гости,1558,,replace,,tourism=guest_house,V094 CONDITIONAL: guest house explicitly named,yes
|
||||
0x2b00,Къща за гости,1558,,replace,,tourism=guest_house,V094 CONDITIONAL: guest house explicitly named,yes
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,264 @@
|
||||
code,source_count,current_before,revised,legacy_v18,native_registry_status,confirmed_indexable_tags,known_nonindexed_tags,other_tags_not_checked,rule_changed,sample1,sample2,sample3,basis
|
||||
0x200,2,place=city;capital=yes;garmin:legend=capital_city,place=city;capital=yes;garmin:legend=capital_city,,unknown_or_map_only,,,place=city,unchanged,"42.6954675,23.3326221 София","41.0612941,28.8116670 Истанбул",,LEGEND VERIFIED | Столица / Capital City | count=2 | original_default= | Exact concentric capital icon | V09 custom render/index mode; confidence=legend
|
||||
0x800,51,place=city;garmin:legend=large_city,place=city;garmin:legend=large_city,place=town,unknown_or_map_only,,,place=city,unchanged,"43.4086561,23.2226515 Монтана","42.9942226,21.9385600 Лесковац","40.8050680,22.0504189 Едеса",LEGEND VERIFIED | Град >75 000 / Large City | count=51 | original_default= | Exact medium city circle icon | V09 custom render/index mode; confidence=legend
|
||||
0x900,283,place=town;garmin:legend=town,place=town;garmin:legend=town,place=locality,unknown_or_map_only,,,place=town,unchanged,"43.8304281,21.9588590 Болиевац","43.7735653,23.7220788 Козлодуй","43.6919832,23.8554382 Мизия",LEGEND VERIFIED | Град <75 000 / Town | count=283 | original_default= | Exact small city circle icon | V09 custom render/index mode; confidence=legend
|
||||
0xd00,6610,place=locality,place=locality,place=village,nonindexed_known_type,,place=locality,,unchanged,"43.9936996,21.5700674 Сенски рудник","43.9523935,21.5941644 Сисевац","43.8554907,21.5954947 Извор","count=6610 default= icon=no example=(43.5990930,27.4806476) Бенковски | V09 custom render/index mode; confidence=uncertain"
|
||||
0xe00,114,place=neighbourhood,place=neighbourhood,,unknown_or_map_only,,,place=neighbourhood,unchanged,"42.7199936,23.2214499 кв. Филиповци","42.7358937,23.2393670 кв Република","43.4697032,25.6756711 Гара Бяла","count=114 default= icon=no example=(42.1879506,24.3242025) Вароша | V09 custom render/index mode; confidence=uncertain"
|
||||
0x1100,8430,place=locality,place=locality,place=village,nonindexed_known_type,,place=locality,,unchanged,"43.4009099,23.0571914 Илица (Сръбляница)","43.4326673,23.2169008 кв.Кошарник","43.3940649,23.2331228 Мала Кутловица","count=8430 default= icon=no example=(42.5309944,22.8053427) Боневците | V09 custom render/index mode; confidence=uncertain"
|
||||
0x2800,54902,place=locality,place=locality,place=locality,nonindexed_known_type,,place=locality,,unchanged,"43.8014817,23.5094976 Овощната градина","43.8289905,23.5180807 о-в Цибър","43.7958598,23.5537434 Страането","count=54902 default=place=locality icon=no example=(42.4072909,23.6418056) Атовица | V09 custom render/index mode; confidence=source-inferred"
|
||||
0x2a07,227,bgm:landmark=unknown;bgm:unclassified=yes,amenity=fast_food,,confirmed_native_poi,amenity=fast_food,,,changed,"42.9683876,23.2961011 Бистро 'Свидня'","42.7317739,23.2534862 Баничарница","42.0257092,23.9940548 Бистро""Иглика""","count=227 default= icon=no example=(43.9981198,22.8685999) При Неков грил | V09 custom render/index mode; confidence=uncertain | V094 REVIEW: snacks/bakeries and kiosks from sampled names; bistro exceptions"
|
||||
0x2a0e,525,shop=yes,amenity=cafe,,confirmed_native_poi,amenity=cafe,,,changed,"43.4095573,23.2264066 Неделя","43.4136343,23.2262778 Виенска сладкарница Валка","42.8555632,23.0387592 Руми-кафене","count=525 default= icon=yes example=(42.2905827,25.9344506) Клуб Бекс | V09 custom render/index mode; confidence=uncertain | native-tag candidate | V094 REVIEW: sampled names cafe/tea/confectionery"
|
||||
0x2b00,1558,tourism=hotel,tourism=hotel,,unknown_or_map_only,,,tourism=hotel,unchanged,"42.7394986,22.3281884 Хотел","42.9889226,23.2392812 Къщи за гости ""Искрец""","42.9868841,23.2958865 Борисови къщи","count=1558 default=tourism=hotel icon=no example=(42.8339124,26.4551425) Къща за гости Кенара | V09 custom render/index mode; confidence=source-inferred | native-tag candidate"
|
||||
0x2c01,20,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.6830006,23.2853937 Виваком","42.6989007,23.2976031 Национален дворец на децата","42.0202160,23.9878106 Каса ВИК","count=20 default= icon=yes example=(42.0277262,23.9895701) Виваком | V09 custom render/index mode; confidence=unknown"
|
||||
0x2c02,538,amenity=kindergarten,amenity=kindergarten,,unknown_or_map_only,,,amenity=kindergarten,unchanged,"43.7792730,23.7294173 ОДЗ","43.7086129,23.8299036 ДГ “Здравец","43.6949444,23.8096046 ОДЗ Радост","count=538 default= icon=yes example=(43.1070256,26.4204884) ОДЗ | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: sampled nursery/kindergarten"
|
||||
0x2c03,474,amenity=community_centre,amenity=community_centre,,unknown_or_map_only,,,amenity=community_centre,unchanged,"43.7766767,23.7306619 Читалище Христо Ботев","43.7377739,23.9583063 Читалище Надежда","43.6636162,23.0670619 Читалище Просвета","count=474 default= icon=yes example=(42.2885871,23.2639360) Читалище ""Просветен лъч"" | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: Bulgarian chitalishte/cultural centre"
|
||||
0x2c05,2481,amenity=school;garmin:legend=school,amenity=school;garmin:legend=school,,unknown_or_map_only,,,amenity=school,unchanged,"43.7642956,23.5052490 ОУ ""Христо Ботев""","43.7817407,23.7193751 ОУ Христо Ботев","43.7785006,23.7257481 СОУ Св.св. Кирил и Методий",LEGEND VERIFIED | Училище / School | count=2481 | original_default= | Exact yellow school icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x2c06,57,bgm:unclassified=yes,amenity=university,,confirmed_native_poi,amenity=university,,,changed,"42.6784301,23.2531214 Нов български университет","42.6830435,23.3043623 ВМА","42.6757264,23.3363342 Софийска духовна семинария","count=57 default= icon=yes example=(42.1340704,24.7654796) Аграрен университет | V09 custom render/index mode; confidence=unknown | V094 REVIEW: sampled names universities/faculties, one medical exception"
|
||||
0x2c07,20,bgm:unclassified=yes,tourism=zoo,,confirmed_native_poi,tourism=zoo,,,changed,"42.0310521,23.5822821 Парк за танцуващи мечки","43.6728430,26.9188857 Детска градина Радост","42.8736520,25.3300309 зоопарк","count=20 default= icon=yes example=(42.6421452,26.9801474) Зоопарк | V09 custom render/index mode; confidence=unknown | V094 REVIEW: sampled names zoos/animal parks"
|
||||
0x2c08,1328,leisure=horse_riding,leisure=sports_centre,,confirmed_native_poi,leisure=sports_centre,,,changed,"43.6659551,23.0704093 стадион Христо Ботев","43.3688951,23.4793496 Стадион","42.8574944,23.0329871 спортен к-с Арена","default= icon=yes example=(43.3452487,28.0663562) ледена пързалка | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: sports venues of varied kinds: not horse-riding by default; name overrides for specific sports"
|
||||
0x2c0b,1606,amenity=place_of_worship;garmin:legend=church,amenity=place_of_worship;garmin:legend=church,,confirmed_native_poi,amenity=place_of_worship,,,unchanged,"43.7093425,23.8309550 Успение Богородично""","43.6585093,23.0598307 църква Св. Преподобна Параскева","43.3864474,23.0818033 Балова шума",LEGEND VERIFIED | Църква / Church | count=1606 | original_default=amenity=place_of_worship | Exact yellow church icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x2d01,68,bgm:unclassified=yes,amenity=theatre,,confirmed_native_poi,amenity=theatre,,,changed,"43.4085703,23.2221365 Драматичен тетър Драгомир Асенов","43.4099221,23.2263422 Младежки дом","43.4091926,23.2249045 Кино 77","count=68 default= icon=no example=(42.0212245,23.0970383) Синемакс | V09 custom render/index mode; confidence=unknown | V094 REVIEW: sampled names drama theatres/opera, cinemas as exceptions"
|
||||
0x2d02,434,bgm:landmark=unknown;bgm:unclassified=yes,amenity=bar,,confirmed_native_poi,amenity=bar,,,changed,"43.4061241,23.2251406 The hub","42.8491044,23.0336738 Пуул бар Стефанови","42.8465939,23.2246685 кафе-бар Мокамбо","count=434 default= icon=no example=(43.0718994,25.6072640) Bar Grill Kolata | V09 custom render/index mode; confidence=uncertain | V094 REVIEW: sampled names bars/clubs, cafe/restaurant exceptions"
|
||||
0x2d03,57,bgm:unclassified=yes,amenity=cinema,,confirmed_native_poi,amenity=cinema,,,changed,"42.6818419,23.3006716 Кино Урвич","42.7000809,23.2987404 Аура","42.7050805,23.2901573 Комплекс Арена","count=57 default= icon=no example=(42.6926780,23.3266354) БИАД | V09 custom render/index mode; confidence=unknown | V094 REVIEW: sampled names cinemas, theatre exception"
|
||||
0x2d09,337,leisure=swimming_pool,leisure=swimming_pool,,unknown_or_map_only,,,leisure=swimming_pool,unchanged,"43.3955455,23.2254410 Воден парк Сините eзера","42.7062607,23.1467128 Басейн","42.6750612,23.3128595 Спартак","count=337 default=leisure=swimming_pool icon=yes example=(42.0240998,24.3614316) р-т и басейн Корал | V09 custom render/index mode; confidence=source-inferred | native-tag candidate"
|
||||
0x2e02,477,shop=convenience,shop=convenience,,confirmed_native_poi,shop=convenience,,,unchanged,"42.9882789,23.2408905 Хранителни стоки","42.8440404,23.3217859 Минимаркет ""Кътински shop""","42.8432679,23.3223653 бакалия","count=477 default= icon=no example=(42.4623299,27.4146438) Chicken group, Чикън груп | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: sampled groceries and convenience shops"
|
||||
0x2e03,502,shop=yes,shop=supermarket,,confirmed_native_poi,shop=supermarket,,,changed,"43.7786508,23.7220788 Корона","43.4187412,23.2226300 Т маркет","43.4215951,23.2272863 Kaufland","count=502 default= icon=no example=(43.1818700,27.8989434) Национална верига Ardes.bg Варна, Аспарухово | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: sampled names Billa/Lidl/Kaufland supermarkets"
|
||||
0x2e04,66,bgm:unclassified=yes,amenity=marketplace,,confirmed_native_poi,amenity=marketplace,,,changed,"42.8081203,23.2002282 пазар","42.6982570,23.3085465 Мол София","42.6786232,23.3206272 Ситицентър","count=66 default= icon=no example=(42.0116758,24.8763728) Пазар Асеница | V09 custom render/index mode; confidence=unknown | V094 REVIEW: samples markets and malls, mall override"
|
||||
0x2e05,487,amenity=pharmacy;garmin:legend=pharmacy,amenity=pharmacy;garmin:legend=pharmacy,,unknown_or_map_only,,,amenity=pharmacy,unchanged,"42.6773357,23.2396674 Рр-2000","42.7196932,23.2453108 Ник","42.7156806,23.2362986 Кимекс",LEGEND VERIFIED | Аптека / Pharmacy | count=487 | original_default= | Exact yellow pharmacy cup icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x2e06,348,shop=yes,shop=yes,,unknown_or_map_only,,,shop=yes,unchanged,"42.9598904,23.3508611 Магазин Европа","42.2021770,23.9957070 Винарска изба ""Белово"" - фирмен магазин","43.0294776,25.0923443 Technomarket","count=348 default= icon=no example=(41.7380476,25.6744480) Хранителни стоки | V09 custom render/index mode; confidence=uncertain | native-tag candidate | V094 REVIEW: mixed shops, groceries conditional; do not assert convenience for electronics"
|
||||
0x2e07,105,shop=clothes;garmin:legend=clothes_shop,shop=clothes;garmin:legend=clothes_shop,,unknown_or_map_only,,,shop=clothes,unchanged,"42.7464509,23.2299042 Спортдепот","42.6972270,23.3167005 Стената","42.6936221,23.3450890 Екипировка",LEGEND VERIFIED | Магазин за дрехи / Clothes shop | count=105 | original_default= | Exact clothes-on-hanger icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x2e08,13,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.7426529,23.2318783 Техномаркет","42.7295208,23.2671332 Практикер","42.9759622,25.4808569 Строителни материали","count=13 default= icon=no example=(42.9759622,25.4808569) Строителни материали | V09 custom render/index mode; confidence=unknown"
|
||||
0x2e09,8,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.7395844,23.2375860 Аико","43.2233047,27.8732157 JYSK","43.2210302,27.8725934 Мьомакс","count=8 default= icon=no example=(43.2325959,27.8543973) мебелна къша Явор | V09 custom render/index mode; confidence=unknown"
|
||||
0x2e0a,159,shop=car,shop=yes,,unknown_or_map_only,,,shop=yes,changed,"42.7732301,23.1881905 Citroen","42.6727653,23.3678555 Борса цветя","42.4852467,27.4827290 Травъл фрий шоп","count=159 default= icon=no example=(43.2042933,27.9221392) Сиела | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: mixed automobile, winery, stationery, toys; shop=car incorrect globally"
|
||||
0x2e0b,52,bgm:unclassified=yes,shop=electronics,,confirmed_native_poi,shop=electronics,,,changed,"42.7304864,23.2681203 Хит","42.7195430,23.2573700 Технополис","42.6891804,23.3303905 Геотрейд","count=52 default= icon=no example=(42.1432328,24.7814441) Зора | V09 custom render/index mode; confidence=unknown | V094 REVIEW: sampled technology/electronics shops"
|
||||
0x2f00,1269,landuse=industrial,landuse=industrial,,unknown_or_map_only,,,landuse=industrial,unchanged,"43.7260580,23.9511180 хлебозавод","43.7253070,23.9496803 Индустриални сгради","43.7238479,23.9513326 Промишлени сгради","count=1269 default= icon=yes example=(41.9971061,24.0856361) Завод Никола Божанов | V09 custom render/index mode; confidence=inferred | V094 REVIEW: true use but area tag on point may not draw POI icon"
|
||||
0x2f01,778,amenity=fuel,amenity=fuel,,unknown_or_map_only,,,amenity=fuel,unchanged,"43.4021115,23.2375646 Лукойл","42.8660774,23.0253911 K&A","42.7358723,23.2465553 Петрол","count=778 default= icon=no example=(41.4071703,23.3677912) Бензиностанция | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: gas station"
|
||||
0x2f03,106,shop=car_repair,shop=car_repair,,unknown_or_map_only,,,shop=car_repair,unchanged,"42.7313876,23.2313204 Субару сервиз","42.7292633,23.2440662 Дияна-гуми","42.7489614,23.2238960 Волво","count=106 default= icon=no example=(42.7011108,23.3444452) Автоклиматици | V09 custom render/index mode; confidence=inferred | native-tag candidate | V094 REVIEW: sampled names vehicle servicing/tyre changes"
|
||||
0x2f05,486,amenity=post_office,amenity=post_office,,unknown_or_map_only,,,amenity=post_office,unchanged,"43.7776208,23.7287307 Козлодуй","43.7751102,23.7318420 поща","43.6916399,23.8570476 Мизия","count=486 default=amenity=post_office icon=no example=(43.1468940,23.9251757) Поща | V09 custom render/index mode; confidence=source-inferred | native-tag candidate | V094 REVIEW: old and sampled data postal services"
|
||||
0x2f06,320,amenity=bank,amenity=bank,,unknown_or_map_only,,,amenity=bank,unchanged,"42.2650051,23.6065292 ПИБ","42.0189929,23.6683917 Банка ДСК","42.0190787,23.6684132 УниКредит Булбанк","count=320 default= icon=no example=(43.8561344,25.9691691) SG Expressbank | V09 custom render/index mode; confidence=inferred | native-tag candidate"
|
||||
0x2f08,123,bgm:landmark=unknown;bgm:unclassified=yes,railway=station;station=subway,station=subway,confirmed_native_poi,railway=station,,,changed,"42.6826143,23.2418561 Горна баня","42.7273107,23.2627988 Сливница","42.7127409,23.2683349 Западен парк","count=123 default= icon=yes example=(42.7127409,23.2683349) Западен парк | V09 custom render/index mode; confidence=uncertain | V094 REVIEW: station names correspond to Sofia metro stations; source is metro"
|
||||
0x2f0a,4,amenity=place_of_worship;religion=jewish;garmin:legend=synagogue,amenity=place_of_worship;religion=jewish,,confirmed_native_poi,amenity=place_of_worship,,,changed,"42.1920061,24.3338370 Синагога",,,LEGEND VERIFIED | Синагога / Synagogue | count=4 | original_default= | Exact yellow star icon | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: sampled names all synagogues
|
||||
0x2f0b,1499,leisure=track,amenity=parking,,nonindexed_known_type,,amenity=parking,,changed,"43.7308860,23.9443588 автополигон","42.6845670,23.3202195 Подземен паркинг НДК","42.6754689,23.3230519 Подземен частен","count=1499 default= icon=no example=(42.7493906,24.9058342) Платен паркинг | V09 custom render/index mode; confidence=inferred | V094 REVIEW: source examples overwhelmingly parking, old leisure=track incorrect; name exceptions"
|
||||
0x2f0c,320,amenity=place_of_worship;religion=muslim;garmin:legend=mosque,amenity=place_of_worship;religion=muslim;garmin:legend=mosque,,confirmed_native_poi,amenity=place_of_worship,,,unchanged,"42.6995230,23.3225584 Джамия","42.0163536,23.6689067 Централна джамия","43.7385893,26.7519450 Демир Баба теке",LEGEND VERIFIED | Джамия / Mosque | count=320 | original_default= | Exact crescent icon | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: mosque
|
||||
0x2f0e,4,amenity=car_wash;garmin:legend=car_wash,amenity=car_wash;garmin:legend=car_wash,,unknown_or_map_only,,,amenity=car_wash,unchanged,"42.2939301,22.7035904 Олимпия",,,LEGEND VERIFIED | Автомивка / Car Wash | count=4 | original_default= | Exact blue/red carwash icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x3001,170,bgm:unclassified=yes,amenity=police,,confirmed_native_poi,amenity=police,,,changed,"43.7287402,23.9509678 пожарна","43.7371087,23.9729834 гранична полиция","43.7335038,23.9619327 Полиция","count=170 default= icon=yes example=(42.6446557,26.9834948) Пожарна Команда | V09 custom render/index mode; confidence=unknown | V094 REVIEW: mixed police/fire brigades, name exceptions"
|
||||
0x3002,564,emergency=first_aid,emergency=first_aid,,unknown_or_map_only,,,emergency=first_aid,unchanged,"43.7720633,23.7260699 МБАЛ","43.4031200,23.2344532 МБАЛ - Монтана","43.4017682,23.2388520 МБАЛ ""Сити клиник - Свети Георги""","count=564 default= icon=no example=(42.6900816,23.3072805) Институт Пирогов | V09 custom render/index mode; confidence=inferred | native-tag candidate"
|
||||
0x3003,841,amenity=townhall,amenity=townhall,,unknown_or_map_only,,,amenity=townhall,unchanged,"43.7781787,23.7274003 Община","43.7368941,23.9586926 община","43.4080768,23.2249045 община Монтана","count=841 default= icon=no example=(43.1841660,27.8940296) община Аспарухово | V09 custom render/index mode; confidence=inferred | native-tag candidate"
|
||||
0x3006,85,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.7977347,22.4487162 ГКПП Стрезимировци","43.7362504,28.5582304 ГКПП Дуранкулак","43.7391043,28.5608482 ГКПП Вама Веке","count=85 default= icon=yes example=(43.9623928,27.7865267) Крушари-Добромир | V09 custom render/index mode; confidence=unknown"
|
||||
0x3007,132,man_made=pumping_station,amenity=courthouse,,confirmed_native_poi,amenity=courthouse,,,changed,"43.7364006,23.9589500 Съдебна палата","43.4064031,23.2261705 Окръжен съд","43.4095144,23.2255697 Административен съд","count=132 default= icon=no example=(43.1769776,27.4429464) Районен съд | V09 custom render/index mode; confidence=inferred | V094 REVIEW: source courts/prosecutors and pump stations; name exceptions"
|
||||
0x3008,37,amenity=fire_station;garmin:legend=fire_station,amenity=fire_station;garmin:legend=fire_station,,confirmed_native_poi,amenity=fire_station,,,unchanged,"42.7032137,23.2955003 Главна дирекция „Пожарна безопасност и защита на населението“","42.3052597,23.8637638 Пожарна","43.0599260,27.0125270 Пожарна команда",LEGEND VERIFIED | Пожарна / Fire Station | count=37 | original_default= | Exact firefighter icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x4700,101,garmin:legend=port;bgm:legend=port;bgm:needs_geographic_review=yes,harbour=yes;garmin:legend=port;bgm:needs_geographic_review=yes,,confirmed_native_poi,harbour=yes,,,changed,"42.0604706,27.9666424 Пристан разходки","41.6431189,25.2845192 Пристан",," | Пристанище / Port | count=101 | original_default= | Exact anchor; functional port use not independently confirmed | V09 custom render/index mode; confidence=legend | V094 REVIEW: source port symbol, inland samples mean do NOT map to leisure=marina"
|
||||
0x4e00,14,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.0110536,23.6421061 Минерална баня","42.7165604,24.9186015 Тоалетна","41.8874574,23.4654880 Турска баня","count=14 default= icon=no example=(41.9877291,24.0870094) Минерална баня | V09 custom render/index mode; confidence=unknown"
|
||||
0x6400,465,landuse=quarry;garmin:legend=quarry,landuse=quarry;garmin:legend=quarry,man_made=mineshaft,unknown_or_map_only,,,landuse=quarry,unchanged,"42.8594899,23.0157137 Козяк (Кам.)","42.8529453,23.0758381 (Кам.)","42.8817630,23.1405544 (Пяс.)",LEGEND VERIFIED | Кариера / Quarry | count=465 | original_default= | Exact crossed mining tools icon; point vs area geometry still to assess | V09 custom render/index mode; confidence=legend
|
||||
0x6401,1679,man_made=bridge;garmin:legend=bridge,man_made=bridge;garmin:legend=bridge,building=bridge,unknown_or_map_only,,,man_made=bridge,unchanged,"42.8029060,22.2878051 Гробище","42.7980566,22.4442744 Ратко Павлович","42.7375460,25.2069068 Къркеланов мост",т / Bridge | count=1679 | original_default= | Exact bridge-icon depiction; original route connectivity not established | V09 custom render/index mode; confidence=legend | V094 REVIEW: bridge node may be excluded from nearby POI search by stock OsmAnd
|
||||
0x6402,6186,tourism=wilderness_hut;building=yes;garmin:legend=building,building=yes;garmin:legend=building,tourism=wilderness_hut,confirmed_native_poi,building=yes,,,changed,"43.4945512,23.4724188 Помпена станция","42.8253722,22.4400902 Бивша гранична застава ""Рудина""","42.8362083,22.4799800 Гранична застава ""Барнос""","riginal_default= | Exact yellow building icon; original building footprint not present | V09 custom render/index mode; confidence=legend | V094 REVIEW: generic building: source includes offices, substations, military buildings; NO blanket shelter tag"
|
||||
0x6403,1729,landuse=cemetery;garmin:legend=graveyard_tombstone,historic=wayside_shrine;garmin:legend=graveyard_tombstone,amenity=grave_yard;cemetery=grave,confirmed_native_poi,historic=wayside_shrine,,,changed,"43.6168599,23.0250049 Св. Троица","43.6162162,23.0212283 Св. Трифон Зарезан","43.4605622,23.0369997 Първанов гроб",efault= | Exact grave cross; ambiguous between graveyard and religious votive feature | V09 custom render/index mode; confidence=legend | V094 REVIEW: source mostly votive/obrok sites and individual graves; NOT every point a cemetery; name exceptions
|
||||
0x6404,1507,amenity=place_of_worship;garmin:legend=church,amenity=place_of_worship;garmin:legend=church,building=monastery,confirmed_native_poi,amenity=place_of_worship,,,unchanged,"43.7750244,23.7308335 църква Св. Троица","43.7364864,23.9629841 Успение богородично","43.7357569,23.9557743 Храм Св. Георги",LEGEND VERIFIED | Църква / Church | count=1507 | original_default=amenity=place_of_worship | Exact church icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6405,260,tourism=wilderness_hut;building=hut;garmin:legend=hunting_lodge,building=hut;garmin:legend=hunting_lodge;bgm:access=unknown,tourism=hunting_lodge,unknown_or_map_only,,,building=hut,changed,"42.8412509,22.4996567 Фургон","42.6693535,23.0688429 Ловна хижа","42.8713346,23.3238673 ловен заслон",жа / Hunting Lodge | count=260 | original_default=tourism=alpine_hut | Exact hunting lodge icon; do NOT infer tourist/alpine hut | V09 custom render/index mode; confidence=legend | V094 REVIEW: hunting lodge is not necessarily a public wilderness hut
|
||||
0x6406,854,barrier=gate;garmin:legend=barrier_gate,barrier=gate;garmin:legend=barrier_gate,highway=crossing;railway=crossing,unknown_or_map_only,,,barrier=gate,unchanged,"42.9024911,23.1878471 вход поделение","42.7168393,23.0642295 КПП","42.9902101,23.2793427 Вход болница",LEGEND VERIFIED | Бариера / Barrier/Gate | count=854 | original_default= | Exact black/red barrier icon | V09 custom render/index mode; confidence=legend
|
||||
0x6407,2861,tourism=picnic_site;garmin:legend=picnic_site,tourism=picnic_site;garmin:legend=picnic_site,amenity=bench,unknown_or_map_only,,,tourism=picnic_site,unchanged,"42.7001882,23.1456399 пейка","42.9902101,23.2794499 Беседка","42.9396129,23.3048558 Маса и пейки","LEGEND VERIFIED | Пикник / Picnic Site | count=2861 | original_default=tourism=picnic_site | Exact picnic bench icon | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: picnic location, not necessarily bench"
|
||||
0x6408,12,emergency=mountain_rescue;garmin:legend=mountain_rescue,emergency=mountain_rescue;garmin:legend=mountain_rescue,emergency=mountain_rescue,unknown_or_map_only,,,emergency=mountain_rescue,unchanged,"42.2645974,23.6044264 ПСС","43.2466722,26.5717864 НАП","42.6026416,23.2414269 ПСС бунгало",LEGEND VERIFIED | ПСС / Mountain Rescue | count=12 | original_default=emergency=mountain_rescue | Exact shield with red cross | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6409,1235,historic=monument;garmin:legend=monument,historic=monument;garmin:legend=monument,historic=memorial,unknown_or_map_only,,,historic=monument,unchanged,"43.7648320,23.5042834 Паметник на загиналите в I-та св. война","43.7374949,23.9585638 Т. Паница","43.7371731,23.9577913 Д.Илиев",LEGEND VERIFIED | Паметник / Monument | count=1235 | original_default= | Exact obelisk icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x640a,2095,hazard=yes;garmin:legend=danger;bgm:unclassified=yes,hazard=yes;garmin:legend=danger,trail_visibility=bad,confirmed_native_poi,hazard=yes,,,changed,"43.3950090,23.1846285 Залят път","42.8981352,23.1942201 обрасло","42.6692033,23.1037331 липсваща маркировка","LEGEND VERIFIED | Опасност / Danger | count=2095 | original_default= | Exact danger triangle, but specific hazard is unspecified | V09 custom render/index mode; confidence=legend | V094 REVIEW: hazard is semantic, remove incorrect bgm:unclassified"
|
||||
0x640b,1128,amenity=place_of_worship;garmin:legend=chapel,amenity=place_of_worship;garmin:legend=chapel,building=church,confirmed_native_poi,amenity=place_of_worship,,,unchanged,"43.4045148,23.2220721 Св.св.Кирил иМетодий","42.8771925,23.0749369 Св. Иван","42.9306006,23.2061505 Св. Георги",LEGEND VERIFIED | Параклис / Chapel | count=1128 | original_default=amenity=place_of_worship | Exact small chapel icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x640c,1353,man_made=mine_shaft;garmin:legend=mine_shaft,man_made=mine_shaft;garmin:legend=mine_shaft,man_made=mineshaft,unknown_or_map_only,,,man_made=mine_shaft,unchanged,"42.7414727,22.2189260 (Мол.)","42.9051089,23.2066226 (Въгл.)","42.9152369,23.2671762 Стаханов",LEGEND VERIFIED | Мина / Mine Shaft | count=1353 | original_default= | Exact crossed tools icon | V09 custom render/index mode; confidence=legend | V094 REVIEW: accurate physical mine opening; POI registry may not index
|
||||
0x640d,137,aerialway=station;garmin:legend=lift,aerialway=station;garmin:legend=lift,piste=type=connection,unknown_or_map_only,,,aerialway=station,unchanged,"42.2483540,23.6099410 Лифт(Горна)","42.2578382,23.5882044 Влек котва парк","42.2452641,23.5911226 Кабинков лифт(междинна)",LEGEND VERIFIED | Лифт / Lift | count=137 | original_default=aerialway=station | Exact chairlift icon; unknown whether point is aerialway station or line | V09 custom render/index mode; confidence=legend
|
||||
0x640e,977,highway=bus_stop;garmin:legend=bus_stop_station,highway=bus_stop;garmin:legend=bus_stop_station,highway=bus_stop,unknown_or_map_only,,,highway=bus_stop,unchanged,"43.7789941,23.7224865 Автогара","43.7361646,23.9557743 автогара","43.6651397,23.0754089 Брусарци",LEGEND VERIFIED | Спирка / Bus Stop/Station | count=977 | original_default= | Exact bus stop icon; may be terminal rather than stop | V09 custom render/index mode; confidence=legend | V094 REVIEW: stop; amenity=bus_station only if named accordingly
|
||||
0x640f,2766,landuse=cemetery;garmin:legend=graveyard_tombstone,landuse=cemetery;garmin:legend=graveyard_tombstone,cemetery=grave,unknown_or_map_only,,,landuse=cemetery,unchanged,"42.7093506,22.0759535 Гробище","42.8418732,23.2311702 гробищен парк","42.1422029,22.1689725 Гробише","LEGEND VERIFIED | Гробище, оброк / Graveyard/Tombstone | count=2766 | original_default= | Exact two-cross marker; ambiguous cemetery versus religious site | V09 custom render/index mode; confidence=legend | V094 REVIEW: actual cemetery symbol"
|
||||
0x6410,98,bgm:unclassified=yes,bgm:unclassified=yes,power=generator,provenance_only,,,,unchanged,"42.9985785,23.3555603 МВЕЦ Церово","42.0395923,23.5535288 ВЕЦ","42.2469592,23.5145831 МВЕЦ Роса","count=98 default= icon=yes example=(42.0124269,26.4874792) ВЕЦ ""Устрем"" | V09 custom render/index mode; confidence=unknown"
|
||||
0x6411,875,man_made=tower;garmin:legend=tower,man_made=tower;garmin:legend=tower,man_made=tower,unknown_or_map_only,,,man_made=tower,unchanged,"43.3890224,23.2158065 НИМХ Монтана","43.4105015,23.2060218 РРС Монтана","42.8131843,22.4631572 Кула",LEGEND VERIFIED | Кула / Tower | count=875 | original_default=man_made=tower | Exact tower icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6413,76,garmin:legend=tunnel;bgm:unclassified=yes,tunnel=yes;garmin:legend=tunnel,tunnel=building_passage,provenance_only,,,,changed,"42.6887298,23.3397245 Подлез","42.0839453,23.4567547 Тунел вода","42.7647972,24.7336578 Галерия","VERIFIED | Тунел / Tunnel | count=76 | original_default= | Exact tunnel portal icon; avoid tunnel=yes on standalone node | V09 custom render/index mode; confidence=legend | V094 REVIEW: point tunnel indicator only, does not create road segment tunnel"
|
||||
0x6414,32922,amenity=drinking_water;garmin:legend=drinking_water,amenity=drinking_water;man_made=water_tap;garmin:legend=drinking_water,amenity=drinking_water;drinking_water=yes;man_made=water_tap,confirmed_native_poi,amenity=drinking_water;man_made=water_tap,,,changed,"43.6806107,23.9911795 (Сух.)","43.4345984,23.0224943 Стублата","43.4433532,23.0482221 Лековница","drinking_water | Exact tap/fountain icon; source may include dry fountains | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: legend + v18 dual index; potable status implied by original symbol, inspect dry sources"
|
||||
0x6415,264,tourism=camp_site;garmin:legend=camp_site,tourism=camp_site;garmin:legend=camp_site,tourism=picnic_site,unknown_or_map_only,,,tourism=camp_site,unchanged,"42.1667075,23.6539721 бивак ""Заврачица""","42.0007753,23.7665820 Бивак","42.0310307,23.9641428 Къмпинг",LEGEND VERIFIED | Място за бивакуване / Camp Site | count=264 | original_default=amenity=shelter | Exact blue tent icon; corrects shelter | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6416,2886,historic=ruins;garmin:legend=ruins,historic=ruins;garmin:legend=ruins,tourism=camp_site,unknown_or_map_only,,,historic=ruins,unchanged,"42.7102089,22.4757314 Граничен пост","42.6776576,23.1390738 стар заслон","42.6733017,23.1490946 крепост",LEGEND VERIFIED | Руини / Ruins | count=2886 | original_default= | Exact ruin block icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6417,8868,man_made=water_well;garmin:legend=well,man_made=water_well;garmin:legend=well,amenity=watering_place,unknown_or_map_only,,,man_made=water_well,unchanged,"43.7240410,23.5418987 Миленков геран","43.7055659,23.5351396 148","43.7627506,23.5696864 Левянски геран",VERIFIED | Кладенец / Well | count=8868 | original_default= | Exact turquoise well-circle icon; no automatic drinking-water claim | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: well; do not infer potable water
|
||||
0x6418,305,power=generator;generator:source=wind,power=generator;generator:source=wind,amenity=grit_bin;fixme=Маркировка,unknown_or_map_only,,,power=generator,unchanged,"43.1920409,26.5263820 Ветрогенератор",,,"count=305 default= icon=yes example=(42.7765560,26.3197017) Ветрогенератор | V09 custom render/index mode; confidence=inferred"
|
||||
0x6419,113,power=generator;generator:source=solar,power=substation,fixme=unknown-from-bg-mountains,nonindexed_known_type,,power=substation,,changed,"42.0435405,23.5456109 трафпост","42.0130706,24.2570615 Подстанция ВЕЦ Пещера","42.1499705,24.3179584 Подстанция Алеко","count=113 default= icon=yes example=(42.7218604,23.6192322) Подстанция Столник | V09 custom render/index mode; confidence=inferred | V094 REVIEW: substations common; no global solar-generator claim"
|
||||
0x641a,62,amenity=place_of_worship;garmin:legend=church,amenity=place_of_worship;garmin:legend=church,amenity=monastery,confirmed_native_poi,amenity=place_of_worship,,,unchanged,"42.9599547,23.0566549 Св. Никола","42.6823997,23.1001067 Дивотински ман. Св. Троица 805м","42.9840946,23.2748580 Ман. Успение Богородично",LEGEND VERIFIED | Църква / Church | count=62 | original_default=amenity=place_of_worship | Exact yellow church icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x641c,3120,highway=bus_stop;garmin:legend=bus_stop_station,highway=bus_stop;garmin:legend=bus_stop_station,,unknown_or_map_only,,,highway=bus_stop,unchanged,"42.7278256,23.1054282 А:44КУ,49","42.6855540,23.1390095 А:44ГУ,47","42.6880860,23.1192255 А:44МСМ,44МУ,48",LEGEND VERIFIED | Спирка / Bus Stop/Station | count=3120 | original_default= | Exact blue bus station icon | V09 custom render/index mode; confidence=legend | V094 REVIEW: stop; amenity=bus_station only if named accordingly
|
||||
0x641d,17,bgm:unclassified=yes,bgm:unclassified=yes,power=generator,provenance_only,,,,unchanged,"42.7201009,23.3233523 ТЕЦ „София“","42.2851968,23.0325365 ТЕЦ „Бобов дол“","43.8662410,26.0090375 ТЕЦ „Русе Изток“","count=17 default= icon=yes example=(43.8307285,25.9306741) ТЕЦ „Русе Запад“ | V09 custom render/index mode; confidence=unknown"
|
||||
0x641e,55,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.3201728,24.3003416 Соларен Парк Солсбор","42.8275180,24.9023795 Соларен парк","42.3448062,24.2493367 Фотоволтаичен парк Елшица","count=55 default= icon=yes example=(42.3201728,24.3003416) Соларен Парк Солсбор | V09 custom render/index mode; confidence=unknown"
|
||||
0x641f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"43.7468505,23.7692428 АЕЦ Козлодуй",,,"count=1 default= icon=yes example=(43.7468505,23.7692428) АЕЦ Козлодуй | V09 custom render/index mode; confidence=unknown"
|
||||
0x6500,31214,man_made=water_intake;garmin:legend=reservoir,man_made=water_intake;amenity=water_point;drinking_water=unknown;garmin:legend=reservoir;bgm:compat=water_point_unverified,amenity=water_point;place=locality,confirmed_native_poi,amenity=water_point,,man_made=water_intake,changed,"43.3802032,23.4851217 184","42.8668928,23.0847001 587","42.6751256,23.1309199 каптаж стар","orks | Exact solid blue square; legend means catchment/reservoir, not waterworks | V09 custom render/index mode; confidence=legend | V094 REVIEW: PRIVATE-ONLY v18 category recovery: catchment does not prove public potable water; verify access/quality"
|
||||
0x6501,2412,amenity=restaurant;garmin:legend=restaurant,amenity=restaurant;garmin:legend=restaurant,amenity=restaurant,confirmed_native_poi,amenity=restaurant,,,unchanged,"43.7757325,23.7307692 При Серго","43.7800884,23.7217569 Механа Банята","43.7784147,23.7228727 Панорама",LEGEND VERIFIED | Ресторант / Restaurant | count=2412 | original_default=amenity=cafe | Exact fork+knife icon; corrects name-based cafe inference | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6502,1864,ford=yes;garmin:legend=river_passage;bgm:unclassified=yes,ford=yes;garmin:legend=river_passage,ford=yes,confirmed_native_poi,ford=yes,,,changed,"43.7700462,23.7724400 Бент","42.8177762,23.4124446 Брод","42.8215313,23.4510899 брод","D VERIFIED | Брод / River Passage | count=1864 | original_default= | Exact blue river crossing; need to verify whether it is ford=yes | V09 custom render/index mode; confidence=legend | V094 REVIEW: ford is semantic, remove incorrect bgm:unclassified"
|
||||
0x6503,760,bgm:trail_junction=yes;garmin:legend=crossroad;bgm:unclassified=yes,bgm:trail_junction=yes;garmin:legend=crossroad,route=hiking,provenance_only,,,,changed,"42.2793388,23.5382509 За Боровец","42.3269105,23.5528207 на юг екопътека Чакъра","42.2471952,23.5828829 Разклона",al_default=tourism=information;information=guidepost | Exact black crossroad icon; corrects guidepost assumption | V09 custom render/index mode; confidence=legend | V094 REVIEW: do not fabricate guideposts where only a route intersection is indicated
|
||||
0x6506,424,tourism=museum;garmin:legend=museum,tourism=museum;garmin:legend=museum,tourism=museum,unknown_or_map_only,,,tourism=museum,unchanged,"43.7835431,23.7217784 Национален Исторически музей - РАДЕЦКИ","43.7375808,23.9573622 Музей Дико Илиев","43.7371302,23.9596152 Музей",LEGEND VERIFIED | Музей / Museum | count=424 | original_default= | Exact museum icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x650b,8767,trailblazed=yes;garmin:legend=winter_marking;bgm:unclassified=yes,tourism=information;information=trail_blaze;hiking=yes;garmin:legend=winter_marking,piste=type=hike,nonindexed_known_type,,tourism=information,,changed,"42.1617723,23.7171006 Рзкл TK Белмекен","42.1479106,23.7567329 За Ибър","43.0234265,27.2498274 Табели",riginal_default=tourism=information;information=board | Exact winter red/yellow marker; corrects information=boa | V09 custom render/index mode; confidence=legend | V094 REVIEW: accurate point trail mark; not guaranteed searchable POI in stock OsmAnd
|
||||
0x6511,50308,natural=spring;garmin:legend=spring,natural=spring;garmin:legend=spring,natural=spring,unknown_or_map_only,,,natural=spring,unchanged,"43.5907888,23.0589080 Оджин","43.4688663,23.0003500 Студен кладенец","43.4618711,23.0113363 Цановица",звор / Spring | count=50308 | original_default=amenity=drinking_water | Exact blue water spring drop; no automatic potable-water claim | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: spring; not assumed potable
|
||||
0x6600,66206,bgm:spot_height=yes;garmin:legend=triangulation;bgm:unclassified=yes,bgm:spot_height=yes;garmin:legend=triangulation,place=locality,provenance_only,,,,changed,"43.7977695,23.5016656 37","43.8004303,23.5182095 72","43.8156223,23.5188532 29","tion | count=66206 | original_default= | Exact square survey icon; preserve any independently parsed numeric elevation | V09 custom render/index mode; confidence=legend-ambiguous | V094 REVIEW: retain source ele metadata, DO NOT assign place=locality"
|
||||
0x6601,165,bgm:landmark=natural_phenomenon;garmin:legend=natural_phenomenon;bgm:unclassified=yes,tourism=attraction;bgm:landmark=natural_phenomenon;garmin:legend=natural_phenomenon,place=locality;tourism=attraction,confirmed_native_poi,tourism=attraction,,,changed,"42.8913975,23.2381225 Дупката","42.8463364,23.3303905 Кътински пирамиди","42.0073843,23.5218358 водопад Казаните","феномен / Natural Phenomenon | count=165 | original_default= | Exact natural-phenomenon hiking silhouette; subtype unknown | V09 custom render/index mode; confidence=legend | V094 REVIEW: natural phenomenon point, generic visitor interest provisional"
|
||||
0x6602,891,tourism=viewpoint;garmin:legend=viewpoint,tourism=viewpoint;garmin:legend=viewpoint,tourism=viewpoint;place=locality,unknown_or_map_only,,,tourism=viewpoint,unchanged,"42.9399991,23.3051562 Панорама","42.9989433,23.3445954 Викалото","42.9921627,23.4496522 към в.Козница",LEGEND VERIFIED | Гледка / Viewpoint | count=891 | original_default=tourism=viewpoint | Exact black camera icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6603,26366,man_made=survey_point;garmin:legend=triangulation,man_made=survey_point;garmin:legend=triangulation,,nonindexed_known_type,,man_made=survey_point,,unchanged,"42.3178983,23.5780764 982","42.3178768,23.5752225 980","42.3167396,23.5741282 998", VERIFIED | Кота / Triangulation | count=26366 | original_default= | Exact black square survey-point icon | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: retain source elevation and annotation; APPEND mode only
|
||||
0x6604,10,bgm:unclassified=yes,bgm:unclassified=yes,natural=beach;tourism=camp_site,provenance_only,,,,unchanged,"42.4436617,27.4963117 Къмпинг ""Крайморие""","43.6400127,28.5708261 Къмпинг Карвуна","43.0315375,27.8832793 къмпинг Рай","count=10 default= icon=yes example=(42.1888089,27.8359652) Къмпинг ""Арапя"" | V09 custom render/index mode; confidence=unknown"
|
||||
0x6608,2644,tourism=hotel;garmin:legend=hotel,tourism=hotel;garmin:legend=hotel,place=locality;tourism=wilderness_hut,unknown_or_map_only,,,tourism=hotel,unchanged,"43.3893228,23.2167506 Ринг","43.3897305,23.2159781 Аугуста","43.4091496,23.2240033 Житомир",LEGEND VERIFIED | Хотел / Hotel | count=2644 | original_default=tourism=viewpoint | Exact hotel/bed icon; corrects viewpoint | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: hotel not wilderness hut; native POI
|
||||
0x6609,372,tourism=alpine_hut;garmin:legend=hut,tourism=alpine_hut;garmin:legend=hut,building=bungalow;place=locality,confirmed_native_poi,tourism=alpine_hut,,,unchanged,"43.6697102,23.6456251 Брестовец","42.8914833,23.1741142 Люляка","42.6693320,23.1078959 х.Люлин (руини) 1060м",LEGEND VERIFIED | Хижа / Hut | count=372 | original_default=tourism=alpine_hut | Exact mountain hut icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x660a,275,amenity=shelter;shelter_type=basic_hut;garmin:legend=refuge,amenity=shelter;garmin:legend=refuge,place=locality;tourism=wilderness_hut,confirmed_native_poi,amenity=shelter,,,changed,"42.9726577,23.0680275 Школа Езда","42.6796532,23.0855155 Янкова поята","42.9415870,23.2864451 Свидня","ge | count=275 | original_default=tourism=wilderness_hut | Exact refuge icon; shelter type provisional | V09 custom render/index mode; confidence=legend | native-tag candidate | V094 REVIEW: source refuge; no assumption of heating, beds or 24h access"
|
||||
0x660b,2612,trailblazed=yes;garmin:legend=mark;bgm:unclassified=yes,tourism=information;information=trail_blaze;hiking=yes;garmin:legend=mark,information=guidepost;place=locality,nonindexed_known_type,,tourism=information,,changed,"42.8252864,22.4414206 Пир. 374","42.8168321,22.4409699 Пир. 187","42.8379250,22.4831772 Пир. 193",| original_default=tourism=information;information=board | Exact black circle mark icon; corrects information=board | V09 custom render/index mode; confidence=legend | V094 REVIEW: accurate point blaze; trailblazed belongs on paths not isolated nodes
|
||||
0x660c,94,natural=saddle,natural=saddle,place=locality,unknown_or_map_only,,,natural=saddle,unchanged,"42.1907187,23.6004138 Премка","42.1668363,23.7219501 Ушите","42.7413011,25.0688696 Чимколиба","count=94 default=natural=saddle icon=yes example=(41.7514372,23.3985186) Влахински превал | V09 custom render/index mode; confidence=source-inferred | native-tag candidate"
|
||||
0x660d,8,bgm:unclassified=yes,bgm:unclassified=yes,aeroway=aerodrome;place=locality,provenance_only,,,,unchanged,"42.6904893,23.4124017 Летище София","43.8889003,22.8024030 Летище Жеглица","43.1460357,25.7195950 Летище Горна Оряховица","count=8 default= icon=yes example=(43.8889003,22.8024030) Летище Жеглица | V09 custom render/index mode; confidence=unknown"
|
||||
0x660e,8,bgm:unclassified=yes,bgm:unclassified=yes,military=airfield;natural=volcano,provenance_only,,,,unchanged,"42.8157806,23.2999206 Авиобаза Доброславци","43.4520006,24.5126653 Авиобаза Долна Митрополия","43.5985994,27.8323603 Авиобаза Добрич","count=8 default= icon=yes example=(42.1178055,25.0028229) Авиобаза Чешнегирово | V09 custom render/index mode; confidence=unknown"
|
||||
0x660f,44,bgm:unclassified=yes,bgm:unclassified=yes,place=locality,provenance_only,,,,unchanged,"43.5402775,23.2688498 Авиобаза Габровница","43.4979630,23.3034825 Летателна площадка Ерден","42.8448772,23.0146837 Летателна площадка Сливница","count=44 default= icon=yes example=(43.6951160,26.0480905) Летище Русе | V09 custom render/index mode; confidence=unknown"
|
||||
0x6610,15,garmin:legend=meadow;bgm:unclassified=yes,garmin:legend=meadow;bgm:unclassified=yes,place=locality,provenance_only,,,,unchanged,"41.7146158,23.5689354 Дългата поляна","41.8613005,23.3034182 Поляна","41.8687248,23.3031178 Поляна здравец",LEGEND VERIFIED | Поляна / Meadow | count=15 | original_default= | Exact green meadow/flag icon; point geometry not polygon | V09 custom render/index mode; confidence=legend
|
||||
0x6613,1746,waterway=waterfall;garmin:legend=waterfall,waterway=waterfall;garmin:legend=waterfall,waterway=waterfall;place=locality,unknown_or_map_only,,,waterway=waterfall,unchanged,"42.8182268,23.4132600 Подгумерски водопад","42.8204799,23.4505105 Войнешки водопад","42.0339918,23.5631204 Станков водопад",LEGEND VERIFIED | Водопад / Waterfall | count=1746 | original_default=waterway=waterfall | Exact three blue streams icon | V09 custom render/index mode; confidence=legend
|
||||
0x6614,15289,natural=rock;garmin:legend=rock,natural=rock;garmin:legend=rock,natural=ravine;place=locality,unknown_or_map_only,,,natural=rock,unchanged,"42.6775932,22.4788642 Кървави камик","42.9519510,23.0338025 2","42.8956246,23.1761527 катерачен обект Бъчвата",LEGEND VERIFIED | Скала / Rock | count=15289 | original_default=garmin:marker=distance_or_index | Exact black rock silhouette; many numerical labels require additional checking | V09 custom render/index mode; confidence=legend
|
||||
0x6615,942,natural=cave_entrance;garmin:legend=cave,natural=cave_entrance;garmin:legend=cave,natural=peak;place=locality,unknown_or_map_only,,,natural=cave_entrance,unchanged,"42.9690742,23.1419921 Пещта","42.8955173,23.1758952 Хайдушката п-ра","42.8963971,23.1767106 Терзийската п-ра",LEGEND VERIFIED | Пещера / Cave | count=942 | original_default= | Exact yellow cave icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6616,15540,natural=peak,natural=peak,natural=peak,unknown_or_map_only,,,natural=peak,unchanged,"43.7769127,21.8764186 Пресло (1405)","43.8136911,23.5667896 Окница (40)","43.7260151,23.6191463 Чукара (151)","NAME-SUPPORTED | peak names confirm natural=peak despite no TYP icon; count=15540 default=natural=peak icon=no example=(42.8299427,26.0312033) Букачев чукар | V09 custom render/index mode; confidence=verified | native-tag candidate"
|
||||
0x6617,112,natural=peak;garmin:legend=peak,natural=peak;garmin:legend=peak,natural=peak;place=locality,unknown_or_map_only,,,natural=peak,unchanged,"42.6823568,22.4656034 Било (1737)","42.9397202,23.3048773 Церия (1234)","42.1792388,23.5853076 Мусала (2925)",LEGEND VERIFIED | Връх / Peak | count=112 | original_default=natural=peak | Exact triangular mountain icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6618,10176,natural=tree;garmin:legend=tree,natural=tree;garmin:legend=tree,landuse=forest;natural=hill,unknown_or_map_only,,,natural=tree,unchanged,"43.6818552,23.8606095 Летен дъб","43.7382460,23.9182448 Бял бор","43.6720920,23.9911366 Черница",LEGEND VERIFIED | Дърво / Tree | count=10176 | original_default= | Exact green pine icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6619,195,historic=archaeological_site;garmin:legend=prehistoric_site,historic=archaeological_site;garmin:legend=prehistoric_site,historic=monument;natural=cave_entrance,unknown_or_map_only,,,historic=archaeological_site,unchanged,"43.0966616,27.3624158 Скални ниши","43.2099795,27.4630308 Шарапташ","43.2132626,27.4653912 Тракийско светилище",LEGEND VERIFIED | Праисторически обект / Prehistoric site | count=195 | original_default= | Exact yellow prehistoric icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x661a,384,historic=archaeological_site;garmin:legend=antiquity_site,historic=archaeological_site;garmin:legend=antiquity_site,historic=castle;place=locality,unknown_or_map_only,,,historic=archaeological_site,unchanged,"43.4051585,23.2216001 Стара Римска Баня","43.4020472,23.2178020 Кастра ад Монтанезиум (Монтанска крепост)","42.8413153,23.3710313 крепост Белиград",LEGEND VERIFIED | Античен обект / Antiquity site | count=384 | original_default= | Exact yellow antiquity icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x661b,319,historic=archaeological_site;garmin:legend=medieval_site,historic=archaeological_site;garmin:legend=medieval_site,historic=heritage;place=locality,unknown_or_map_only,,,historic=archaeological_site,unchanged,"42.9245710,23.2585502 Оброк м-ла Марковци","42.9098725,23.2487440 Оброчен камък","42.8997874,23.2613397 Царичински оброк",LEGEND VERIFIED | Средновековен обект / Medieval site | count=319 | original_default= | Exact yellow medieval icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x661c,1169,bgm:landmark=mound;bgm:unclassified=yes,bgm:landmark=mound,place=locality,provenance_only,,,,changed,"42.3227906,23.5107636 Фъркова могила","42.0598698,23.9486504 Божков гроб","43.7023902,28.5267735 Кючукова могила","count=1169 default= icon=yes example=(41.7346358,24.6707010) Могила | V09 custom render/index mode; confidence=uncertain | V094 REVIEW: named mounds not all archaeological tumuli or hills; terrain annotation"
|
||||
0x661d,1412,tourism=wilderness_hut;building=agricultural,building=agricultural;bgm:landmark=livestock_building,object=bg-mouintains-simvol-roga;place=locality,unknown_or_map_only,,,building=agricultural,changed,"42.7880573,23.2058930 кошари","42.8160810,23.4084320 Овчарник","42.0902753,23.5790849 Колиба Малка Баненска река","count=1412 default= icon=yes example=(42.6115251,24.9675894) селскостопански постройки | V09 custom render/index mode; confidence=inferred | V094 REVIEW: source is primarily agricultural/sheepfold, not public hiking hut"
|
||||
0x661e,979,amenity=shelter,amenity=shelter;garmin:legend=shelter,amenity=shelter;place=locality,confirmed_native_poi,amenity=shelter,,,changed,"42.8891015,23.2022667 навес","42.6746106,23.0835629 беседка","42.8657341,23.2842350 навес и чешма","count=979 default=amenity=shelter icon=yes example=(41.4898252,24.7246242) навес с барбекю | V09 custom render/index mode; confidence=source-inferred | native-tag candidate | V094 REVIEW: roofed shelter/canopy; do not assume overnight lodging"
|
||||
0x10000,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=23 default= icon=no example=(42.4906540,28.1000018) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10018,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=160 default= icon=no example=(43.2075763,27.3377824) ото | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10100,12,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.4783373,27.4493837 LFl 4.5s W 4M","42.4815774,27.4833083 LFl W 3s 10M Horn(n)","43.1913543,27.9202938 Iso G 4s 2M","REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=35 default= icon=no example=(43.2024050,27.0830369) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10118,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=159 default= icon=no example=(42.3472738,27.8012896) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1011e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.5841022,27.6902461) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10204,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.5164247,27.6434898 LFl(3) W 10s Horn","42.6573801,27.7476025 Fl(3) W 10s",,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=6 default= icon=no example=(43.2060313,27.2352362) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10205,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=7 default= icon=no example=(42.7170539,27.1994662) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10209,8,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.4596262,27.4533534 R","42.4733591,27.4699187 Fl 3s","43.1995296,27.8640532 Fl R","REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=11 default= icon=no example=(42.4790454,27.4806905) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1020a,9,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.4593043,27.4570227 G","42.4753761,27.4862909 Fl 3s","43.1951952,27.7179480 Fl G","REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=12 default= icon=no example=(42.4856114,28.4411144) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1020b,5,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.4769211,27.4610353 Fl 4s W","43.1872773,27.8926563 Y Fl 5s 4M","41.8699050,28.0232477 Fl(2) 6s","REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=7 default= icon=no example=(42.6320171,27.6835513) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1020c,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(43.1997228,27.8581524) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10218,3,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=161 default= icon=no example=(41.9324327,28.1997156) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10301,2006,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2018 default= icon=no example=(43.1939721,27.9443908) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10401,157,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=158 default= icon=no example=(43.1992292,27.8581738) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10402,41,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=45 default= icon=no example=(43.2398915,28.1541181) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10403,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(42.3191428,27.3510432) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1040d,229,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=229 default= icon=no example=(42.6323819,27.6772428) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1040e,4,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.0751047,27.9735947) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1041e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.6499343,27.8320599) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10501,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.7575231,28.0074978) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10602,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.7095008,27.8530884) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1061d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2051730,27.9395843) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10701,58,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=59 default= icon=no example=(42.4661708,27.5432396) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10705,33,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=33 default= icon=no example=(42.4353790,27.6875854) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10706,32,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=32 default= icon=no example=(43.1958175,27.7090001) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10801,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.8644896,27.9285121) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10903,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=8 default= icon=no example=(41.8466663,27.1602845) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10a01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(41.9518948,27.8967762) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10b01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=18 default= icon=no example=(42.4577379,26.8323469) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10b09,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2140994,27.9750752) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10c00,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(41.8485546,27.8088427) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10d01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2011819,27.8610492) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1161f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.1953883,27.9319453) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1171e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2746100,27.7074981) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1181f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2013321,27.7061892) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x11901,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.4857402,27.9561281) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x11902,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.0788169,27.8834510) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x11a02,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2987928,27.8464150) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1201f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.5242352,27.5483894) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x12102,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=6 default= icon=no example=(42.5290418,27.8020620) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x12502,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(43.2015038,27.8690314) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x12a03,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=7 default= icon=no example=(41.7048740,28.0575585) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x12c1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2013321,27.7046227) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x12d01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.4645104,27.9524589) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x12e1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2423377,28.1846094) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x13001,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.4904394,27.8976560) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1321f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.6127267,27.8121257) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1331f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2015467,27.8662419) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x13400,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.0351424,27.6759124) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x13a01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(41.9161892,27.8259444) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x13c03,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.5164032,27.8715634) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x13d1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"43.2068896,27.9138136 я припек",,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(41.9803476,27.5775290) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1411e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2047009,27.7807546) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14400,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2175541,27.9579949) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14401,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.4857402,28.2857180) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1441c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.1683731,28.0286121) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14501,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(42.8306079,28.1585383) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14602,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(43.1857967,27.8471661) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1461c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.5024128,27.5485826) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1461f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2052588,27.8920341) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1471f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.5125623,28.4499764) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14803,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=16 default= icon=no example=(41.7048740,28.0575585) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1481f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2054090,27.8980207) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14a1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2069111,27.9140067) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14b01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(43.2014823,27.8672075) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14c01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.0417809,27.8935146) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14d1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.5163817,27.6553559) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x14e1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.4941864,27.8600621) ес | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x15200,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.4892378,28.2068181) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x15303,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(42.4453783,27.9671144) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1541d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.4657202,27.6436615) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x15501,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(41.9985008,27.8312445) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x15800,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2052588,27.9370308) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x15a00,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=6 default= icon=no example=(42.0008826,28.0595756) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x15e01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(43.6395192,27.9830790) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x16103,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(44.0216589,28.6207366) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x16201,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(44.0655184,28.9989281) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x16701,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(43.8174891,27.8137565) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1681c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"42.6561785,27.8976774 Тюлен",,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.6561785,27.8976774) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1681d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.4778142,27.9166889) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x16a00,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2054305,27.8999090) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x16b1c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.6562428,27.9021621) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x16e1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.6637101,27.7445555) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x17000,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.4856114,27.5214815) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1711d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.1645536,27.9943871) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x17303,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=6 default= icon=no example=(42.7173972,27.9722643) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1731e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.1925559,27.8419733) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1751f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.5641251,27.9423738) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1771e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.0213318,28.1093144) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1791e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2051516,27.8927636) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x17d1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.8188062,28.1426811) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x17e03,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=18 default= icon=no example=(43.1967616,27.6618361) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x18003,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=10 default= icon=no example=(42.6977420,27.6655483) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1811e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(42.6157951,28.1614137) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x18302,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.7289200,27.9383826) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1861d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.0271387,27.6801825) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1861f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2055807,27.8994370) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x18901,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.5641251,27.9499054) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x18a1f,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"43.2052374,27.8961754 ставена",,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2015467,27.8710055) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x18c1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2053876,27.8944159) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x18d01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(42.6413083,27.7646613) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x19001,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(41.8282771,27.9910183) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1921f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.1928349,27.8456640) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x19a00,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.0997944,27.9608488) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x19d03,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.8070259,27.9287481) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a11f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.6562643,27.9072046) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a401,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(41.9857550,27.7072835) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a501,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(41.8626308,27.5157952) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a51c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.0785379,27.5604272) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a800,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.5856042,27.6471162) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a81c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2015252,27.7040648) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a91c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2051516,27.9386616) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1a91f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.1954098,27.9332972) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ab1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=6 default= icon=no example=(42.5103736,27.7936292) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1b101,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(41.9518948,27.7869129) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1b31f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.4185133,27.7397704) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1b402,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(43.1732011,27.9486609) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1b41c,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2049155,27.7816987) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1b703,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.7092648,27.8581309) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ba03,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(43.2014394,27.8686452) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1be1c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.2013321,27.7118969) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1c11e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.5125623,27.3952889) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1c31d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.1954956,27.9373097) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1c500,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.3673286,27.7575159) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1c71f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2048726,27.7903676) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1c81e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.1928992,27.8485823) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1cb1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=7 default= icon=no example=(43.5310936,27.8975701) ев | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ce02,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.4771142,27.2752333) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1d501,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.4895167,27.3352933) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1d51e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,"43.2054305,27.8993511 вец",,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2054305,27.8993511) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1d61f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(43.2015681,27.8704262) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1d71c,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.7140632,27.7076268) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1d800,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(43.5437322,27.9161096) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1da01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(42.7597547,27.7897239) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1da1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(42.4458933,27.5304723) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1db01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.4896884,28.2659340) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1db1e,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2012463,27.8598905) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1db1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.5776649,28.1740737) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1dc01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(43.1860757,27.8722286) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1de1f,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.4868131,27.7382255) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1e101,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.1975555,27.8627014) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1e21f,2,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(41.9545984,27.8767347) ch Resort | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ea00,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=2 default= icon=no example=(42.4904394,28.5019040) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ec1d,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(41.7846966,27.5348711) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1f401,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=4 default= icon=no example=(41.9518948,27.9132557) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1f501,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=3 default= icon=no example=(42.4896884,28.3043861) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1fb01,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=1 default= icon=no example=(43.2013750,27.7114248) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ff00,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=20 default= icon=no example=(42.6580453,27.1025848) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ff11,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=5 default= icon=no example=(43.2470155,27.4592757) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x1ff18,1,bgm:unclassified=yes,bgm:unclassified=yes,,provenance_only,,,,unchanged,,,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=148 default= icon=no example=(42.0976567,27.6564074) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
# BGMountains POI translation review (v0.94, 2026-10-16)
|
||||
|
||||
## What was revised
|
||||
|
||||
Inputs: the user's `poi-overrides-all-types(3).csv` (1,871 unique Garmin codes), the original strict decoded 410,563-point source inventory and legend, old v18 KMZ/OBF tag-category comparison, and OsmAnd upstream `poi_types.xml` (SHA `f2f284589c8287f9db3a6607265af91c59267788`).
|
||||
|
||||
**This is a config revision, not a new converter release or compiled OBF.** It is intended for private map generation. It is **not suitable for direct import into public OpenStreetMap**: it contains private-map compatibility aliases and synthetic classifications.
|
||||
|
||||
Two *alternative* POI configuration formats are included because old and new programs read different schemas:
|
||||
|
||||
- `BGMountains-POI-overrides-v094-reviewed.csv` — **five columns** (`code,name_contains,mode,tags,note`). Intended for C++ `garmin2osm --poi-overrides`. Contains the 1,871 base rows **plus 79 case-sensitive name-conditional rules**, for 1,950 rules total. Edit this if invoking C++ directly.
|
||||
- `BGMountains-native-poi-mapping-v094.csv` — **15 columns**. Intended for the previously supplied v0.93 Python `scripts/build_bgmountains.py --poi-config ...` pipeline. Contains 1,871 base rows; it cannot represent `name_contains` overrides in the unmodified v0.93 translator. It retains your existing fallback label prefixes from that v0.93 baseline. **Do not supply the five-column file to `--poi-config`**. Conversely the 15-column file is not accepted by `--poi-overrides`.
|
||||
|
||||
**For a Python v0.93 run, use only one POI control file (`--poi-config`) and the existing single line file (`--line-config`).** The pipeline regenerates the intermediate five-column C++ rules. Updating an older `config/poi-overrides-all-types.csv` alone does *not* update a v0.93 Python build.
|
||||
|
||||
We could not inspect the current private Gitea branch; if its CLI or schema has since changed, compare its current code with these schemas before installing.
|
||||
|
||||
## Important changes, by meaning
|
||||
|
||||
| Hex | Previously | Reviewed result | Important caveat |
|
||||
|---|---|---|---|
|
||||
| `0x6414` | `amenity=drinking_water` | Add `man_made=water_tap`, matching v18's dual native POI categories | A Garmin fountain icon does not confirm *present-day* water flow. C++ conditional `Сух/сух` name exceptions explicitly mark `drinking_water=no`. |
|
||||
| `0x6500` | `man_made=water_intake` | Add `amenity=water_point` **as a private OsmAnd compatibility alias**, plus `drinking_water=unknown` | **Not a reliable potable source.** OSM `water_point` normally implies water suitable for refilling tanks, so this alias should not be exported to public OSM; physical water availability/access need confirmation. |
|
||||
| `0x6511` | `natural=spring` | Preserve | Not automatically potable. |
|
||||
| `0x6417` | `man_made=water_well` | Preserve | Not automatically potable. |
|
||||
| `0x6402` | `tourism=wilderness_hut` on **all** buildings | Generic `building=yes`; C++ overrides only when named `заслон` or `хижа` | Source names include post offices, substations, military facilities. An erroneous shelter label is a hiking safety problem. |
|
||||
| `0x661d` | Agricultural structures advertised as wilderness huts | `building=agricultural`, with name-specific hut/shelter exceptions | Includes many sheepfolds. Overnight access unknown. |
|
||||
| `0x6405` | Hunting lodges advertised as wilderness huts | `building=hut`, plus explicitly named shelter exceptions | Hunting lodges may be locked/private. |
|
||||
| `0x6600` | `replace` could erase elevation during C++ processing | `append` preserves numeric `ele=*`; no fake locality | Numeric annotation, not generic POI. |
|
||||
| `0x6603` | `replace` could erase elevation | `append` preserves source metadata | Survey point label, lower visual priority. |
|
||||
| `0x2c08` | All sports venues `leisure=horse_riding` | Default sports venue + name-specific `stadium`, `pitch`, `playground`, `ice_rink`, etc. | Mixed Garmin category, so unknown venue subtype remains provisional. |
|
||||
| `0x2f0b` | `leisure=track` for predominantly parking | `amenity=parking`, with a few non-parking name exceptions | Parking is **non-indexed** in inspected OsmAnd POI definitions even though it has map rendering support. |
|
||||
| `0x2c06` | Unclassified | `amenity=university`; hospital exception | Based on university/faculty source names. |
|
||||
| `0x2c07` | Unclassified | `tourism=zoo` | Based on zoo names. |
|
||||
| `0x2d01` / `0x2d03` | Unclassified | Theatre/cinema with named exceptions | Names distinguish mixed types. |
|
||||
| `0x2a07` / `0x2a0e` | Unclassified or generic shop | Fast food / cafe with exceptions | Named bistros can be restaurants. |
|
||||
| `0x2e03` / `0x2e0b` | Generic shop / unclassified | Supermarket / electronics store | Samples include Lidl, Billa, Kaufland, Technopolis. |
|
||||
| `0x3007` | Every courthouse/pumping station called `man_made=pumping_station` | Courthouse default; exceptions for pumps, treatment plants, prisons | Type group is demonstrably heterogeneous. |
|
||||
| `0x650b` / `0x660b` | `trailblazed=yes` incorrectly placed on isolated nodes | `tourism=information;information=trail_blaze;hiking=yes` | Accurate OSM trail-marker *point* classification, but `tourism=information` may not be indexed in stock OsmAnd Nearby search; `trailblazed=*` belongs on path ways. |
|
||||
| `0x4700` | Risk of generating fictitious marinas | `harbour=yes` + source/needs-review metadata | Do not convert unidentified inland marks into `leisure=marina`. |
|
||||
|
||||
Other refinements are in the change log. Existing Garmin provenance `garmin:type`, feature, mapset and real source names are preserved by the tested C++ exporter. This config **does not generate names**; the converter's existing name-label logic does that.
|
||||
|
||||
## Visibility and Nearby limitations
|
||||
|
||||
The upstream OsmAnd POI registry has distinct cases:
|
||||
- Indexable: `amenity=drinking_water`, `man_made=water_tap`, `amenity=water_point`, `natural=spring`, `amenity=shelter`, `tourism=alpine_hut`, etc.
|
||||
- `no_indx=true` in inspected definitions: `place=locality`, `tourism=information`, `man_made=bridge`, `amenity=parking`, `man_made=survey_point`, among others.
|
||||
- Not registered as a native standalone POI type: `man_made=water_intake`, `building=agricultural`, `building=hut`, `shop=yes`, etc.
|
||||
|
||||
**Semantic correctness, presence in the OBF POI index, Nearby search and visibility at a given zoom are separate properties.** Tags such as `bgm:priority=critical` or `zoom_hint=12` do *not* alter stock OsmAnd's minimum zoom rules. Adding two *valid* primary tags may create multiple indexed categories as in the old v18 OBF, but that does not guarantee early rendering. Stock `place=locality` is not a general searchable POI substitute. The detailed native support for all codes still requires validating the exact installed OsmAnd release/`poi_types.xml` and then checking a compiled OBF.
|
||||
|
||||
### C++ direct regional test
|
||||
|
||||
```bash
|
||||
./build/garmin2osm --input gmapsupp.img \
|
||||
--bbox '25.05,42.43,25.16,42.54' \
|
||||
--poi-overrides BGMountains-POI-overrides-v094-reviewed.csv \
|
||||
--output regional.osm
|
||||
```
|
||||
|
||||
For a real dry-point test:
|
||||
|
||||
```bash
|
||||
./build/garmin2osm --input gmapsupp.img \
|
||||
--bbox '23.985,43.675,23.999,43.688' \
|
||||
--poi-overrides BGMountains-POI-overrides-v094-reviewed.csv \
|
||||
--output dry-test.osm
|
||||
```
|
||||
|
||||
### v0.93 Python preparation (no OBF)
|
||||
|
||||
```bash
|
||||
python3 scripts/build_bgmountains.py \
|
||||
--img gmapsupp.img \
|
||||
--poi-config /absolute/path/BGMountains-native-poi-mapping-v094.csv \
|
||||
--line-config config/line-mapping.csv \
|
||||
--bbox '25.05,42.43,25.16,42.54' \
|
||||
--out ./review-regional \
|
||||
--prepare-only
|
||||
```
|
||||
|
||||
No extra custom rendering file is required for the revised native categories. To compile a regional OBF later, remove `--prepare-only` and supply your existing OsmAndMapCreator through `--creator`. Do **not** start the whole-country build without sufficient temporary storage (~25+ GiB was previously needed on your machine).
|
||||
|
||||
### Changing settings later
|
||||
|
||||
In the five-column file, `replace` removes existing semantic defaults but keeps Garmin source and name. `append` preserves defaults/elevation and adds tags. A row with `name_contains` applies only if the POI name contains that **case-sensitive** string; matching rows are applied sequentially in file order. To correct a default for an entire hex type, edit its base row; to specialize named subtypes, add later `name_contains` rows with the same hex. Do not add duplicate keys inside a single `tags` field. Quote CSV fields that contain commas.
|
||||
|
||||
In the v0.93 native file edit `osm_tags` and `action`, not `name_contains`: that field does not exist in the old 15-column schema. `priority` and `zoom_hint` are advisory only. Keep the upstream pipeline's `config/line-mapping.csv` alongside this POI control. If you need conditional name rules in that pipeline, its translator must be extended; using these files alone cannot accomplish that.
|
||||
|
||||
## Test evidence
|
||||
|
||||
- 1,871 input base codes retained. Five-column result has 1,950 CSV rules: 1,871 base + 79 name-specific exceptions. Original inactive 1,608 code entries kept inert (many historically rejected extended-point parser candidates).
|
||||
- C++ binary loaded all 1,950 rules and extracted **427 POIs** in a real 0.11° × 0.11° regional test. Verified 37 drinking-water points have both `amenity=drinking_water` and `man_made=water_tap`. All 63 checked `0x6600` spot heights preserved `ele`.
|
||||
- The additional small `'(Сух.)'` IMG feature at ~43.6806107,23.9911795 was correctly marked `man_made=water_tap;drinking_water=no` and had **no** `amenity=drinking_water` tag.
|
||||
- v0.93 native translator `prepare` and `transform` processed 427 regional POIs, 3,611 ways and 6 hiking relations using the derived 15-column config; 0 missing mapped source types. The native output retains the dual `0x6414` tags and numeric spot heights.
|
||||
- No entire-country IMG extraction, no new OBF generation or phone-side zoom/POI overlay visual test was run.
|
||||
|
||||
## Reference documentation
|
||||
|
||||
- OsmAnd native POI registry: https://github.com/osmandapp/OsmAnd-resources/blob/master/poi/poi_types.xml
|
||||
- OSM `amenity=drinking_water`: https://wiki.openstreetmap.org/wiki/Tag:amenity=drinking_water
|
||||
- OSM `man_made=water_tap`: https://wiki.openstreetmap.org/wiki/Tag:man_made=water_tap
|
||||
- OSM `amenity=water_point`: https://wiki.openstreetmap.org/wiki/Tag:amenity=water_point
|
||||
- OSM `information=trail_blaze`: https://wiki.openstreetmap.org/wiki/Tag:information=trail_blaze
|
||||
- OSM `tourism=wilderness_hut`: https://wiki.openstreetmap.org/wiki/Tag:tourism=wilderness_hut
|
||||
@@ -0,0 +1,51 @@
|
||||
code,source_count,legend_bg,legacy_color,osm_way_tags,route_mode,osmc_symbol,network,route_confidence,priority,note
|
||||
0x10e1f,237835,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f12,217991,Неустановена линейна декорация,#e6e6e6,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f16,36027,Неустановена линейна декорация,#e6e6e6,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e0b,15148,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f17,10965,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ff3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e13,10607,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e12,8662,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f14,8475,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f11,6436,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f0a,6231,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ff0000,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e0a,4347,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#0030ff,,relation,blue:white:blue_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f00,4197,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f0f,4098,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#399500,,relation,green:white:green_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e11,3926,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e05,3011,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ffff00,,relation,yellow:white:yellow_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e0c,2375,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e1c,2045,Неустановена линейна декорация,#ffffff,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f13,1575,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e19,1469,Неустановена линейна декорация,#525252,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e09,1074,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#0030ff,,relation,blue:white:blue_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e04,1048,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ffff00,,relation,yellow:white:yellow_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e16,636,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10504,591,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f0e,488,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#399500,,relation,green:white:green_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10401,394,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e1b,368,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ff0000,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10602,354,Неустановена линейна декорация,#777777,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e1d,327,Неустановена линейна декорация,#ffffff,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e18,292,Неустановена линейна декорация,#414141,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e0d,267,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10108,232,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e1a,205,Неустановена линейна декорация,#ffffff,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e03,199,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ffff00,,relation,yellow:white:yellow_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e15,184,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f05,162,Неустановена линейна декорация,#7b3000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e1e,162,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e08,119,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#0030ff,,relation,blue:white:blue_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e17,110,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ff0000,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e14,109,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f15,98,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f04,93,Неустановена линейна декорация,#7b3000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10606,78,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e02,60,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#ffff00,,relation,yellow:white:yellow_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e0e,56,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10402,47,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f03,9,Неустановена линейна декорация,#7b3000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10f01,4,Неустановена линейна декорация,#7b3000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10e0f,4,Неустановена линейна декорация,#000000,,preserve,,lwn,unknown,terrain,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10503,3,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
0x10403,3,"Туристическа линейна маркировка (цветът е потвърден, шарката не)",#bd3041,,relation,red:white:red_bar,lwn,color_only,critical,Provisional symbol until verified against legend; overlay is NOT a routable path
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -53,10 +53,10 @@ code,name_contains,mode,tags,note
|
||||
0x4e00,,replace,bgm:unclassified=yes,"count=14 default= icon=no example=(41.9877291,24.0870094) Минерална баня | V09 custom render/index mode; confidence=unknown"
|
||||
0x6400,,replace,landuse=quarry;garmin:legend=quarry,LEGEND VERIFIED | Кариера / Quarry | count=465 | original_default= | Exact crossed mining tools icon; point vs area geometry still to assess | V09 custom render/index mode; confidence=legend
|
||||
0x6401,,replace,man_made=bridge;garmin:legend=bridge,LEGEND VERIFIED | Мост / Bridge | count=1679 | original_default= | Exact bridge-icon depiction; original route connectivity not established | V09 custom render/index mode; confidence=legend
|
||||
0x6402,,replace,building=yes;garmin:legend=building,LEGEND VERIFIED | Сграда / Building | count=6186 | original_default= | Exact yellow building icon; original building footprint not present | V09 custom render/index mode; confidence=legend
|
||||
0x6402,,replace,tourism=wilderness_hut;building=yes;garmin:legend=building,LEGEND VERIFIED | Сграда / Building | count=6186 | original_default= | Exact yellow building icon; original building footprint not present | V09 custom render/index mode; confidence=legend
|
||||
0x6403,,replace,landuse=cemetery;garmin:legend=graveyard_tombstone,"LEGEND VERIFIED | Гробище, оброк / Graveyard/Tombstone | count=1729 | original_default= | Exact grave cross; ambiguous between graveyard and religious votive feature | V09 custom render/index mode; confidence=legend"
|
||||
0x6404,,replace,amenity=place_of_worship;garmin:legend=church,LEGEND VERIFIED | Църква / Church | count=1507 | original_default=amenity=place_of_worship | Exact church icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6405,,replace,building=hut;garmin:legend=hunting_lodge,LEGEND VERIFIED | Ловна хижа / Hunting Lodge | count=260 | original_default=tourism=alpine_hut | Exact hunting lodge icon; do NOT infer tourist/alpine hut | V09 custom render/index mode; confidence=legend
|
||||
0x6405,,replace,tourism=wilderness_hut;building=hut;garmin:legend=hunting_lodge,LEGEND VERIFIED | Ловна хижа / Hunting Lodge | count=260 | original_default=tourism=alpine_hut | Exact hunting lodge icon; do NOT infer tourist/alpine hut | V09 custom render/index mode; confidence=legend
|
||||
0x6406,,replace,barrier=gate;garmin:legend=barrier_gate,LEGEND VERIFIED | Бариера / Barrier/Gate | count=854 | original_default= | Exact black/red barrier icon | V09 custom render/index mode; confidence=legend
|
||||
0x6407,,replace,tourism=picnic_site;garmin:legend=picnic_site,LEGEND VERIFIED | Пикник / Picnic Site | count=2861 | original_default=tourism=picnic_site | Exact picnic bench icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x6408,,replace,emergency=mountain_rescue;garmin:legend=mountain_rescue,LEGEND VERIFIED | ПСС / Mountain Rescue | count=12 | original_default=emergency=mountain_rescue | Exact shield with red cross | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
@@ -112,7 +112,7 @@ code,name_contains,mode,tags,note
|
||||
0x661a,,replace,historic=archaeological_site;garmin:legend=antiquity_site,LEGEND VERIFIED | Античен обект / Antiquity site | count=384 | original_default= | Exact yellow antiquity icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x661b,,replace,historic=archaeological_site;garmin:legend=medieval_site,LEGEND VERIFIED | Средновековен обект / Medieval site | count=319 | original_default= | Exact yellow medieval icon | V09 custom render/index mode; confidence=legend | native-tag candidate
|
||||
0x661c,,replace,bgm:landmark=mound;bgm:unclassified=yes,"count=1169 default= icon=yes example=(41.7346358,24.6707010) Могила | V09 custom render/index mode; confidence=uncertain"
|
||||
0x661d,,replace,building=agricultural,"count=1412 default= icon=yes example=(42.6115251,24.9675894) селскостопански постройки | V09 custom render/index mode; confidence=inferred"
|
||||
0x661d,,replace,tourism=wilderness_hut;building=agricultural,"count=1412 default= icon=yes example=(42.6115251,24.9675894) селскостопански постройки | V09 custom render/index mode; confidence=inferred"
|
||||
0x661e,,replace,amenity=shelter,"count=979 default=amenity=shelter icon=yes example=(41.4898252,24.7246242) навес с барбекю | V09 custom render/index mode; confidence=source-inferred | native-tag candidate"
|
||||
0x10000,,replace,bgm:unclassified=yes,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=23 default= icon=no example=(42.4906540,28.1000018) | V09 custom render/index mode; confidence=unverified-extended"
|
||||
0x10001,,append,,"REVIEW NEEDED (extended POI decoder may have fragmented label and bad coordinates) | count=6 default= icon=no example=(42.5068760,26.9733667) "
|
||||
|
||||
|
@@ -0,0 +1,75 @@
|
||||
# Original legend crosswalk, all 67 historic icon matches
|
||||
|
||||
Reference table generated from the earlier reviewed Garmin TYP/legend matching data. **Documentation only**; the two CSV files under `config/` are the only user-editable translation controls. The original icon PNGs are not needed, used, or packaged. Where no native semantic tag is defensible it is shown as “unclassified”.
|
||||
|
||||
| Source Garmin hex | Legend BG | Legend EN | Old TYP icon | v0.92 OSM tags | Priority | Sample lat, lon |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 0x200 | Столица | Capital City | TYP:0x200 | place=city;capital=yes | normal | 41.0612941, 28.8116670 |
|
||||
| 0x800 | Град >75 000 | Large City | TYP:0x800 | place=city | normal | 43.9025259, 22.2788572 |
|
||||
| 0x900 | Град <75 000 | Town | TYP:0x900 | place=town | normal | 42.0570374, 24.5459247 |
|
||||
| 0x2c05 | Училище | School | TYP:0x2c05 | amenity=school | normal | 41.7214394, 24.6845198 |
|
||||
| 0x2c0b | Църква | Church | TYP:0x2c0b | amenity=place_of_worship | normal | 43.0217957, 24.8099613 |
|
||||
| 0x2d04 | Казино | Casino | TYP:0x2d04 | unclassified | normal | — |
|
||||
| 0x2d08 | Пързалка | Ice Rink | TYP:0x2d08 | unclassified | normal | — |
|
||||
| 0x2e05 | Аптека | Pharmacy | TYP:0x2e05 | amenity=pharmacy | normal | 42.7442837, 25.3431416 |
|
||||
| 0x2e07 | Магазин за дрехи | Clothes shop | TYP:0x2e07 | shop=clothes | normal | 43.0698609, 25.6080151 |
|
||||
| 0x2f0a | Синагога | Synagogue | TYP:0x2f0a | amenity=place_of_worship;religion=jewish | normal | 41.5711713, 23.7286234 |
|
||||
| 0x2f0c | Джамия | Mosque | TYP:0x2f0c | amenity=place_of_worship;religion=muslim | normal | 44.0549612, 26.7991304 |
|
||||
| 0x2f0e | Автомивка | Car Wash | TYP:0x2f0e | amenity=car_wash | normal | 42.2939301, 22.7035904 |
|
||||
| 0x3004 | Съд | Courthouse | TYP:0x3004 | unclassified | normal | — |
|
||||
| 0x3008 | Пожарна | Fire Station | TYP:0x3008 | amenity=fire_station | normal | 42.5666571, 27.6193285 |
|
||||
| 0x4700 | Пристанище | Port | TYP:0x4700 | unclassified | normal | 41.6431189, 25.2845192 |
|
||||
| 0x6400 | Кариера | Quarry | TYP:0x6400 | landuse=quarry | normal | 40.8776808, 23.9436078 |
|
||||
| 0x6401 | Мост | Bridge | TYP:0x6401 | man_made=bridge | critical | 42.6645684, 22.9691291 |
|
||||
| 0x6402 | Сграда | Building | TYP:0x6402 | building=yes | critical | 42.5507569, 23.2217932 |
|
||||
| 0x6403 | Гробище, оброк | Graveyard/Tombstone | TYP:0x6403 | landuse=cemetery | normal | 43.0418801, 23.5914874 |
|
||||
| 0x6404 | Църква | Church | TYP:0x6404 | amenity=place_of_worship | normal | 42.1896243, 27.0861053 |
|
||||
| 0x6405 | Ловна хижа | Hunting Lodge | TYP:0x6405 | building=hut | normal | 42.7514720, 27.2207522 |
|
||||
| 0x6406 | Бариера | Barrier/Gate | TYP:0x6406 | barrier=gate | normal | 41.5502286, 24.7455668 |
|
||||
| 0x6407 | Пикник | Picnic Site | TYP:0x6407 | tourism=picnic_site | normal | 41.9156528, 25.3233576 |
|
||||
| 0x6408 | ПСС | Mountain Rescue | TYP:0x6408 | emergency=mountain_rescue | critical | 42.6026416, 23.2414269 |
|
||||
| 0x6409 | Паметник | Monument | TYP:0x6409 | historic=monument | normal | 43.1282043, 23.6027098 |
|
||||
| 0x640a | Опасност | Danger | TYP:0x640a | hazard=yes | normal | 43.1507993, 25.3284001 |
|
||||
| 0x640b | Параклис | Chapel | TYP:0x640b | amenity=place_of_worship | normal | 41.9598985, 24.3688774 |
|
||||
| 0x640c | Мина | Mine Shaft | TYP:0x640c | man_made=mine_shaft | normal | 43.0497336, 22.9328442 |
|
||||
| 0x640d | Лифт | Lift | TYP:0x640d | aerialway=station | normal | 42.6056242, 23.2995987 |
|
||||
| 0x640e | Спирка | Bus Stop/Station | TYP:0x640e | highway=bus_stop | normal | 43.3513427, 25.1430917 |
|
||||
| 0x640f | Гробище, оброк | Graveyard/Tombstone | TYP:0x640f | landuse=cemetery | normal | 42.0345068, 22.4919534 |
|
||||
| 0x6411 | Кула | Tower | TYP:0x6411 | man_made=tower | normal | 41.7411375, 24.8182654 |
|
||||
| 0x6412 | Начало пътека | Trailhead | TYP:0x6412 | unclassified | normal | — |
|
||||
| 0x6413 | Тунел | Tunnel | TYP:0x6413 | unclassified | normal | 42.6872063, 23.3418703 |
|
||||
| 0x6414 | Чешма | Drinking Water | TYP:0x6414 | amenity=drinking_water | critical | 43.1117034, 23.7205982 |
|
||||
| 0x6415 | Място за бивакуване | Camp Site | TYP:0x6415 | tourism=camp_site | critical | 41.7506647, 25.1023221 |
|
||||
| 0x6416 | Руини | Ruins | TYP:0x6416 | historic=ruins | normal | 41.5940452, 24.3094182 |
|
||||
| 0x6417 | Кладенец | Well | TYP:0x6417 | man_made=water_well | critical | 42.2628808, 24.2223430 |
|
||||
| 0x641a | Църква | Church | TYP:0x641a | amenity=place_of_worship | normal | 42.9599547, 23.0566549 |
|
||||
| 0x641b | Църква | Church | TYP:0x641b | unclassified | normal | — |
|
||||
| 0x641c | Спирка | Bus Stop/Station | TYP:0x641c | highway=bus_stop | normal | 42.8542113, 25.3382707 |
|
||||
| 0x6500 | Каптаж | Reservoir | TYP:0x6500 | man_made=water_intake | critical | 42.0310307, 24.7559094 |
|
||||
| 0x6501 | Ресторант | Restaurant | TYP:0x6501 | amenity=restaurant | normal | 43.3456135, 28.0715060 |
|
||||
| 0x6502 | Брод | River Passage | TYP:0x6502 | ford=yes | normal | 42.7929711, 25.2992177 |
|
||||
| 0x6503 | Кръстопът | Crossroad | TYP:0x6503 | unclassified | normal | 43.4149003, 28.4942007 |
|
||||
| 0x6505 | Магазин | Shop | TYP:0x6505 | unclassified | normal | — |
|
||||
| 0x6506 | Музей | Museum | TYP:0x6506 | tourism=museum | normal | 42.1463013, 24.7487855 |
|
||||
| 0x650b | Зимна маркировка | Winter Marking | TYP:0x650b | unclassified | normal | 42.0000887, 23.3820820 |
|
||||
| 0x650c | Задънена улица | Dead End | TYP:0x650c | unclassified | normal | — |
|
||||
| 0x6511 | Извор | Spring | TYP:0x6511 | natural=spring | critical | 42.8732872, 23.3371925 |
|
||||
| 0x6600 | Кота | Triangulation | TYP:0x6600 | unclassified | terrain | 41.2312603, 22.6785922 |
|
||||
| 0x6601 | Природен феномен | Natural Phenomenon | TYP:0x6601 | unclassified | terrain | 42.9589677, 25.2119064 |
|
||||
| 0x6602 | Гледка | Viewpoint | TYP:0x6602 | tourism=viewpoint | normal | 41.7438412, 23.4641790 |
|
||||
| 0x6603 | Кота | Triangulation | TYP:0x6603 | man_made=survey_point | normal | 42.5520873, 23.8540435 |
|
||||
| 0x6608 | Хотел | Hotel | TYP:0x6608 | tourism=hotel | critical | 42.8794885, 27.8957462 |
|
||||
| 0x6609 | Хижа | Hut | TYP:0x6609 | tourism=alpine_hut | critical | 43.2779145, 27.1159101 |
|
||||
| 0x660a | Заслон | Refuge | TYP:0x660a | amenity=shelter;shelter_type=basic_hut | critical | 42.8157377, 23.8160634 |
|
||||
| 0x660b | Маркировка | Mark | TYP:0x660b | unclassified | normal | 43.9378452, 22.4072385 |
|
||||
| 0x6610 | Поляна | Meadow | TYP:0x6610 | unclassified | terrain | 42.8212094, 26.1542845 |
|
||||
| 0x6613 | Водопад | Waterfall | TYP:0x6613 | waterway=waterfall | normal | 42.7435327, 24.6398234 |
|
||||
| 0x6614 | Скала | Rock | TYP:0x6614 | natural=rock | terrain | 42.7437901, 25.2693915 |
|
||||
| 0x6615 | Пещера | Cave | TYP:0x6615 | natural=cave_entrance | normal | 41.8003178, 23.3853436 |
|
||||
| 0x6617 | Връх | Peak | TYP:0x6617 | natural=peak | normal | 41.7893529, 22.9647946 |
|
||||
| 0x6618 | Дърво | Tree | TYP:0x6618 | natural=tree | normal | 42.7752686, 23.9033318 |
|
||||
| 0x6619 | Праисторически обект | Prehistoric site | TYP:0x6619 | historic=archaeological_site | normal | 42.5703692, 25.3767228 |
|
||||
| 0x661a | Античен обект | Antiquity site | TYP:0x661a | historic=archaeological_site | normal | 41.7153454, 26.0307527 |
|
||||
| 0x661b | Средновековен обект | Medieval site | TYP:0x661b | historic=archaeological_site | normal | 42.0922279, 24.2505813 |
|
||||
|
||||
**Caveats:** “unclassified” means the converter retains provenance but does not forge an amenity to force an icon. `zoom_hint` is nonfunctional on stock OsmAnd unless a style is separately changed. Neither legend-derived geometry nor icons may be redistributed under arbitrary licenses.
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"status": "PASS_preparation_only",
|
||||
"subset": "Vihren region; no national OSM/OBF build performed",
|
||||
"bbox": "23.30,41.67,23.50,41.84",
|
||||
"workers": 4,
|
||||
"total_pois": 1915,
|
||||
"unknown_fallback": 150,
|
||||
"spot_heights_preserved": 296,
|
||||
"unclassified": 0,
|
||||
"hiking_route_relations": 82,
|
||||
"candidate_overlay_segments": 273,
|
||||
"caveat": "Serial route grouping differs (78 vs 82). OBF rendering not validated."
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"release": "v0.92",
|
||||
"test_environment": "Linux runtime, C++17, Python 3",
|
||||
"unit_tests": "3/3 CTest cases passed; v0.92 suite 5 individual tests",
|
||||
"source_img_size_bytes": 441942016,
|
||||
"vihren": {
|
||||
"bbox": "23.36,41.70,23.42,41.76",
|
||||
"pois": 221,
|
||||
"ways": 5597,
|
||||
"colored_source_segments": 53,
|
||||
"hiking_route_relations": 17,
|
||||
"physical_highway_segments_reused": 48,
|
||||
"unmatched_overlay_segments": 5,
|
||||
"serial_vs_parallel": "identical counts, mapping, and relation statistics"
|
||||
},
|
||||
"plovdiv": {
|
||||
"bbox": "24.63,42.02,24.67,42.05",
|
||||
"example_pois": [
|
||||
{
|
||||
"type": "0x6602",
|
||||
"lat": 42.0346141,
|
||||
"lon": 24.6528697,
|
||||
"osm": "tourism=viewpoint"
|
||||
},
|
||||
{
|
||||
"type": "0x6500",
|
||||
"lat": 42.0347643,
|
||||
"lon": 24.6522689,
|
||||
"osm": "man_made=water_intake"
|
||||
},
|
||||
{
|
||||
"type": "0x6500",
|
||||
"lat": 42.0340991,
|
||||
"lon": 24.6520114,
|
||||
"osm": "man_made=water_intake"
|
||||
}
|
||||
]
|
||||
},
|
||||
"elevation": {
|
||||
"bbox": "22.29,43.88,22.34,43.93",
|
||||
"spot_height_points": 23,
|
||||
"with_ele_tag": 23
|
||||
},
|
||||
"future_template": {
|
||||
"tested": true,
|
||||
"discovered_poi_types_in_bbox": 21,
|
||||
"discovered_line_types_in_bbox": 16
|
||||
},
|
||||
"obf_build": {
|
||||
"status": "NOT_TESTED",
|
||||
"reason": "Official OsmAndMapCreator is not installed and runtime has no network access"
|
||||
},
|
||||
"stock_icon_visibility": {
|
||||
"status": "NOT_VERIFIED",
|
||||
"reason": "Stock OsmAnd icon and zoom behaviour depends on app version, built-in map style and settings"
|
||||
},
|
||||
"notes": [
|
||||
"zero independent Garmin icon PNGs packaged",
|
||||
"two editable CSV mapping files",
|
||||
"C++ jobs default 1",
|
||||
"Native OSM route relations, no custom phone renderer"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"release": "v0.95",
|
||||
"code_groups": 6,
|
||||
"test_groups_passed": 6,
|
||||
"real_img_source": "gmapsupp(2).img (original supplied, 441942016 bytes)",
|
||||
"real_regional_bbox": "25.05,42.43,25.16,42.54",
|
||||
"real_regional_pois": 427,
|
||||
"real_regional_routes": 6,
|
||||
"parallel_workers_tested": [
|
||||
1,
|
||||
8
|
||||
],
|
||||
"serial_parallel_poi_tags_names_equal": true,
|
||||
"conditional_dry_water_result": "man_made=water_tap;drinking_water=no; amenity=drinking_water absent",
|
||||
"java_real_obf_build": "NOT TESTED (official host unavailable)",
|
||||
"obf_mock_orchestration": "PASSED; not a real OBF binary"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"status": "PASS",
|
||||
"counts": {
|
||||
"pois": 101,
|
||||
"has_native_semantics": 43,
|
||||
"generated_name": 42,
|
||||
"has_bgm_poi_tag": 101,
|
||||
"needs_custom_style": 58
|
||||
},
|
||||
"catalog_types": 13,
|
||||
"exported_types": 13,
|
||||
"note": "POIs without standard OSM categories are valid for this PRIVATE map because bgm_poi uses custom index and renderer. They require importing the style; this does not prove OBF persistence or screen visibility.",
|
||||
"errors": [],
|
||||
"missing_types_from_catalog": []
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
mapset,kind,code,lat,lon,name
|
||||
"02234083","classic","0x6614",41.7204094,23.3722329,"Скала - 3310ACQWEHI"
|
||||
"02234083","classic","0x6616",41.7381334,23.3741426,"Гергийца (2589)"
|
||||
"02234083","classic","0x660c",41.7340136,23.3734131,"Порта"
|
||||
"02234083","classic","0x6616",41.7392492,23.3764601,"Безимен (2571)"
|
||||
"02234083","classic","0x6616",41.7404938,23.3828115,"Безимен (2554)"
|
||||
"02234083","classic","0x6616",41.7317605,23.3725762,"Безимен (2543)"
|
||||
"02234083","classic","0x6615",41.7433262,23.3739281,"Дупка"
|
||||
"02234083","classic","0x660c",41.7268896,23.3712673,"Синанишка порта"
|
||||
"02234083","classic","0x6600",41.7258811,23.3701301,"2467"
|
||||
"02234083","classic","0x6616",41.7444634,23.3775759,"Муратова чука (2449)"
|
||||
"02234083","classic","0x6503",41.7358160,23.3705378,"Рзкл"
|
||||
"02234083","classic","0x6414",41.7449141,23.3817816,"Вода (поток)"
|
||||
"02234083","classic","0x2800",41.7440128,23.3833480,"Гергийца"
|
||||
"02234083","classic","0x6600",41.7351937,23.4019947,"2592"
|
||||
"02234083","classic","0x6600",41.7336702,23.4037971,"2609"
|
||||
"02234083","classic","0x6616",41.7310739,23.4095478,"(2636)"
|
||||
"02234083","classic","0x660c",41.7308378,23.4024239,"Седл Малко-Голямо Спано поле-2527"
|
||||
"02234083","classic","0x6616",41.7301941,23.3993340,"Спанополски чукар (2576)"
|
||||
"02234083","classic","0x6600",41.7301941,23.3989048,"2576"
|
||||
"02234083","classic","0x6602",41.7262030,23.4098053,"Панорама Башлийски и Бъндеришки чукар"
|
||||
"02234083","classic","0x6614",41.7244649,23.4084320,"Скала - ZZC90NYTOCF"
|
||||
"02234083","classic","0x6600",41.7223191,23.4024024,"2404"
|
||||
"02234083","classic","0x650b",41.7234993,23.3853221,"Зимна маркировка - QRY7DY72FMJ"
|
||||
"02234083","classic","0x650b",41.7258811,23.3845925,"Зимна маркировка - 8JT9VM2UB7C"
|
||||
"02234083","classic","0x650b",41.7268896,23.3845067,"Зимна маркировка - L9KQU03N0Y5"
|
||||
"02234083","classic","0x650b",41.7277479,23.3846354,"Зимна маркировка - BRWNXVGHJ2A"
|
||||
"02234083","classic","0x650b",41.7285633,23.3850217,"Зимна маркировка - QCHZ1FXG1S6"
|
||||
"02234083","classic","0x650b",41.7290139,23.3851933,"Зимна маркировка - L9UJHM8M3L5"
|
||||
"02234083","classic","0x650b",41.7302155,23.3854723,"Зимна маркировка - ZLGO6R0CWUL"
|
||||
"02234083","classic","0x6511",41.7302585,23.3907509,"Вода - Извор - SZ7MXII06CB"
|
||||
"02234083","classic","0x650b",41.7324471,23.3863521,"Зимна маркировка - 0PO1Y4QNTKQ"
|
||||
"02234083","classic","0x650b",41.7333913,23.3868670,"Зимна маркировка - S0OOBD9DMT9"
|
||||
"02234083","classic","0x650b",41.7340779,23.3873820,"Зимна маркировка - ZJJ69H95896"
|
||||
"02234083","classic","0x650b",41.7346787,23.3876395,"Зимна маркировка - LRUKQQLW46P"
|
||||
"02234083","classic","0x650b",41.7201734,23.3826399,"Зимна маркировка - ECDE8HOEXT6"
|
||||
"02234083","classic","0x650b",41.7204738,23.3879399,"Зимна маркировка - BJH9BWY0S43"
|
||||
"02234083","classic","0x650b",41.7204738,23.3852148,"Зимна маркировка - EON21NTVSS2"
|
||||
"02234083","classic","0x650b",41.7205381,23.3832836,"Зимна маркировка - BJ2IFV25F7B"
|
||||
"02234083","classic","0x650b",41.7206669,23.3861375,"Зимна маркировка - KN9AQOZ1709"
|
||||
"02234083","classic","0x650b",41.7206669,23.3843994,"Зимна маркировка - UPFR04SCKJ6"
|
||||
"02234083","classic","0x6503",41.7207956,23.3865023,"Рзкл жълта/кафява маркировка"
|
||||
"02234083","classic","0x650b",41.7208600,23.3870173,"Зимна маркировка - ETSMTX323F0"
|
||||
"02234083","classic","0x650b",41.7209244,23.3838630,"Зимна маркировка - 2CH7W5QYBST"
|
||||
"02234083","classic","0x650b",41.7210317,23.3819532,"Зимна маркировка - 0H20QTLK2D8"
|
||||
"02234083","classic","0x650b",41.7217612,23.3817601,"Зимна маркировка - UR9S90UU4PI"
|
||||
"02234083","classic","0x650b",41.7224050,23.3814812,"Зимна маркировка - 0WITPVLXNZ5"
|
||||
"02234083","classic","0x650b",41.7226624,23.3854723,"Зимна маркировка - KKJ6VOB1UHB"
|
||||
"02234083","classic","0x6503",41.7229199,23.3789277,"Рзкл зимна"
|
||||
"02234083","classic","0x640a",41.7336702,23.4035397,"Опасно слизане"
|
||||
"02234083","classic","0x2800",41.7288423,23.3857727,"Големо Спано поле"
|
||||
"02234083","classic","0x2800",41.7323613,23.4036040,"Караулите"
|
||||
"02234083","classic","0x2800",41.7231774,23.4089899,"Малко Спано поле"
|
||||
"02234083","classic","0x6600",41.7415237,23.3972311,"2501"
|
||||
"02234083","classic","0x660c",41.7417812,23.3968449,"Бъндеришка порта"
|
||||
"02234083","classic","0x6600",41.7420816,23.3896351,"2468"
|
||||
"02234083","classic","0x6600",41.7421889,23.4056425,"2351"
|
||||
"02234083","classic","0x6600",41.7431545,23.4037542,"2314"
|
||||
"02234083","classic","0x6600",41.7439270,23.4045053,"2304"
|
||||
"02234083","classic","0x6600",41.7442489,23.4093547,"2229"
|
||||
"02234083","classic","0x6616",41.7449570,23.3969307,"Муратов връх (2669)"
|
||||
"02234083","classic","0x6600",41.7452145,23.4076166,"2241"
|
||||
"02234083","classic","0x6600",41.7461371,23.4070802,"2248"
|
||||
"02234083","classic","0x660c",41.7486048,23.3911800,"Муратова премка"
|
||||
"02234083","classic","0x660c",41.7495489,23.3981752,"Влахински превал"
|
||||
"02234083","classic","0x660c",41.7514372,23.3985186,"Влахински превал"
|
||||
"02234083","classic","0x6616",41.7519951,23.3843350,"Гредаро (2605)"
|
||||
"02234083","classic","0x6602",41.7353010,23.4009218,"Иглата на Караулите"
|
||||
"02234083","classic","0x6600",41.7354727,23.4006214,"2586"
|
||||
"02234083","classic","0x6600",41.7358804,23.3998060,"2578"
|
||||
"02234083","classic","0x6600",41.7360520,23.3994627,"2597"
|
||||
"02234083","classic","0x6600",41.7374468,23.4049559,"2504"
|
||||
"02234083","classic","0x6503",41.7538404,23.3998060,"Рзкл зимно ребро от Муратово езеро"
|
||||
"02234083","classic","0x6511",41.7506433,23.3959866,"Вода - Извор - D7562E5MBLA"
|
||||
"02234083","classic","0x650b",41.7363524,23.3882833,"Зимна маркировка - ML7GHHLGGVB"
|
||||
"02234083","classic","0x6503",41.7493987,23.3964372,"Кръстопът - JS2LQVK6Z0Y"
|
||||
"02234083","classic","0x6503",41.7450428,23.4058356,"Рзкл Муратово ез."
|
||||
"02234083","classic","0x650b",41.7410517,23.3955145,"Зимна маркировка - ZVN3B0ZG6Z7"
|
||||
"02234083","classic","0x650b",41.7415667,23.3965015,"Зимна маркировка - C0GJITD9Q3J"
|
||||
"02234083","classic","0x6511",41.7416954,23.3946991,"Вода - Извор - YY24QUNHLSL"
|
||||
"02234083","classic","0x650b",41.7419100,23.3969736,"Зимна маркировка - N2XVWARW03F"
|
||||
"02234083","classic","0x650b",41.7400217,23.3932185,"Зимна маркировка - T5WTMHMWT4K"
|
||||
"02234083","classic","0x650b",41.7395926,23.3919525,"Зимна маркировка - UFCHM8SJ1D7"
|
||||
"02234083","classic","0x650b",41.7391205,23.3905363,"Зимна маркировка - O9IF7BPSJR6"
|
||||
"02234083","classic","0x650b",41.7378759,23.3894420,"Зимна маркировка - XCWILHV9I1K"
|
||||
"02234083","classic","0x6503",41.7445707,23.4080887,"Рзкл Муратово езеро"
|
||||
"02234083","classic","0x6414",41.7549562,23.3888626,"Вода - Чешма - XSX8SGXW0S5"
|
||||
"02234083","classic","0x6414",41.7544627,23.3887553,"Вода - Чешма - X8HNWYW73EC"
|
||||
"02234083","classic","0x6415",41.7402792,23.3953857,"Бивак"
|
||||
"02234083","classic","0x640a",41.7359018,23.3995056,"Опасно слизане"
|
||||
"02234083","classic","0x6415",41.7453432,23.4067583,"Бивак"
|
||||
"02234083","classic","0x6414",41.7509651,23.4011364,"Вода - Чешма - 4DAF49MGS8O"
|
||||
"02234083","classic","0x640a",41.7456007,23.3963728,"Опасно слизане"
|
||||
"02234083","classic","0x6414",41.7461586,23.4052134,"Вода (поток)"
|
||||
"02234083","classic","0x6414",41.7388630,23.3864164,"Вода - Чешма - 6B4RAQRPAHT"
|
||||
"02234083","classic","0x6414",41.7479610,23.4026814,"Вода - Чешма - B107V8QHR57"
|
||||
"02234083","classic","0x6414",41.7468023,23.3872318,"Вода (поток)"
|
||||
"02234083","classic","0x6415",41.7465878,23.4065437,"Бивак"
|
||||
"02234083","classic","0x2800",41.7509651,23.3984327,"Влахински превал"
|
||||
"02234083","classic","0x2800",41.7442060,23.4046984,"Муратов циркус"
|
||||
"02234083","classic","0x2800",41.7368460,23.3897853,"Циркус Големо Спано поле"
|
||||
"02234083","classic","0x2800",41.7449570,23.4051275,"Овинати езера"
|
||||
|
@@ -0,0 +1,14 @@
|
||||
code,kind,count,named,default_tags,has_typ_icon,configured_rules,example1_lat,example1_lon,example1_name,example1_map,example2_lat,example2_lon,example2_name,example2_map,example3_lat,example3_lon,example3_name,example3_map,example4_lat,example4_lon,example4_name,example4_map,example5_lat,example5_lon,example5_name,example5_map
|
||||
"0x2800",classic,8,8,"place=locality",no,1,41.7440128,23.3833480,"Гергийца","02234083",41.7509651,23.3984327,"Влахински превал","02234083",41.7288423,23.3857727,"Големо Спано поле","02234083",41.7231774,23.4089899,"Малко Спано поле","02234083",41.7442060,23.4046984,"Муратов циркус","02234083"
|
||||
"0x640a",classic,3,3,"garmin:legend=danger",yes,1,41.7456007,23.3963728,"Опасно слизане","02234083",41.7359018,23.3995056,"Опасно слизане","02234083",41.7336702,23.4035397,"Опасно слизане","02234083",,,,,,,,
|
||||
"0x6414",classic,8,8,"garmin:legend=drinking_water;amenity=drinking_water",yes,1,41.7544627,23.3887553,"Вода - Чешма - X8HNWYW73EC","02234083",41.7388630,23.3864164,"Вода - Чешма - 6B4RAQRPAHT","02234083",41.7468023,23.3872318,"Вода (поток)","02234083",41.7509651,23.4011364,"Вода - Чешма - 4DAF49MGS8O","02234083",41.7449141,23.3817816,"Вода (поток)","02234083"
|
||||
"0x6415",classic,3,3,"garmin:legend=camp_site;tourism=camp_site",yes,1,41.7402792,23.3953857,"Бивак","02234083",41.7453432,23.4067583,"Бивак","02234083",41.7465878,23.4065437,"Бивак","02234083",,,,,,,,
|
||||
"0x6503",classic,7,7,"garmin:legend=crossroad",yes,1,41.7538404,23.3998060,"Рзкл зимно ребро от Муратово езеро","02234083",41.7445707,23.4080887,"Рзкл Муратово езеро","02234083",41.7358160,23.3705378,"Рзкл","02234083",41.7493987,23.3964372,"Кръстопът - JS2LQVK6Z0Y","02234083",41.7229199,23.3789277,"Рзкл зимна","02234083"
|
||||
"0x650b",classic,31,31,"garmin:legend=winter_marking",yes,1,41.7419100,23.3969736,"Зимна маркировка - N2XVWARW03F","02234083",41.7285633,23.3850217,"Зимна маркировка - QCHZ1FXG1S6","02234083",41.7205381,23.3832836,"Зимна маркировка - BJ2IFV25F7B","02234083",41.7395926,23.3919525,"Зимна маркировка - UFCHM8SJ1D7","02234083",41.7324471,23.3863521,"Зимна маркировка - 0PO1Y4QNTKQ","02234083"
|
||||
"0x6511",classic,3,3,"garmin:legend=spring;natural=spring",yes,1,41.7506433,23.3959866,"Вода - Извор - D7562E5MBLA","02234083",41.7416954,23.3946991,"Вода - Извор - YY24QUNHLSL","02234083",41.7302585,23.3907509,"Вода - Извор - SZ7MXII06CB","02234083",,,,,,,,
|
||||
"0x6600",classic,17,17,"garmin:legend=triangulation",yes,1,41.7415237,23.3972311,"2501","02234083",41.7421889,23.4056425,"2351","02234083",41.7442489,23.4093547,"2229","02234083",41.7452145,23.4076166,"2241","02234083",41.7336702,23.4037971,"2609","02234083"
|
||||
"0x6602",classic,2,2,"garmin:legend=viewpoint;tourism=viewpoint",yes,1,41.7262030,23.4098053,"Панорама Башлийски и Бъндеришки чукар","02234083",41.7353010,23.4009218,"Иглата на Караулите","02234083",,,,,,,,,,,,
|
||||
"0x660c",classic,7,7,"natural=saddle",yes,1,41.7514372,23.3985186,"Влахински превал","02234083",41.7417812,23.3968449,"Бъндеришка порта","02234083",41.7495489,23.3981752,"Влахински превал","02234083",41.7486048,23.3911800,"Муратова премка","02234083",41.7340136,23.3734131,"Порта","02234083"
|
||||
"0x6614",classic,2,2,"garmin:legend=rock",yes,1,41.7204094,23.3722329,"Скала - 3310ACQWEHI","02234083",41.7244649,23.4084320,"Скала - ZZC90NYTOCF","02234083",,,,,,,,,,,,
|
||||
"0x6615",classic,1,1,"garmin:legend=cave;natural=cave_entrance",yes,1,41.7433262,23.3739281,"Дупка","02234083",,,,,,,,,,,,,,,,
|
||||
"0x6616",classic,9,9,"natural=peak",no,1,41.7404938,23.3828115,"Безимен (2554)","02234083",41.7381334,23.3741426,"Гергийца (2589)","02234083",41.7317605,23.3725762,"Безимен (2543)","02234083",41.7444634,23.3775759,"Муратова чука (2449)","02234083",41.7301941,23.3993340,"Спанополски чукар (2576)","02234083"
|
||||
|
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"source_pois": 101,
|
||||
"expected_after_drops": 101,
|
||||
"declared_drops": 0,
|
||||
"osm_pois": 101,
|
||||
"missing": 0,
|
||||
"extra": 0,
|
||||
"rule_tag_violations": {},
|
||||
"examples": [],
|
||||
"status": "PASS",
|
||||
"limitation": "Same IMG decoder is used for manifest and OSM: this validates export fidelity, not ground truth or OBF persistence."
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Prefer existing physical highway ways as native hiking-route relation members.
|
||||
Never invent highway=path. Only attach where exact coordinate-node edge coverage >=90%
|
||||
and physical-way overrun <=10%; otherwise preserve original overlay member.
|
||||
"""
|
||||
import argparse,collections,json,pathlib,xml.etree.ElementTree as ET
|
||||
from native_translate import mapping,tagdict,top_level_elements
|
||||
|
||||
def edges(refs):
|
||||
for a,b in zip(refs,refs[1:]):
|
||||
if a!=b:yield (a,b) if a<b else (b,a)
|
||||
|
||||
def match(src,dst,line_cfg,report):
|
||||
candidates={c for c,r in mapping(line_cfg).items() if r.get('route_mode')=='relation'}
|
||||
overlay_refs={};lookup=collections.defaultdict(set)
|
||||
with open(src,'rb') as f:
|
||||
for e in top_level_elements(f):
|
||||
if e.tag=='way':
|
||||
t=tagdict(e)
|
||||
if t.get('garmin:type') in candidates and t.get('garmin:feature')=='extended_polyline':
|
||||
oid=int(e.get('id'))
|
||||
ns=[int(n.get('ref')) for n in e.findall('nd')]
|
||||
if len(ns)>=2:
|
||||
overlay_refs[oid]=ns
|
||||
for edge in edges(ns):lookup[edge].add(oid)
|
||||
e.clear()
|
||||
elif e.tag in ('node','relation'):e.clear()
|
||||
matches=collections.defaultdict(collections.Counter);physical_len={};written=0
|
||||
with open(src,'rb') as f,open(dst,'w',encoding='utf-8') as out:
|
||||
out.write('<?xml version="1.0" encoding="UTF-8"?>\n<osm version="0.6" generator="garmin2osm-v0.92" upload="never">\n')
|
||||
replaced={}; relations=0
|
||||
for e in top_level_elements(f):
|
||||
if e.tag=='way':
|
||||
t=tagdict(e)
|
||||
if 'highway' in t:
|
||||
pid=int(e.get('id'));ns=[int(n.get('ref')) for n in e.findall('nd')]
|
||||
physical_len[pid]=max(0,len(ns)-1)
|
||||
for edge in edges(ns):
|
||||
for oid in lookup.get(edge,()):matches[oid][pid]+=1
|
||||
elif e.tag=='relation':
|
||||
if not replaced:
|
||||
for oid,refs in overlay_refs.items():
|
||||
nseg=max(1,len(refs)-1)
|
||||
for pid,matched in matches.get(oid,{}).most_common():
|
||||
# Not allowed to extend a colored marking beyond its actual physical segment.
|
||||
if matched/nseg>=0.9 and physical_len.get(pid,10**9)<=nseg*1.1:
|
||||
replaced[oid]=pid;break
|
||||
tt=tagdict(e)
|
||||
if tt.get('route')=='hiking' and tt.get('bgm:source_feature')=='extended_polyline':
|
||||
seen=set();relations+=1
|
||||
for m in list(e.findall('member')):
|
||||
val=int(m.get('ref'))
|
||||
ref=str(replaced.get(val,val))
|
||||
if ref in seen:e.remove(m)
|
||||
else:m.set('ref',ref);seen.add(ref)
|
||||
out.write(ET.tostring(e,encoding='unicode')+'\n');written+=1;e.clear()
|
||||
out.write('</osm>\n')
|
||||
summary={'candidate_overlay_ways':len(overlay_refs),'attached_to_existing_highway':len(replaced),
|
||||
'unmatched_overlay_members':len(overlay_refs)-len(replaced),'hiking_relations':relations,
|
||||
'policy':'Attach only with >=90% shared physical segments and <=10% physical overrun. No invented routable ways.'}
|
||||
pathlib.Path(report).write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
return summary
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--osm',required=True);p.add_argument('--out',required=True);p.add_argument('--lines',required=True);p.add_argument('--report',required=True)
|
||||
a=p.parse_args();print(json.dumps(match(a.osm,a.out,a.lines,a.report),ensure_ascii=False,indent=2))
|
||||
if __name__=='__main__':main()
|
||||
+143
-291
@@ -1,307 +1,159 @@
|
||||
#!/usr/bin/env python3
|
||||
"""One-command BGMountains IMG -> reviewed OSM -> custom indexed OsmAnd OBF.
|
||||
"""BGMountains v0.95 native OsmAnd pipeline: only TWO editable mapping files.
|
||||
|
||||
Only Python 3 stdlib needed for orchestration; CMake/C++/OsmAndMapCreator are external.
|
||||
Never needs extracted Garmin icons or custom styles on Android.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import csv
|
||||
import datetime as dt
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
import argparse,datetime,hashlib,json,os,pathlib,shutil,subprocess,sys,time,urllib.request,zipfile
|
||||
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
||||
TRANSLATOR=ROOT/'scripts/native_translate.py'
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
UPSTREAM = {
|
||||
'poi': 'https://raw.githubusercontent.com/osmandapp/OsmAnd-resources/master/poi/poi_types.xml',
|
||||
'rendering': 'https://raw.githubusercontent.com/osmandapp/OsmAnd-resources/master/obf_creation/rendering_types.xml'
|
||||
}
|
||||
|
||||
def call(argv, cwd=None, log=None, env=None):
|
||||
argv = [str(s) for s in argv]
|
||||
print('+', ' '.join(shlex_quote(s) for s in argv), flush=True)
|
||||
def run(*args,log=None,cwd=None,env=None):
|
||||
cmd=[str(x) for x in args];print('+',' '.join(cmd),flush=True)
|
||||
if log:
|
||||
with Path(log).open('w', encoding='utf-8') as output:
|
||||
r = subprocess.run(argv, cwd=cwd, env=env, stdout=output, stderr=subprocess.STDOUT)
|
||||
if r.returncode:
|
||||
print('Command failed; last log lines:', file=sys.stderr)
|
||||
print('\n'.join(Path(log).read_text(errors='replace').splitlines()[-35:]), file=sys.stderr)
|
||||
raise SystemExit(r.returncode)
|
||||
else:
|
||||
subprocess.run(argv, cwd=cwd, env=env, check=True)
|
||||
with open(log,'w',encoding='utf-8') as f:
|
||||
p=subprocess.run(cmd,stdout=f,stderr=subprocess.STDOUT,cwd=cwd,env=env)
|
||||
if p.returncode:
|
||||
print(''.join(pathlib.Path(log).read_text(errors='replace').splitlines(True)[-40:]),file=sys.stderr)
|
||||
raise RuntimeError('Command failed, see '+str(log))
|
||||
else:subprocess.run(cmd,check=True,cwd=cwd,env=env)
|
||||
|
||||
def shlex_quote(s):
|
||||
import shlex
|
||||
return shlex.quote(s)
|
||||
|
||||
def base_xml(provided, creator, key, cache, offline):
|
||||
if provided:
|
||||
p = Path(provided).expanduser().resolve()
|
||||
if not p.is_file(): raise SystemExit('Upstream XML not found: '+str(p))
|
||||
def creator_dir(x):
|
||||
if not x:return None
|
||||
p=pathlib.Path(x).expanduser().resolve();p=p.parent if p.name=='utilities.sh' else p
|
||||
if not (p/'utilities.sh').exists():raise FileNotFoundError('MapCreator utilities.sh missing: '+str(p))
|
||||
return p
|
||||
names = ['poi_types.xml'] if key == 'poi' else ['rendering_types.xml']
|
||||
if creator:
|
||||
for prefix in [creator, creator/'resources', creator/'config', creator/'poi', creator/'obf_creation']:
|
||||
for name in names:
|
||||
candidate = prefix/name
|
||||
if candidate.is_file(): return candidate.resolve()
|
||||
target = cache/names[0]
|
||||
if target.is_file(): return target
|
||||
if offline:
|
||||
raise SystemExit('Missing upstream '+names[0]+': pass --'+key+'-base FILE or allow download')
|
||||
target.parent.mkdir(parents=True,exist_ok=True)
|
||||
print('Fetching official OsmAnd '+names[0]+' (first run only) ...', flush=True)
|
||||
try:
|
||||
with urllib.request.urlopen(UPSTREAM[key], timeout=50) as u, target.open('wb') as out:
|
||||
shutil.copyfileobj(u,out)
|
||||
except Exception as ex:
|
||||
target.unlink(missing_ok=True)
|
||||
raise SystemExit('Cannot fetch official OsmAnd '+names[0]+': '+str(ex)+'\nProvide --'+key+'-base FILE manually.')
|
||||
return target
|
||||
|
||||
def catalog_rows(p):
|
||||
with Path(p).open('r',encoding='utf-8-sig',newline='') as f:
|
||||
rows=list(csv.DictReader(f))
|
||||
if not rows or not {'code','count','has_typ_icon'}.issubset(rows[0]):
|
||||
raise SystemExit('Invalid POI catalog (expected code,count,has_typ_icon): '+str(p))
|
||||
out=[]
|
||||
seen=set()
|
||||
for r in rows:
|
||||
code='0x'+format(int(r['code'],0),'x')
|
||||
if code in seen:raise SystemExit('Duplicate code in catalog: '+code)
|
||||
seen.add(code)
|
||||
out.append(dict(r,code=code,count=int(r['count'])))
|
||||
return sorted(out,key=lambda r:(-r['count'],int(r['code'],0)))
|
||||
def fetch_creator(target):
|
||||
extracted=target/'unpacked';c=list(extracted.rglob('utilities.sh')) if extracted.exists() else []
|
||||
if c:return c[0].parent
|
||||
target.mkdir(parents=True,exist_ok=True)
|
||||
url='https://download.osmand.net/latest-night-build/OsmAndMapCreator-main.zip'
|
||||
zip_path=target/'OsmAndMapCreator-main.zip'
|
||||
if not zip_path.exists():
|
||||
print('Fetching official MapCreator:',url)
|
||||
with urllib.request.urlopen(url,timeout=120) as src,open(zip_path,'wb') as dst:shutil.copyfileobj(src,dst)
|
||||
with zipfile.ZipFile(zip_path) as z:
|
||||
for entry in z.infolist():
|
||||
parts=pathlib.PurePosixPath(entry.filename)
|
||||
if parts.is_absolute() or '..' in parts.parts:raise ValueError('Unsafe creator ZIP entry')
|
||||
z.extractall(extracted)
|
||||
c=list(extracted.rglob('utilities.sh'))
|
||||
if not c:raise FileNotFoundError('Creator ZIP lacks utilities.sh')
|
||||
for f in extracted.rglob('*.sh'):f.chmod(f.stat().st_mode|0o111)
|
||||
return c[0].parent
|
||||
|
||||
def write_xml(tree,path):
|
||||
ET.indent(tree,space=' ')
|
||||
tree.write(path,encoding='utf-8',xml_declaration=True)
|
||||
ET.parse(path)
|
||||
|
||||
def make_configs(poi_base,render_base,catalog,folder,threshold,frequent_zoom=15,rare_zoom=17):
|
||||
# Keep entire upstream definitions! Replacing with a standalone tiny XML is
|
||||
# precisely what used to make ordinary semantic POIs disappear.
|
||||
poi=ET.parse(poi_base); proot=poi.getroot()
|
||||
if proot.tag!='poi_types':raise SystemExit('Not an OsmAnd poi_types.xml: '+str(poi_base))
|
||||
if any(c.get('name')=='bgmountains_raw' for c in proot.findall('poi_category')):
|
||||
raise SystemExit('Input poi_types.xml already has a bgmountains_raw category')
|
||||
cat=ET.SubElement(proot,'poi_category',name='bgmountains_raw', default_tag='bgm_poi', no_edit='true')
|
||||
for r in catalog:
|
||||
code=r['code']
|
||||
ET.SubElement(cat,'poi_type',name='bgm_'+code.replace('0x',''),tag='bgm_poi',value='type_'+code,no_edit='true')
|
||||
pa=folder/'poi_types.bgmountains.xml'
|
||||
write_xml(poi,pa)
|
||||
|
||||
rendering=ET.parse(render_base); rroot=rendering.getroot()
|
||||
if rroot.tag != 'osmand_types':
|
||||
# OsmAnd resources use the tag 'rendering_types'; fail instead of dropping all built-in types.
|
||||
raise SystemExit('Unexpected OsmAnd rendering_types.xml root: '+rroot.tag)
|
||||
cats=[c for c in rroot.iter('category') if c.get('name')=='user_defined']
|
||||
target=cats[0] if cats else ET.SubElement(rroot,'category',name='user_defined')
|
||||
for r in catalog:
|
||||
code=r['code']
|
||||
ET.SubElement(target,'type',tag='bgm_poi',value='type_'+code,
|
||||
minzoom=str(frequent_zoom) if r['count']>=threshold else str(rare_zoom))
|
||||
rp=folder/'rendering_types.bgmountains.xml'
|
||||
write_xml(rendering,rp)
|
||||
summary={
|
||||
'catalog_types':len(catalog),
|
||||
'poi_count_in_catalog':sum(r.get('sample_count',r['count']) for r in catalog),
|
||||
'types_at_or_above_threshold':sum(r['count']>=threshold for r in catalog),
|
||||
'frequent_index_minzoom':frequent_zoom, 'rare_index_minzoom':rare_zoom,
|
||||
'items_at_or_above_threshold':sum(r.get('sample_count',r['count']) for r in catalog if r['count']>=threshold),
|
||||
'types_with_extracted_icon':sum(r['has_typ_icon']=='yes' for r in catalog),
|
||||
'poi_base_sha256':hashlib.sha256(Path(poi_base).read_bytes()).hexdigest(),
|
||||
'rendering_base_sha256':hashlib.sha256(Path(render_base).read_bytes()).hexdigest(),
|
||||
'note':'Local-only compat data; synthetic information icons are not truth about an informational board. All Garmin hex POI types indexed; high-frequency raw types inserted into OBF map index from selected frequent zoom; low-frequency from selected rare zoom. Rendering exact Garmin icon PNGs requires installing compatible icons/style in OsmAnd, not merely compiling an OBF.'
|
||||
}
|
||||
(folder/'type-index-summary.json').write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
return pa,rp,summary
|
||||
|
||||
def make_style(icon_csv, folder, catalog):
|
||||
"""Optional addon for otherwise unclassified types using preinstalled OsmAnd icons.
|
||||
|
||||
This cannot install Garmin bitmap icons and must be enabled in OsmAnd.
|
||||
def disk_preflight(out, source_bytes, extra_gb=0):
|
||||
"""Conservative warning/blocking check before disk-heavy MapCreator stage.
|
||||
In experience 6 GiB OSM has required >25 GiB scratch. 5x + 1 GiB
|
||||
is intentionally approximate, not a guarantee of completion.
|
||||
"""
|
||||
known={r['code'] for r in catalog}
|
||||
with Path(icon_csv).open(encoding='utf-8-sig',newline='') as f:
|
||||
rows=list(csv.DictReader(f))
|
||||
visible=[]
|
||||
for r in rows:
|
||||
code='0x'+format(int(r['code'],0),'x')
|
||||
if code not in known:continue # bbox builds are smaller than national config
|
||||
if r['show_in_custom_style'].strip().lower() not in ('yes','true','1'):continue
|
||||
name=r['osmand_builtin_icon'].strip()
|
||||
if not name or not name.replace('_','').replace('-','').isalnum():
|
||||
raise SystemExit('Invalid OsmAnd icon in '+str(icon_csv)+' for '+code)
|
||||
zoom=int(r['minzoom'])
|
||||
if not 1<=zoom<=21:raise SystemExit('Invalid icon zoom for '+code)
|
||||
visible.append((code,name,zoom))
|
||||
root=ET.Element('renderingStyle',name='BGMountains custom POIs',depends='default',
|
||||
defaultColor='#fff8ee',version='1')
|
||||
order=ET.SubElement(root,'order')
|
||||
points=ET.SubElement(root,'point')
|
||||
for code,name,zoom in visible:
|
||||
ET.SubElement(order,'case',tag='bgm_poi',value='type_'+code,minzoom=str(zoom),
|
||||
point='true',objectType='1',order='128')
|
||||
ET.SubElement(points,'case',tag='bgm_poi',value='type_'+code,minzoom=str(zoom),
|
||||
icon=name,iconOrder='10',iconVisibleSize='12')
|
||||
path=folder/'BGMountains-custom.render.xml'
|
||||
write_xml(ET.ElementTree(root),path)
|
||||
return path,len(visible)
|
||||
|
||||
def obtain_creator(cache, offline=False):
|
||||
"""Opt-in download from official OsmAnd nightly build (large file)."""
|
||||
import zipfile
|
||||
link='https://download.osmand.net/latest-night-build/OsmAndMapCreator-main.zip'
|
||||
target=cache/'OsmAndMapCreator-main.zip'
|
||||
extracted=cache/'unpacked'
|
||||
found=list(extracted.rglob('utilities.sh')) if extracted.is_dir() else []
|
||||
if found:return found[0].parent
|
||||
if offline:raise SystemExit('Cannot download MapCreator with --offline; pass --creator')
|
||||
cache.mkdir(parents=True,exist_ok=True)
|
||||
if not target.is_file():
|
||||
print('Downloading official OsmAndMapCreator (~150 MB) ...',flush=True)
|
||||
try:
|
||||
with urllib.request.urlopen(link,timeout=180) as src,target.open('wb') as dest:
|
||||
shutil.copyfileobj(src,dest,length=2**20)
|
||||
except Exception as ex:
|
||||
target.unlink(missing_ok=True)
|
||||
raise SystemExit('Cannot download OsmAndMapCreator: '+str(ex)+'\nDownload manually: '+link)
|
||||
print('Extracting MapCreator...',flush=True)
|
||||
extracted.mkdir(parents=True,exist_ok=True)
|
||||
with zipfile.ZipFile(target) as z:
|
||||
for m in z.infolist():
|
||||
rel=Path(m.filename)
|
||||
if rel.is_absolute() or '..' in rel.parts:
|
||||
raise SystemExit('Unsafe file in MapCreator archive: '+m.filename)
|
||||
z.extract(m,extracted)
|
||||
found=list(extracted.rglob('utilities.sh'))
|
||||
if not found:raise SystemExit('MapCreator ZIP extracted but utilities.sh not found')
|
||||
for name in ('utilities.sh','inspector.sh'):
|
||||
for f in extracted.rglob(name):f.chmod(f.stat().st_mode|0o111)
|
||||
return found[0].parent
|
||||
|
||||
def resolve_creator(path):
|
||||
name = path or os.environ.get('OSMAND_MAPCREATOR')
|
||||
if not name:return None
|
||||
creator=Path(name).expanduser().resolve()
|
||||
if creator.is_file() and creator.name=='utilities.sh':creator=creator.parent
|
||||
if not (creator/'utilities.sh').is_file():
|
||||
raise SystemExit('Cannot find utilities.sh under '+str(creator))
|
||||
return creator
|
||||
gib=1024**3
|
||||
free=shutil.disk_usage(out).free
|
||||
required=max(int(2*gib),int(source_bytes*5 + gib),int(extra_gb*gib))
|
||||
if free<required:
|
||||
raise RuntimeError(f'Insufficient free space for OBF build: free={free/gib:.2f} GiB; '
|
||||
f'estimated minimum={required/gib:.2f} GiB. '
|
||||
'Use --prepare-only or set --ignore-disk-check if scratch resides on another filesystem.')
|
||||
print(f'Disk preflight OK: free={free/gib:.1f} GiB, estimated minimum={required/gib:.1f} GiB',flush=True)
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__,formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
inp=p.add_mutually_exclusive_group(required=True)
|
||||
inp.add_argument('--img',help='Original Garmin IMG; converter is compiled and executed')
|
||||
inp.add_argument('--osm',help='Already converted OSM XML; must contain bgm_poi=type_0x... tags (v0.7+)')
|
||||
p.add_argument('--creator',help='OsmAndMapCreator directory containing utilities.sh (or OSMAND_MAPCREATOR env)')
|
||||
p.add_argument('--download-creator',action='store_true',help='Download official OsmAndMapCreator ZIP automatically (~150 MB) when no --creator is set')
|
||||
p.add_argument('--out',default='build/bgmountains-output',help='Output folder')
|
||||
p.add_argument('--jobs',type=int,default=1,help='C++ IMG reader threads (opt-in)')
|
||||
p.add_argument('--bbox',help='Optional W,S,E,N bounds for small-area test')
|
||||
p.add_argument('--threshold',type=int,default=100,help='Frequent-type occurrence threshold')
|
||||
p.add_argument('--frequent-zoom',type=int,default=15,help='Store frequent raw map icons at zoom N and above')
|
||||
p.add_argument('--rare-zoom',type=int,default=17,help='Store rare raw map icons at zoom N and above')
|
||||
p.add_argument('--icons-config',default=str(ROOT/'config/osmand-icons.csv'),help='OsmAnd built-in icon choices for custom addon style')
|
||||
p.add_argument('--poi-overrides',default=str(ROOT/'config/poi-overrides-all-types.csv'))
|
||||
p.add_argument('--poi-name-fixes',default=str(ROOT/'config/poi-name-fixes.csv'))
|
||||
p.add_argument('--poi-labels',default=str(ROOT/'config/poi-labels-bg.csv'),help='Bulgarian prefixes for stable generated names on nameless Garmin POIs')
|
||||
p.add_argument('--poi-additions',default=str(ROOT/'config/poi-additions.csv'))
|
||||
p.add_argument('--poi-base',help='Official upstream poi_types.xml; if omitted use cache/download')
|
||||
p.add_argument('--rendering-base',help='Official upstream rendering_types.xml; if omitted use cache/download')
|
||||
p.add_argument('--offline',action='store_true',help='Never download upstream XML')
|
||||
p.add_argument('--prepare-only',action='store_true',help='Do not run OsmAndMapCreator, but generate OSM and XMLs')
|
||||
p.add_argument('--skip-qa',action='store_true',help='Skip OSM comparison for faster repeat runs')
|
||||
p.add_argument('--java-opts',default='-Xms512M -Xmx8192M',help='JAVA_OPTS for OsmAndMapCreator')
|
||||
p.add_argument('--skip-cpp-build',action='store_true')
|
||||
p.add_argument('--catalog',help='Preexisting POI catalog when supplying --osm (otherwise use packaged nationwide catalog)')
|
||||
p.add_argument('--frequency-catalog',default=str(ROOT/'data/BGMountains-unique-poi-types.csv'),help='Reference nationwide counts for map index zoom decisions even in --bbox builds')
|
||||
args=p.parse_args()
|
||||
if args.jobs<1 or args.jobs>64 or args.threshold<1 or not 1<=args.frequent_zoom<=21 or not 1<=args.rare_zoom<=21: p.error('--jobs 1..64; threshold >=1; zoom 1..21')
|
||||
dest=Path(args.out).expanduser().resolve();dest.mkdir(parents=True,exist_ok=True)
|
||||
creator=resolve_creator(args.creator)
|
||||
if not creator and args.download_creator and not args.prepare_only:
|
||||
creator=obtain_creator(ROOT/'build'/'mapcreator-download',args.offline)
|
||||
if not creator and not args.prepare_only:p.error('Supply --creator /path/to/OsmAndMapCreator (or OSMAND_MAPCREATOR env) or use --prepare-only')
|
||||
if args.img:
|
||||
if not Path(args.img).is_file():p.error('IMG file missing: '+args.img)
|
||||
binary=ROOT/'build'/'garmin2osm'
|
||||
if not args.skip_cpp_build or not binary.is_file():
|
||||
call(['cmake','-S',ROOT,'-B',ROOT/'build','-DCMAKE_BUILD_TYPE=Release'])
|
||||
call(['cmake','--build',ROOT/'build','--config','Release','-j','2'])
|
||||
osm=dest/'BGMountains.osm'
|
||||
cmd=[binary,'--input',Path(args.img).resolve(),'--output',osm,'--poi-overrides',Path(args.poi_overrides).resolve(),
|
||||
'--poi-name-fixes',Path(args.poi_name_fixes).resolve(),'--poi-labels',Path(args.poi_labels).resolve(),'--poi-additions',Path(args.poi_additions).resolve(),
|
||||
'--poi-manifest',dest/'source-pois.csv','--poi-types-out',dest/'poi-types.csv','--jobs',args.jobs]
|
||||
if args.bbox:cmd+=['--bbox',args.bbox]
|
||||
call(cmd,log=dest/'converter.log')
|
||||
catalog=dest/'poi-types.csv'
|
||||
if not args.skip_qa:
|
||||
call([sys.executable,ROOT/'tools/validate_pois.py','compare','--source',dest/'source-pois.csv','--osm',osm,
|
||||
'--rules',Path(args.poi_overrides).resolve(),'--report',dest/'validation.json','--mismatches',dest/'mismatches.csv'],
|
||||
log=dest/'validator.log')
|
||||
else:
|
||||
osm=Path(args.osm).expanduser().resolve()
|
||||
if not osm.is_file():p.error('OSM file missing: '+str(osm))
|
||||
catalog=Path(args.catalog).expanduser().resolve() if args.catalog else ROOT/'data'/'BGMountains-unique-poi-types.csv'
|
||||
call([sys.executable,ROOT/'tools/audit_icon_coverage.py', '--osm',osm, '--catalog',catalog,
|
||||
'--csv',dest/'poi-coverage.csv','--json',dest/'poi-coverage.json'],
|
||||
log=dest/'poi-coverage.log')
|
||||
rows=catalog_rows(catalog)
|
||||
nationwide={r['code']:r['count'] for r in catalog_rows(args.frequency_catalog)} if args.frequency_catalog else {}
|
||||
for row in rows:
|
||||
row['sample_count']=row['count']
|
||||
row['count']=nationwide.get(row['code'],row['count'])
|
||||
# A separately supplied legacy OSM might lack our new classifier.
|
||||
# For --img, the v0.7 converter above just generated it by construction.
|
||||
if args.osm:
|
||||
import gzip
|
||||
opener=gzip.open if osm.suffix=='.gz' else open
|
||||
with opener(osm,'rt',encoding='utf-8',errors='replace') as f:
|
||||
found=any('k="bgm_poi"' in line for line in f)
|
||||
if not found:
|
||||
raise SystemExit('Existing OSM lacks bgm_poi; re-extract with v0.7 C++ converter.')
|
||||
folder=dest/'osmand-config';folder.mkdir(exist_ok=True)
|
||||
poi_base=base_xml(args.poi_base,creator,'poi',dest/'upstream',args.offline)
|
||||
rendering_base=base_xml(args.rendering_base,creator,'rendering',dest/'upstream',args.offline)
|
||||
poi_cfg,render_cfg,summary=make_configs(poi_base,rendering_base,rows,folder,args.threshold,args.frequent_zoom,args.rare_zoom)
|
||||
style,nicons=make_style(args.icons_config,folder,rows)
|
||||
print('Indexed POI types:',summary['catalog_types'],'frequent:',summary['types_at_or_above_threshold'],'custom style icons:',nicons,flush=True)
|
||||
if args.prepare_only:
|
||||
print('PREPARED (no OBF generated). Files at:',dest)
|
||||
p.add_argument('--img',help='Original Garmin IMG')
|
||||
p.add_argument('--osm',help='Already generated original Garmin-derived raw OSM')
|
||||
p.add_argument('--out',default='build/bgmountains-v095')
|
||||
p.add_argument('--poi-config',default=str(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'))
|
||||
p.add_argument('--line-config',default=str(ROOT/'config/line-mapping.csv'))
|
||||
p.add_argument('--jobs',type=int,default=1,help='IMG workers: default 1; explicitly set N to parallelize')
|
||||
p.add_argument('--bbox',help='W,S,E,N region for smoke tests')
|
||||
p.add_argument('--template',action='store_true',help='Generate blank new-map mapping templates using this IMG (not BGMountains defaults)')
|
||||
p.add_argument('--attach-routes',action='store_true',help='Opt in to high-memory matching of trail overlays to physical paths (not recommended on full map)')
|
||||
p.add_argument('--no-route-attachment',action='store_true',help='Deprecated: attachment is disabled by default')
|
||||
p.add_argument('--prepare-only',action='store_true',help='Extract/translate/validate, do not compile OBF')
|
||||
p.add_argument('--creator',help='Existing OsmAndMapCreator directory containing utilities.sh')
|
||||
p.add_argument('--download-creator',action='store_true',help='Obtain official OsmAndMapCreator from download.osmand.net')
|
||||
p.add_argument('--java-opts',default='-Xms512M -Xmx8192M')
|
||||
p.add_argument('--keep-raw',action='store_true',help='Keep pre-translation OSM for debugging')
|
||||
p.add_argument('--ignore-disk-check',action='store_true',help='Override conservative OBF scratch space safety check')
|
||||
p.add_argument('--disk-headroom-gb',type=float,default=0,help='Minimum free GiB required before Java OBF build (0=auto estimate)')
|
||||
a=p.parse_args()
|
||||
if bool(a.img)==bool(a.osm):p.error('Provide exactly one of --img or --osm')
|
||||
if a.jobs<1 or a.jobs>64:p.error('--jobs must be 1..64')
|
||||
if not a.template:
|
||||
for typ,pth in (('POI',a.poi_config),('line',a.line_config)):
|
||||
if not pathlib.Path(pth).is_file():p.error(f'{typ} mapping file not found: {pth}')
|
||||
out=pathlib.Path(a.out).resolve();out.mkdir(parents=True,exist_ok=True)
|
||||
src=pathlib.Path(a.osm).resolve() if a.osm else None
|
||||
poi=pathlib.Path(a.poi_config).resolve();line=pathlib.Path(a.line_config).resolve()
|
||||
# Resolve MapCreator before running an expensive full-country extraction.
|
||||
# Missing download/network credentials must fail before consuming gigabytes.
|
||||
creator=None
|
||||
if not a.prepare_only and not a.template:
|
||||
creator=creator_dir(a.creator or os.environ.get('OSMAND_MAPCREATOR'))
|
||||
if not creator and a.download_creator:
|
||||
creator=fetch_creator(ROOT/'build'/'mapcreator')
|
||||
if not creator:
|
||||
p.error('OBF requires --creator DIR or --download-creator; for testing use --prepare-only.')
|
||||
bin_path=ROOT/'build'/'garmin2osm'
|
||||
if a.img:
|
||||
run('cmake','-S',ROOT,'-B',ROOT/'build','-DCMAKE_BUILD_TYPE=Release')
|
||||
run('cmake','--build',ROOT/'build','-j','2','--target','garmin2osm')
|
||||
src=out/'IMG-raw.osm'
|
||||
cmd=[str(bin_path),'--input',str(pathlib.Path(a.img).resolve()),'--output',str(src),
|
||||
'--poi-types-out',str(out/'discovered-pois.csv'),'--poi-manifest',str(out/'source-pois.csv'),
|
||||
'--jobs',str(a.jobs)]
|
||||
if a.bbox:cmd+=['--bbox',a.bbox]
|
||||
if not a.template:
|
||||
run(sys.executable,TRANSLATOR,'prepare','--poi',poi,'--lines',line,'--out',out)
|
||||
cmd+=['--poi-overrides',str(out/'cpp-poi-override.csv'),'--poi-labels',str(out/'cpp-name-labels.csv')]
|
||||
run(*cmd,log=out/'extract.log')
|
||||
if a.template:
|
||||
if not a.img:p.error('--template requires --img')
|
||||
run(sys.executable,TRANSLATOR,'template','--poi',out/'poi-mapping.TEMPLATE.csv',
|
||||
'--lines',out/'line-mapping.TEMPLATE.csv','--catalog',out/'discovered-pois.csv','--osm',src)
|
||||
print('Templates created. Review both files before using them for native OSM conversion.')
|
||||
return 0
|
||||
before={str(i.resolve()) for i in dest.glob('*.obf')}
|
||||
# Save Java logs, and preserve OsmAndMapCreator temp data inside user-selected output dir.
|
||||
env=os.environ.copy();env['JAVA_OPTS']=args.java_opts
|
||||
program=creator/'utilities.sh'
|
||||
cmd=[str(program),'generate-obf-no-address',str(osm),
|
||||
'--poi-types='+str(poi_cfg),'--rendering-types='+str(render_cfg)]
|
||||
call(cmd,cwd=dest,log=dest/'mapcreator.log',env=env)
|
||||
after=[i for i in dest.glob('*.obf') if str(i.resolve()) not in before]
|
||||
if not after:
|
||||
# Creator sometimes writes next to the .osm source, even with cwd changed.
|
||||
alt=list(osm.parent.glob('*.obf'))
|
||||
after=[i for i in alt if i.is_file()]
|
||||
if not after:raise SystemExit('MapCreator returned success but no .obf was found; see '+str(dest/'mapcreator.log'))
|
||||
built=max(after,key=lambda p:p.stat().st_mtime)
|
||||
print('OBF created:',built,'bytes:',built.stat().st_size)
|
||||
if (creator/'inspector.sh').is_file():
|
||||
call([str(creator/'inspector.sh'),'-vpoi',str(built)],cwd=dest,log=dest/'obf-poi-index.txt',env=env)
|
||||
(dest/'build-manifest.json').write_text(json.dumps({
|
||||
'created_at_utc':dt.datetime.now(dt.timezone.utc).isoformat(),
|
||||
'obf':str(built),'obf_bytes':built.stat().st_size,
|
||||
'input_osm':str(osm),'poi_types':str(poi_cfg),'rendering_types':str(render_cfg),
|
||||
'custom_style':str(style), 'custom_style_icon_rules':nicons,
|
||||
'summary':summary
|
||||
},ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
dst=out/'BGMountains-native.osm'
|
||||
run(sys.executable,TRANSLATOR,'transform','--poi',poi,'--lines',line,'--osm',src,
|
||||
'--out',dst,'--report',out/'route-report.json',log=out/'translate.log')
|
||||
if a.attach_routes and not a.no_route_attachment:
|
||||
attached=out/'BGMountains-native-attached.osm'
|
||||
run(sys.executable,ROOT/'scripts/attach_native_routes.py','--osm',dst,'--out',attached,
|
||||
'--lines',line,'--report',out/'route-attachment.json',log=out/'route-attachment.log')
|
||||
dst.unlink();attached.rename(dst)
|
||||
run(sys.executable,TRANSLATOR,'audit','--poi',poi,'--lines',line,'--osm',dst,
|
||||
'--out',out/'semantic-audit.json',log=out/'audit.log')
|
||||
# Fail closed if the translated file is not well-formed XML, including relation refs.
|
||||
import xml.etree.ElementTree as ET
|
||||
from native_translate import top_level_elements
|
||||
n=0
|
||||
for e in top_level_elements(dst):
|
||||
if e.tag=='relation':n+=1
|
||||
if not a.keep_raw and a.img:src.unlink(missing_ok=True)
|
||||
if a.prepare_only:
|
||||
print('PREPARED OSM:',dst,'route relations:',n,'No OBF compiled.')
|
||||
return 0
|
||||
if __name__=='__main__':
|
||||
sys.exit(main())
|
||||
if not a.ignore_disk_check:disk_preflight(out,dst.stat().st_size,a.disk_headroom_gb)
|
||||
env=os.environ.copy();env['JAVA_OPTS']=a.java_opts
|
||||
# utilities.sh versions differ in whether JAVA_OPTS is honored; this flag is
|
||||
# explicitly recognized by the JVM. It also reaches spawned Java processes.
|
||||
env['JAVA_TOOL_OPTIONS']=a.java_opts
|
||||
# Native types are built into OsmAnd: NO --poi-types/--rendering-types override,
|
||||
# NO custom .render.xml and NO extracted Garmin icons.
|
||||
started=time.time()
|
||||
run(creator/'utilities.sh','generate-obf-no-address',dst,log=out/'mapcreator.log',cwd=out,env=env)
|
||||
obfs=[f for f in out.glob('*.obf') if f.stat().st_mtime >= started-2 and f.stat().st_size>0]
|
||||
if not obfs:raise RuntimeError('Creator returned no OBF in '+str(out))
|
||||
obf=max(obfs,key=lambda x:x.stat().st_mtime)
|
||||
if (creator/'inspector.sh').exists():
|
||||
run(creator/'inspector.sh','-vpoi',obf,log=out/'obf-inspection.txt',cwd=out,env=env)
|
||||
(out/'build-manifest.json').write_text(json.dumps({
|
||||
'version':'v0.95','input':a.img or a.osm,'poi_config':str(poi),'line_config':str(line),
|
||||
'poi_config_sha256':hashlib.sha256(poi.read_bytes()).hexdigest(),
|
||||
'line_config_sha256':hashlib.sha256(line.read_bytes()).hexdigest(),
|
||||
'output_obf':str(obf),'obf_bytes':obf.stat().st_size,
|
||||
'compiled_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
'notice':'Native stock rendering. POI display zoom controlled by OsmAnd; no claim that all tags get icons.'},ensure_ascii=False,indent=2)+'\n')
|
||||
print('BUILT OBF:',obf)
|
||||
return 0
|
||||
if __name__=='__main__':sys.exit(main())
|
||||
|
||||
+2
-13
@@ -1,15 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# Compatibility shim. No manually supplied POI XML files are required anymore.
|
||||
# Preferred: python3 scripts/build_bgmountains.py --img gmapsupp.img --download-creator --out ./build/out
|
||||
set -euo pipefail
|
||||
if (( $# < 2 )); then
|
||||
echo "Usage: $0 CREATOR_DIR INPUT_v0.7.osm[.gz] [OUTPUT_DIR]" >&2
|
||||
echo "Recommended instead: python3 scripts/build_bgmountains.py --img gmapsupp.img --download-creator --out ./build/out" >&2
|
||||
exit 2
|
||||
fi
|
||||
creator="$1"; osm="$2"; out="${3:-$(dirname "$osm")/rebuilt-obf}"
|
||||
root="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source_dir="$(dirname "$osm")"
|
||||
args=(--osm "$osm" --creator "$creator" --out "$out")
|
||||
if [[ -f "$source_dir/poi-types.csv" ]]; then args+=(--catalog "$source_dir/poi-types.csv"); fi
|
||||
exec python3 "$root/scripts/build_bgmountains.py" "${args[@]}"
|
||||
cd "$(dirname "$0")/.."
|
||||
exec python3 scripts/build_bgmountains.py "$@"
|
||||
|
||||
Executable
+315
@@ -0,0 +1,315 @@
|
||||
#!/usr/bin/env python3
|
||||
"""v0.95: streaming IMG/OSM native-OsmAnd translation and honest QA.
|
||||
Only user-editable controls: config/BGMountains-native-poi-mapping-v094.csv and config/line-mapping.csv.
|
||||
"""
|
||||
import argparse,base64,collections,csv,hashlib,json,pathlib,re,sqlite3,sys,xml.etree.ElementTree as ET
|
||||
from xml.sax.saxutils import escape,quoteattr
|
||||
|
||||
def top_level_elements(source):
|
||||
"""Bounded-memory XML pull iterator. ElementTree .clear() alone leaves one
|
||||
empty Element per processed object in the root; clearing the root prevents
|
||||
nationwide Garmin files from exhausting RAM during multi-pass conversion.
|
||||
Callers must fully process each element before requesting the next.
|
||||
"""
|
||||
context=ET.iterparse(source,events=('start','end'))
|
||||
event, root=next(context)
|
||||
if event != 'start' or root.tag != 'osm':
|
||||
raise ValueError('Expected an OSM XML root element')
|
||||
depth=1
|
||||
for event,elem in context:
|
||||
if event=='start':depth+=1;continue
|
||||
if depth==2 and elem.tag in ('node','way','relation'):
|
||||
yield elem
|
||||
root.clear()
|
||||
depth-=1
|
||||
|
||||
def tagdict(elem):return {x.get('k'):x.get('v','') for x in elem.findall('tag')}
|
||||
def set_tags(elem,d):
|
||||
for child in list(elem):
|
||||
if child.tag=='tag':elem.remove(child)
|
||||
for k,v in d.items():
|
||||
if v is not None and v!='':ET.SubElement(elem,'tag',k=k,v=str(v))
|
||||
def read_csv(p):
|
||||
with open(p,encoding='utf-8-sig',newline='') as f:
|
||||
lines=(line for line in f if not line.startswith('#'))
|
||||
yield from csv.DictReader(lines)
|
||||
def code(value):return hex(int(value,0)).lower()
|
||||
def read_poi_configuration(path):
|
||||
"""Read one canonical POI config: unique base rule per code and optional
|
||||
ordered name_contains rules (a v0.94 feature previously lost in Python).
|
||||
The v0.93/0.94 15-column files remain backward-compatible.
|
||||
"""
|
||||
bases={}; conditional=[]
|
||||
for line_no,row in enumerate(read_csv(path),start=2):
|
||||
try:c=code(row['code'])
|
||||
except Exception as ex:raise ValueError(f'Invalid POI code at {path}:{line_no}: {row}') from ex
|
||||
row=dict(row);row['code']=c
|
||||
act=(row.get('action') or 'replace').strip().lower()
|
||||
if act not in ('replace','append','raw','drop'):
|
||||
raise ValueError(f'Invalid action at {path}:{line_no}: {act}')
|
||||
tags_from_string(row.get('osm_tags',''))
|
||||
if row.get('name_contains',''):
|
||||
conditional.append(row)
|
||||
else:
|
||||
if c in bases:raise ValueError(f'Duplicate POI base mapping {c} at {path}:{line_no}')
|
||||
bases[c]=row
|
||||
for row in conditional:
|
||||
if row['code'] not in bases:raise ValueError(f'Conditional rule has no base: {row["code"]}')
|
||||
return bases,conditional
|
||||
|
||||
def mapping(path):
|
||||
"""Base POI map; conditional rules are applied by the C++ override stage."""
|
||||
return read_poi_configuration(path)[0]
|
||||
|
||||
def line_mapping(path):
|
||||
ret={}
|
||||
for row in read_csv(path):
|
||||
try:c=code(row['code'])
|
||||
except Exception as ex:raise ValueError('Invalid line code in '+str(path)+': '+str(row)) from ex
|
||||
if c in ret:raise ValueError(f'Duplicate line code {c} in {path}')
|
||||
ret[c]=row
|
||||
return ret
|
||||
|
||||
def tags_from_string(v):
|
||||
d={}
|
||||
for x in v.split(';'):
|
||||
if not x:continue
|
||||
if '=' not in x:raise ValueError(f'Invalid OSM tag {x!r}')
|
||||
k,val=x.split('=',1)
|
||||
if not k or not val:raise ValueError(f'Invalid OSM tag {x!r}')
|
||||
if k in ('name','garmin:type','garmin:feature','garmin:mapset','bgm:generated_name'):
|
||||
raise ValueError('Reserved mapping key: '+k)
|
||||
d[k]=val
|
||||
return d
|
||||
|
||||
# User-requested PRIVATE-map presentation fallback. A locality is a safe stock-map
|
||||
# display tag, but this is synthetically applied to unknown source types and is
|
||||
# therefore NOT suitable for public OSM imports. Elevation annotations are excluded.
|
||||
SEMANTIC_KEYS = frozenset(('amenity','tourism','emergency','natural','man_made','place',
|
||||
'historic','shop','leisure','building','waterway','railway','power','aeroway',
|
||||
'highway','hazard','ford','water','water_source','landuse','barrier',
|
||||
'aerialway','craft','military','geological','public_transport','sport'))
|
||||
GENERATED_SUFFIX = re.compile(r' - ([A-Z0-9]{7,16})$')
|
||||
|
||||
def stable_unknown_hash(elem,tags):
|
||||
name=tags.get('name','')
|
||||
m=GENERATED_SUFFIX.search(name) if tags.get('bgm:generated_name') == 'yes' else None
|
||||
if m:return m.group(1) # preserve previously generated stable identity
|
||||
seed='|'.join((tags.get('garmin:mapset',''),tags.get('garmin:type',''),
|
||||
elem.get('lat',''),elem.get('lon','')))
|
||||
return base64.b32encode(hashlib.blake2s(seed.encode('utf-8'),digest_size=7).digest()).decode().rstrip('=')[:11]
|
||||
|
||||
def add_native_unknown_fallback(elem,tags,rule):
|
||||
"""Only unclassified POIs. Named POIs retain their genuine source names."""
|
||||
if any(k in tags for k in SEMANTIC_KEYS):return False
|
||||
if tags.get('garmin:type') == '0x6600' or 'ele' in tags:
|
||||
return False # spot heights and elevation annotations are not unknown places
|
||||
if rule and rule.get('action','').strip().lower()=='drop':return False
|
||||
tags['place']='locality'
|
||||
tags['bgm:synthetic_fallback']='place_locality'
|
||||
tags['bgm:classification']='unverified'
|
||||
name=tags.get('name','').strip()
|
||||
if not name or tags.get('bgm:generated_name')=='yes' or name.startswith('Garmin обект '):
|
||||
tags['name']='Неуточнено - '+stable_unknown_hash(elem,tags)
|
||||
tags['bgm:generated_name']='yes'
|
||||
return True
|
||||
|
||||
def make_cpp_rules(pois,out,labels,conditional=()):
|
||||
"""C++ v0.9 uses these internal stage files; they are NOT user control files."""
|
||||
with open(out,'w',encoding='utf-8',newline='') as f,open(labels,'w',encoding='utf-8',newline='') as g:
|
||||
w=csv.writer(f);w.writerow(['code','name_contains','mode','tags','note'])
|
||||
l=csv.writer(g);l.writerow(['code','label_bg'])
|
||||
for c,row in sorted(pois.items(),key=lambda t:int(t[0],0)):
|
||||
action=row.get('action','replace').strip().lower()
|
||||
if action not in ('replace','append','raw','drop'):raise ValueError('Invalid action for '+c)
|
||||
vals=tags_from_string(row.get('osm_tags',''))
|
||||
if action=='raw':action='replace';vals={}
|
||||
if action=='drop':vals={}
|
||||
w.writerow([c,'',action,';'.join(k+'='+v for k,v in vals.items()),'v0.92 compiled from poi-mapping.csv'])
|
||||
if row.get('label_bg'):l.writerow([c,row['label_bg']])
|
||||
# Order matters: base rule first, then the explicit name-specific rules
|
||||
# in CSV order. C++ evaluates these against ORIGINAL Garmin names.
|
||||
for row in conditional:
|
||||
c=row['code'];action=(row.get('action') or 'replace').strip().lower()
|
||||
vals=tags_from_string(row.get('osm_tags',''))
|
||||
if action=='raw':action='keep_raw';vals={}
|
||||
w.writerow([c,row['name_contains'],action,
|
||||
';'.join(k+'='+v for k,v in vals.items()),
|
||||
'v0.95 name-specific rule: '+row.get('note','')])
|
||||
|
||||
def inspect_counts(osm,pois,lines,out,strict=False):
|
||||
seen=collections.Counter();unresolved=collections.Counter();annotations=collections.Counter();fallback=collections.Counter();bad=collections.Counter();routes=collections.Counter();examples={};category=collections.Counter();n=0
|
||||
with open(osm,'rb') as src:
|
||||
for el in top_level_elements(src):
|
||||
if el.tag=='node':
|
||||
t=tagdict(el)
|
||||
if t.get('garmin:feature')=='poi':
|
||||
n+=1;c=t.get('garmin:type','');seen[c]+=1
|
||||
if c not in pois:bad[c]+=1
|
||||
if t.get('bgm:synthetic_fallback')=='place_locality':fallback[c]+=1
|
||||
if c=='0x6600' and 'ele' in t:
|
||||
annotations['spot_height']+=1
|
||||
elif not any(k in t for k in ('amenity','tourism','emergency','natural','man_made','place','historic','shop','leisure','building','waterway','railway','power','aeroway','highway','ele','hazard','ford','water','water_source','landuse','barrier','aerialway','craft','geological')):
|
||||
unresolved[c]+=1
|
||||
if len(examples.get(c,[]))<5:examples.setdefault(c,[]).append([el.get('lat'),el.get('lon'),t.get('name','')])
|
||||
for k in ('amenity','tourism','emergency','natural','man_made','building','historic','shop','leisure'):
|
||||
if k in t:category[f'{k}={t[k]}']+=1
|
||||
elif el.tag=='relation':
|
||||
t=tagdict(el)
|
||||
if t.get('route')=='hiking':routes[t.get('garmin:type','unknown')]+=1
|
||||
el.clear()
|
||||
summary={'status':'FAIL' if strict and (bad or unresolved) else ('WARN' if bad or unresolved else 'PASS'),
|
||||
'total_pois':n,'mapped_types':len(pois),'observed_types':len(seen),
|
||||
'source_codes_not_in_config':dict(bad),'unclassified_counts':dict(unresolved),
|
||||
'unclassified_total':sum(unresolved.values()),'synthetic_locality_fallback_total':sum(fallback.values()),'synthetic_locality_counts':dict(fallback),'annotations_not_missing_pois':dict(annotations),'route_relation_counts':dict(routes),
|
||||
'native_semantic_categories':dict(category.most_common(100)),
|
||||
'sample_coordinates':examples,
|
||||
'warning':'OSM audit only; stock icons/search not guaranteed. Synthetic localities are private-map fallback, not valid public OSM classification. Elevation-only objects deliberately have no artificial POI icon.'}
|
||||
pathlib.Path(out).write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
return summary
|
||||
|
||||
def transform(source,dest,pois,lines,report,strict=False):
|
||||
"""Streaming write; route metadata held in SQLite so nationwide builds do not need RAM for all nodes/ways.
|
||||
Native route relations are built only from selected Garmin colored overlay ways,
|
||||
NOT from invented highway=path edges.
|
||||
"""
|
||||
dbpath=pathlib.Path(dest).with_suffix('.edges.sqlite')
|
||||
if dbpath.exists():raise FileExistsError(dbpath)
|
||||
db=sqlite3.connect(dbpath)
|
||||
db.execute('PRAGMA synchronous=OFF');db.execute('PRAGMA journal_mode=MEMORY')
|
||||
db.execute('CREATE TABLE edges (code TEXT, way INTEGER, a INTEGER, b INTEGER)')
|
||||
poi_count=0; ways=0; candidate_edges=0; emitted=0; synthetic=0; kinds=collections.Counter();seen=collections.Counter();gaps=collections.Counter()
|
||||
with open(source,'rb') as inp,open(dest,'w',encoding='utf-8') as out:
|
||||
out.write('<?xml version="1.0" encoding="UTF-8"?>\n<osm version="0.6" generator="garmin2osm-v0.95" upload="never">\n')
|
||||
for el in top_level_elements(inp):
|
||||
if el.tag=='node':
|
||||
tags=tagdict(el)
|
||||
if tags.get('garmin:feature')=='poi':
|
||||
poi_count+=1;c=tags.get('garmin:type','')
|
||||
if c in pois:
|
||||
row=pois[c];tags['bgm:legend_bg']=row.get('legend_bg','') or row.get('label_bg','')
|
||||
tags['bgm:priority']=row.get('priority','normal')
|
||||
# only provenance; no false POI semantic category inserted here
|
||||
tags.pop('bgm_poi',None);tags.pop('bgm:synthetic_icon',None)
|
||||
# Unclassified Garmin POIs intentionally remain raw: stock OsmAnd
|
||||
# cannot visually draw a unique icon for each without installed resources.
|
||||
else:gaps[c]+=1;tags.pop('bgm_poi',None)
|
||||
if add_native_unknown_fallback(el,tags,pois.get(c)):
|
||||
synthetic+=1
|
||||
set_tags(el,tags);seen[c]+=1
|
||||
out.write(ET.tostring(el,encoding='unicode')+'\n');emitted+=1
|
||||
elif el.tag=='way':
|
||||
ways+=1;tags=tagdict(el)
|
||||
if tags.get('garmin:feature')=='extended_polyline' or 'bgm_route' in tags:
|
||||
c=tags.get('garmin:type','')
|
||||
r=lines.get(c)
|
||||
tags.pop('bgm_route',None)
|
||||
# Raw geometry and colour may still be needed for diagnosis;
|
||||
# no duplicate road geometry or misleading water/terrain categories.
|
||||
if r:
|
||||
tags['bgm:line_legend']=r.get('legend_bg','')
|
||||
tags['bgm:translation_status']=r.get('route_confidence','unknown')
|
||||
tags.update(tags_from_string(r.get('osm_way_tags','')))
|
||||
if r.get('route_mode','').lower()=='relation':
|
||||
nref=[int(x.attrib['ref']) for x in el.findall('nd')]
|
||||
if len(nref)>=2:
|
||||
db.execute('INSERT INTO edges VALUES (?,?,?,?)',(c,int(el.get('id')),nref[0],nref[-1]));candidate_edges+=1
|
||||
else:gaps[c]+=1
|
||||
set_tags(el,tags)
|
||||
out.write(ET.tostring(el,encoding='unicode')+'\n');emitted+=1
|
||||
elif el.tag=='relation':
|
||||
out.write(ET.tostring(el,encoding='unicode')+'\n');emitted+=1
|
||||
el.clear()
|
||||
db.commit()
|
||||
# Use topological unions on endpoint node refs to avoid one nationwide route per color.
|
||||
grouped=collections.defaultdict(list)
|
||||
for c,w,a,b in db.execute('SELECT code,way,a,b FROM edges ORDER BY code,way'):
|
||||
grouped[c].append((w,a,b))
|
||||
relations=0;components=0;largest=0
|
||||
for c,edges in sorted(grouped.items()):
|
||||
parent=list(range(len(edges)));rank=[0]*len(edges)
|
||||
def root(i):
|
||||
while parent[i]!=i:
|
||||
parent[i]=parent[parent[i]];i=parent[i]
|
||||
return i
|
||||
def union(a,b):
|
||||
ra,rb=root(a),root(b)
|
||||
if ra==rb:return
|
||||
if rank[ra]<rank[rb]:ra,rb=rb,ra
|
||||
parent[rb]=ra
|
||||
if rank[ra]==rank[rb]:rank[ra]+=1
|
||||
endpoint={}
|
||||
for i,(w,a,b) in enumerate(edges):
|
||||
for n in (a,b):
|
||||
if n in endpoint:union(i,endpoint[n])
|
||||
else:endpoint[n]=i
|
||||
comps=collections.defaultdict(list)
|
||||
for i,(w,a,b) in enumerate(edges):comps[root(i)].append(w)
|
||||
for members in comps.values():
|
||||
components+=1;largest=max(largest,len(members));relations+=1
|
||||
# Deterministic, negative IDs because prior C++ IDs are positive
|
||||
rel=ET.Element('relation',id=str(-relations),version='1')
|
||||
for wid in sorted(members):ET.SubElement(rel,'member',type='way',ref=str(wid),role='')
|
||||
row=lines[c]
|
||||
tags={'type':'route','route':'hiking','network':row.get('network') or 'lwn',
|
||||
'garmin:type':c,'bgm:source_feature':'extended_polyline',
|
||||
'bgm:confidence':row.get('route_confidence','color_only')}
|
||||
if row.get('osmc_symbol'):tags['osmc:symbol']=row['osmc_symbol']
|
||||
if row.get('legacy_color'):tags['colour']=row['legacy_color']
|
||||
# Use a non-confusing internal identifier, NOT a fabricated official route name.
|
||||
tags['ref']='BG'+c.replace('0x','').upper()
|
||||
set_tags(rel,tags)
|
||||
out.write(ET.tostring(rel,encoding='unicode')+'\n')
|
||||
out.write('</osm>\n')
|
||||
db.close();dbpath.unlink(missing_ok=True)
|
||||
result={'pois':poi_count,'synthetic_locality_fallbacks':synthetic,'ways':ways,'route_candidate_ways':candidate_edges,
|
||||
'route_relations':relations,'route_components':components,'largest_component_ways':largest,
|
||||
'source_types_missing_mapping':dict(gaps),'notes':['Synthetic place=locality on unknown features is PRIVATE-map-only' ,'Native OSM route relations; no custom phone-side stylesheet','Display zoom remains controlled by stock OsmAnd, not config zoom_hint','Route symbol derived from Garmin color; color_only mappings require review']}
|
||||
pathlib.Path(report).write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
return result
|
||||
|
||||
def template(catalog,poi_file,line_file,osm=None):
|
||||
"""Future map: source-driven, abstains from guessing semantic identities."""
|
||||
if pathlib.Path(poi_file).exists() or pathlib.Path(line_file).exists():raise FileExistsError('Refusing to overwrite mapping templates')
|
||||
pr=[]
|
||||
for row in read_csv(catalog):
|
||||
pr.append({'code':row['code'],'source_count':row['count'],'legend_bg':'','legend_en':'',
|
||||
'meaning_confidence':'unknown','osm_tags':'','label_bg':f'Garmin обект {row["code"]}',
|
||||
'priority':'normal','zoom_hint':'17','action':'raw',
|
||||
'note':f'SAMPLE {row.get("example1_lat","")}, {row.get("example1_lon","")}, {row.get("example1_name","")}'})
|
||||
lc=collections.Counter()
|
||||
if osm:
|
||||
with open(osm,'rb') as inp:
|
||||
for el in top_level_elements(inp):
|
||||
if el.tag=='way':
|
||||
t=tagdict(el)
|
||||
if t.get('garmin:feature')=='extended_polyline':lc[t.get('garmin:type','')]+=1
|
||||
el.clear()
|
||||
lr=[{'code':k,'source_count':v,'legend_bg':'','legacy_color':'', 'osm_way_tags':'',
|
||||
'route_mode':'preserve','osmc_symbol':'','network':'lwn','route_confidence':'unknown',
|
||||
'priority':'normal','note':'Unmapped type: check Garmin legend and geometry before enabling'} for k,v in sorted(lc.items())]
|
||||
with open(poi_file,'w',encoding='utf-8',newline='') as f:
|
||||
w=csv.DictWriter(f,fieldnames=list(pr[0]) if pr else ['code','source_count','legend_bg','legend_en','meaning_confidence','osm_tags','label_bg','priority','zoom_hint','action','note']);w.writeheader();w.writerows(pr)
|
||||
with open(line_file,'w',encoding='utf-8',newline='') as f:
|
||||
w=csv.DictWriter(f,fieldnames=list(lr[0]) if lr else ['code','source_count','legend_bg','legacy_color','osm_way_tags','route_mode','osmc_symbol','network','route_confidence','priority','note']);w.writeheader();w.writerows(lr)
|
||||
return {'poi_types':len(pr),'line_types':len(lr)}
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__);sub=p.add_subparsers(dest='mode',required=True)
|
||||
for cmd in ['prepare','transform','audit','template']:
|
||||
s=sub.add_parser(cmd);s.add_argument('--poi',required=True);s.add_argument('--lines',required=True)
|
||||
if cmd=='prepare':s.add_argument('--out',required=True)
|
||||
elif cmd=='transform':s.add_argument('--osm',required=True);s.add_argument('--out',required=True);s.add_argument('--report',required=True)
|
||||
elif cmd=='audit':s.add_argument('--osm',required=True);s.add_argument('--out',required=True);s.add_argument('--strict',action='store_true')
|
||||
else:s.add_argument('--catalog',required=True);s.add_argument('--osm')
|
||||
a=p.parse_args()
|
||||
if a.mode=='template':res=template(a.catalog,a.poi,a.lines,a.osm)
|
||||
else:
|
||||
ps,conditional=read_poi_configuration(a.poi);ls=line_mapping(a.lines)
|
||||
if a.mode=='prepare':
|
||||
out=pathlib.Path(a.out);out.mkdir(parents=True,exist_ok=True)
|
||||
make_cpp_rules(ps,out/'cpp-poi-override.csv',out/'cpp-name-labels.csv',conditional);res={'poi_types':len(ps),'name_specific_rules':len(conditional),'line_types':len(ls)}
|
||||
elif a.mode=='transform':res=transform(a.osm,a.out,ps,ls,a.report)
|
||||
else:res=inspect_counts(a.osm,ps,ls,a.out,a.strict)
|
||||
print(json.dumps(res,ensure_ascii=False,indent=2));return 0
|
||||
if __name__=='__main__':sys.exit(main())
|
||||
+11
-6
@@ -23,9 +23,13 @@ Options parse(int argc,char**argv){Options o;for(int i=1;i<argc;i++){std::string
|
||||
if(o.input.empty())fail("--input required");if(o.output.empty()&&!o.inspect&&o.poiTypesOut.empty()&&o.poiManifest.empty()&&o.iconsDir.empty())fail("--output, --inspect or an inventory output required");if(!o.poiAdditions.empty()&&o.output.empty())fail("--poi-additions requires --output");if(o.jobs<1||o.jobs>64)fail("--jobs must be 1..64");if(o.examples<1||o.examples>20)fail("--poi-examples must be 1..20");if(o.clip&&(!o.bbox.valid()||o.bbox.w< -180||o.bbox.e>180||o.bbox.s< -90||o.bbox.n>90))fail("Invalid bbox");return o;}
|
||||
|
||||
Tags lineTags(int t,bool ext,const std::string&name){Tags v;tag(v,"garmin:type",code(t));tag(v,"garmin:feature",ext?"extended_polyline":"polyline");tag(v,"name",name);
|
||||
if(ext){ // Physical trail identity not proven: preserve Garmin overlay, mark it as a hiking path geometry.
|
||||
tag(v,"highway","path");tag(v,"foot","yes");tag(v,"trailblazed","yes");tag(v,"garmin:route_overlay","yes");
|
||||
const auto it=gStyles.lines.find(t);if(it!=gStyles.lines.end()&&it->second.color)tag(v,"colour",hexColor(it->second.color));return v;
|
||||
if(ext){ // TYP route overlay is graphical, NOT independently verified routable geometry.
|
||||
tag(v,"bgm_route","type_"+code(t));tag(v,"garmin:route_overlay","yes");
|
||||
const auto it=gStyles.lines.find(t);if(it!=gStyles.lines.end()){
|
||||
if(it->second.color)tag(v,"colour",hexColor(it->second.color));
|
||||
if(it->second.hasPattern())tag(v,"bgm:line_pattern","garmin_bitmap");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
if(t<=0x0c){const char*klass=nullptr;
|
||||
switch(t){case 0:klass="road";break;case 1:klass="motorway";break;case 2:klass="primary";break;case 3:klass="secondary";break;case 4:klass="tertiary";break;case 5:klass="unclassified";break;case 6:klass="residential";break;case 7:klass="service";break;case 8:case 9:case 11:klass="motorway_link";break;case 10:klass="track";break;case 12:klass="residential";break;}
|
||||
@@ -44,7 +48,7 @@ Tags areaTags(int t,const std::string&name){Tags v;tag(v,"garmin:type",code(t));
|
||||
case 0x05:tag(v,"amenity","parking");break;case 0x07:tag(v,"aeroway","aerodrome");break;
|
||||
case 0x0b:tag(v,"amenity","hospital");break;case 0x0c:tag(v,"landuse","industrial");break;
|
||||
case 0x13:tag(v,"building","yes");break;case 0x17:tag(v,"leisure","park");break;
|
||||
case 0x18:tag(v,"leisure","golf_course");break;case 0x09:tag(v,"leisure","marina");break;
|
||||
case 0x18:tag(v,"leisure","sports_centre");break;case 0x09:tag(v,"landuse","retail");break;
|
||||
case 0x14:case 0x15:case 0x16:tag(v,"boundary","national_park");break;
|
||||
case 0x28:case 0x32:tag(v,"natural","water");tag(v,"water","sea");break;
|
||||
case 0x3c:case 0x3d:case 0x40:case 0x41:tag(v,"natural","water");break;
|
||||
@@ -69,13 +73,14 @@ Tags poiTags(int t,const std::string&name){Tags v;tag(v,"garmin:type",code(t));t
|
||||
else if(t==0x6407){tag(v,"tourism","picnic_site");}
|
||||
else if(t==0x640d){tag(v,"aerialway","station");}
|
||||
else if(t==0x6404||t==0x640b||t==0x641a||t==0x2c0b){tag(v,"amenity","place_of_worship");}
|
||||
else if(t==0x6602||t==0x6608){tag(v,"tourism","viewpoint");}
|
||||
else if(t==0x6602){tag(v,"tourism","viewpoint");}
|
||||
else if(t==0x6608){tag(v,"tourism","hotel");}
|
||||
else if(t==0x2800){tag(v,"place","locality");}
|
||||
else if(t==0x6600){ // Geographic spot heights; don't misclassify elevations as villages.
|
||||
try{size_t parsed=0;int n=std::stoi(name,&parsed);if(parsed==name.size()&&n>=0&&n<=8848)tag(v,"ele",std::to_string(n));}catch(...){}
|
||||
}
|
||||
else if(t==0x6614){tag(v,"garmin:marker","distance_or_index");}
|
||||
else if(t==0x6500){tag(v,"man_made","water_works");}
|
||||
else if(t==0x6500){tag(v,"man_made","water_intake");}
|
||||
else if(t==0x6402&&name.find("Посетителски")!=std::string::npos){tag(v,"tourism","information");tag(v,"information","office");}
|
||||
else if(t==0x6411){tag(v,"man_made","tower");}
|
||||
// 0x6414 handled above as drinking_water: conflicting old water_well branch removed.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import unittest, tempfile, csv, pathlib, sys
|
||||
sys.path.insert(0,str(pathlib.Path(__file__).resolve().parents[1]/"tools"))
|
||||
import audit_osmand_poi_mapping as a
|
||||
class TestPoiRegistry(unittest.TestCase):
|
||||
def test_registry_and_semantics(self):
|
||||
with tempfile.TemporaryDirectory() as t:
|
||||
p=pathlib.Path(t)/'types.xml'
|
||||
p.write_text('''<poi_types><poi_type name="water" tag="amenity" value="drinking_water"/><poi_type name="locality" tag="place" value="locality" no_indx="true"/><poi_additional name="dwater" tag="drinking_water" value="yes"/></poi_types>''')
|
||||
reg,ex=a.registry(p)
|
||||
base={'code':'0x6414','source_count':'32922','priority':'critical','osm_tags':'amenity=drinking_water;drinking_water=yes'}
|
||||
self.assertEqual(a.interpret(base,reg,ex)['status'],'native_indexable_type')
|
||||
self.assertEqual(a.interpret({**base,'osm_tags':'place=locality'},reg,ex)['status'],'only_nonindexed_type')
|
||||
self.assertEqual(a.interpret({**base,'osm_tags':'drinking_water=yes'},reg,ex)['status'],'additional_without_primary_type')
|
||||
self.assertEqual(a.interpret({**base,'osm_tags':'man_made=water_intake'},reg,ex)['status'],'no_matching_native_type')
|
||||
self.assertEqual(a.interpret({**base,'code':'0x6600','priority':'terrain','osm_tags':''},reg,ex)['severity'],'terrain_exemption')
|
||||
if __name__=='__main__':unittest.main()
|
||||
@@ -14,13 +14,13 @@ class TestLocalDisplay(unittest.TestCase):
|
||||
c=x['code'];self.assertEqual(r[c]['mode'],'replace',c)
|
||||
self.assertTrue(names[c],c)
|
||||
t=r[c]['tags']
|
||||
self.assertTrue(any(a+'=' in t for a in ['amenity','shop','tourism','natural','man_made','leisure','historic','place','building','emergency','power','highway','waterway','sport','industrial']),c)
|
||||
self.assertTrue(t,c) # BGMountains custom POI index handles non-standard types
|
||||
self.assertNotIn('tags,,',t)
|
||||
def test_key_places(self):
|
||||
r={x['code']:x for x in load('config/poi-overrides-all-types.csv')}
|
||||
self.assertIn('man_made=water_intake',r['0x6500']['tags'])
|
||||
self.assertIn('bgm:synthetic_icon=yes',r['0x6500']['tags'])
|
||||
self.assertIn('tourism=information',r['0x6500']['tags'])
|
||||
self.assertNotIn('bgm:synthetic_icon=yes',r['0x6500']['tags'])
|
||||
self.assertNotIn('tourism=information',r['0x6500']['tags'])
|
||||
self.assertIn('tourism=viewpoint',r['0x6602']['tags'])
|
||||
self.assertNotIn('bgm:synthetic_icon',r['0x6602']['tags'])
|
||||
def test_all_legacy_codes_retained_for_review(self):
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
"""v0.92 offline tests: mapping discipline, native routes, names, future-map templates."""
|
||||
import csv,importlib.util,json,pathlib,subprocess,sys,tempfile,unittest,xml.etree.ElementTree as ET
|
||||
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
||||
spec=importlib.util.spec_from_file_location('translator',ROOT/'scripts/native_translate.py')
|
||||
t=importlib.util.module_from_spec(spec);spec.loader.exec_module(t)
|
||||
POI=ROOT/'config/BGMountains-native-poi-mapping-v094.csv';LINES=ROOT/'config/line-mapping.csv'
|
||||
class Native092(unittest.TestCase):
|
||||
def test_native_source_semantics(self):
|
||||
rows=t.mapping(POI)
|
||||
self.assertGreaterEqual(len(rows),263)
|
||||
for c,tags in [('0x6414','amenity=drinking_water'),('0x6511','natural=spring'),('0x6500','man_made=water_intake'),('0x660a','amenity=shelter'),('0x6609','tourism=alpine_hut'),('0x6401','man_made=bridge')]:
|
||||
self.assertIn(tags,rows[c]['osm_tags'])
|
||||
self.assertEqual(rows['0x6402']['label_bg'],'Заслон - Сграда')
|
||||
self.assertIn(rows['0x6600']['action'],('append','raw'))
|
||||
self.assertFalse(any('leisure=marina' in r['osm_tags'] for r in rows.values()))
|
||||
self.assertFalse(any('tourism=information;information=board' in r['osm_tags'] for r in rows.values()))
|
||||
self.assertTrue(all('garmin:type' not in r['osm_tags'] for r in rows.values()))
|
||||
self.assertTrue(all(int(rows[k]['zoom_hint'])<=12 for k in ('0x6414','0x6511','0x6500','0x660a')))
|
||||
def test_config_only_two_and_types(self):
|
||||
self.assertEqual(set(p.name for p in (ROOT/'config').glob('*.csv')),{'BGMountains-native-poi-mapping-v094.csv','line-mapping.csv'})
|
||||
self.assertTrue(any(x.get('route_mode')=='relation' for x in t.line_mapping(LINES).values()))
|
||||
for line in t.line_mapping(LINES).values():
|
||||
if line['route_mode']=='relation':self.assertTrue(line['osmc_symbol'])
|
||||
def test_native_relation_members_and_poi_provenance(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
d=pathlib.Path(directory);src=d/'sample.osm';dst=d/'native.osm';report=d/'report.json'
|
||||
line=next(r for r in t.line_mapping(LINES).values() if r['route_mode']=='relation')
|
||||
typ=line['code']
|
||||
xml=['<?xml version="1.0" encoding="UTF-8"?><osm version="0.6">']
|
||||
for i in range(1,5):xml.append(f'<node id="{i}" lat="41.{i}" lon="23.{i}"/>')
|
||||
xml+=['<node id="100" lat="41.7" lon="23.4"><tag k="garmin:type" v="0x6414"/><tag k="garmin:feature" v="poi"/><tag k="name" v="Вода - Чешма - HASH"/><tag k="amenity" v="drinking_water"/><tag k="bgm_poi" v="type_0x6414"/></node>']
|
||||
for i,refs in [(10,[1,2]),(11,[2,3])]:
|
||||
xml.append(f'<way id="{i}">'+''.join(f'<nd ref="{n}"/>' for n in refs)+f'<tag k="garmin:feature" v="extended_polyline"/><tag k="garmin:type" v="{typ}"/><tag k="bgm_route" v="type_{typ}"/></way>')
|
||||
xml.append('</osm>');src.write_text('\n'.join(xml))
|
||||
result=t.transform(src,dst,t.mapping(POI),t.line_mapping(LINES),report)
|
||||
self.assertEqual(result['route_candidate_ways'],2);self.assertEqual(result['route_relations'],1)
|
||||
root=ET.parse(dst).getroot();rel=root.find('relation');tags=t.tagdict(rel)
|
||||
self.assertEqual(tags['route'],'hiking');self.assertTrue(tags['osmc:symbol'].endswith('_bar'))
|
||||
self.assertEqual(sorted(m.attrib['ref'] for m in rel.findall('member')),['10','11'])
|
||||
node=next(n for n in root.findall('node') if n.get('id')=='100');p=t.tagdict(node)
|
||||
self.assertEqual(p['amenity'],'drinking_water');self.assertEqual(p['name'],'Вода - Чешма - HASH')
|
||||
self.assertEqual(p['garmin:type'],'0x6414');self.assertNotIn('bgm_poi',p)
|
||||
for way in root.findall('way'):self.assertNotIn('highway',t.tagdict(way))
|
||||
def test_route_attachment_to_real_way_only(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
d=pathlib.Path(directory);src=d/'src.osm';dst=d/'out.osm';rep=d/'report.json'
|
||||
typ=next(row['code'] for row in t.line_mapping(LINES).values() if row['route_mode']=='relation')
|
||||
parts=['<osm version="0.6">','<node id="1" lat="41" lon="23"/>','<node id="2" lat="41.1" lon="23.1"/>',
|
||||
'<node id="3" lat="41.2" lon="23.2"/>','<way id="22"><nd ref="1"/><nd ref="2"/><nd ref="3"/><tag k="highway" v="path"/></way>',
|
||||
'<way id="33"><nd ref="1"/><nd ref="2"/><nd ref="3"/><tag k="garmin:type" v="'+typ+'"/><tag k="garmin:feature" v="extended_polyline"/></way>',
|
||||
'<relation id="-1"><member type="way" ref="33" role=""/><tag k="route" v="hiking"/><tag k="bgm:source_feature" v="extended_polyline"/></relation></osm>']
|
||||
src.write_text('\n'.join(parts))
|
||||
subprocess.run([sys.executable,str(ROOT/'scripts/attach_native_routes.py'),'--osm',src,'--out',dst,'--lines',LINES,'--report',rep],check=True,capture_output=True)
|
||||
report=json.loads(rep.read_text());self.assertEqual(report['attached_to_existing_highway'],1)
|
||||
rel=ET.parse(dst).getroot().find('relation');self.assertEqual(rel.find('member').get('ref'),'22')
|
||||
def test_template_for_new_map_without_false_defaults(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
d=pathlib.Path(directory)
|
||||
p=d/'inventory.csv';p.write_text('code,count,example1_lat,example1_lon,example1_name\n0x999,2,42.123,24.321,Unknown place\n')
|
||||
xml=d/'one.osm';xml.write_text('<osm><node id="1" lat="42" lon="24"/><node id="2" lat="43" lon="25"/><way id="3"><nd ref="1"/><nd ref="2"/><tag k="garmin:feature" v="extended_polyline"/><tag k="garmin:type" v="0x11009"/></way></osm>')
|
||||
a=d/'newpoi.csv';b=d/'newlines.csv';stats=t.template(p,a,b,xml)
|
||||
self.assertEqual(stats,{'poi_types':1,'line_types':1})
|
||||
q=list(t.read_csv(a))[0];self.assertEqual(q['action'],'raw');self.assertEqual(q['osm_tags'],'');self.assertIn('42.123',q['note'])
|
||||
r=list(t.read_csv(b))[0];self.assertEqual(r['route_mode'],'preserve');self.assertEqual(r['osmc_symbol'],'')
|
||||
if __name__=='__main__':unittest.main()
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Regression coverage for large XML handling and no false-locality fallbacks."""
|
||||
import csv,importlib.util,os,pathlib,sys,tempfile,unittest,xml.etree.ElementTree as ET
|
||||
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
||||
spec=importlib.util.spec_from_file_location('native_translate', ROOT/'scripts/native_translate.py')
|
||||
t=importlib.util.module_from_spec(spec);spec.loader.exec_module(t)
|
||||
class Reliability093(unittest.TestCase):
|
||||
def test_unknowns_receive_private_locality_and_stable_name(self):
|
||||
from tempfile import TemporaryDirectory
|
||||
with TemporaryDirectory() as d:
|
||||
out=pathlib.Path(d)
|
||||
src=out/'input.osm';dst=out/'output.osm';report=out/'report.json'
|
||||
src.write_text('''<osm version="0.6"><node id="5" lat="42.1345678" lon="24.2345678"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x9999"/><tag k="garmin:mapset" v="1234"/><tag k="bgm:generated_name" v="yes"/><tag k="name" v="Garmin обект 0x9999 - Q3ABCDEFG9"/></node><node id="6" lat="42.2" lon="24.2"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6600"/><tag k="ele" v="176"/><tag k="name" v="176"/></node></osm>''')
|
||||
t.transform(src,dst,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),report)
|
||||
nodes=ET.parse(dst).getroot().findall('node');a=t.tagdict(nodes[0]);b=t.tagdict(nodes[1])
|
||||
self.assertEqual(a['place'],'locality');self.assertEqual(a['name'],'Неуточнено - Q3ABCDEFG9')
|
||||
self.assertEqual(a['garmin:type'],'0x9999');self.assertEqual(a['bgm:classification'],'unverified')
|
||||
self.assertNotIn('place',b);self.assertEqual(b['ele'],'176')
|
||||
data=t.inspect_counts(dst,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),out/'audit.json')
|
||||
self.assertEqual(data['synthetic_locality_fallback_total'],1)
|
||||
|
||||
def test_known_bgm_types_are_not_invented_localities(self):
|
||||
cfg=t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv')
|
||||
self.assertIn('bgm:spot_height=yes',cfg['0x6600']['osm_tags']);self.assertEqual(cfg['0x6600']['action'],'append')
|
||||
self.assertNotIn('place=locality',cfg['0x6600']['osm_tags'])
|
||||
self.assertIn('information=trail_blaze',cfg['0x650b']['osm_tags'])
|
||||
self.assertIn('information=trail_blaze',cfg['0x660b']['osm_tags'])
|
||||
def test_fallback_does_not_overwrite_known_native_types(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
src=pathlib.Path(d)/'before.osm';out=pathlib.Path(d)/'after.osm';rep=pathlib.Path(d)/'rep.json'
|
||||
src.write_text('''<osm><node id="1" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6400"/><tag k="landuse" v="quarry"/><tag k="name" v="Кариера П"/></node><node id="2" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6406"/><tag k="barrier" v="gate"/></node><node id="3" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x640d"/><tag k="aerialway" v="station"/></node></osm>''')
|
||||
t.transform(src,out,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),rep)
|
||||
nodes=ET.parse(out).getroot().findall('node')
|
||||
self.assertTrue(all('place' not in t.tagdict(e) for e in nodes))
|
||||
self.assertEqual(t.tagdict(nodes[0])['name'],'Кариера П')
|
||||
def test_root_memory_does_not_accumulate(self):
|
||||
import tracemalloc
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
path=pathlib.Path(d)/'many.osm'
|
||||
with path.open('w') as f:
|
||||
f.write('<osm version="0.6">\n')
|
||||
for i in range(130000):f.write(f'<node id="{i+1}" lat="42.1" lon="24.2"><tag k="x" v="y"/></node>\n')
|
||||
f.write('</osm>')
|
||||
tracemalloc.start()
|
||||
c=0
|
||||
with open(path,'rb') as f:
|
||||
for e in t.top_level_elements(f):
|
||||
c+=1
|
||||
self.assertEqual(t.tagdict(e)['x'],'y')
|
||||
_,peak=tracemalloc.get_traced_memory();tracemalloc.stop()
|
||||
self.assertEqual(c,130000)
|
||||
self.assertLess(peak,15_000_000, f'Unbounded XML root growth {peak}')
|
||||
def test_audit_spot_height_and_markers(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
path=pathlib.Path(d)/'sample.osm';report=pathlib.Path(d)/'report.json'
|
||||
path.write_text('''<osm version="0.6"><node id="1" lat="43" lon="22"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6600"/><tag k="ele" v="132"/></node><node id="2" lat="43" lon="22"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x650b"/><tag k="tourism" v="information"/><tag k="information" v="route_marker"/></node><node id="3" lat="43" lon="22"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6502"/><tag k="ford" v="yes"/></node></osm>''')
|
||||
stats=t.inspect_counts(path,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),report)
|
||||
self.assertEqual(stats['unclassified_total'],0)
|
||||
self.assertEqual(stats['annotations_not_missing_pois']['spot_height'],1)
|
||||
if __name__=='__main__':unittest.main()
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Version 0.95 regression checks, including its REAL published CLI contract.
|
||||
|
||||
Only mock OsmAndMapCreator is used for the orchestration test, not for proving
|
||||
OBF binary correctness. The real IMG tests were run separately during release.
|
||||
"""
|
||||
import csv,importlib.util,json,pathlib,subprocess,sys,tempfile,unittest,xml.etree.ElementTree as ET
|
||||
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
||||
SPEC=importlib.util.spec_from_file_location('nt',ROOT/'scripts/native_translate.py')
|
||||
t=importlib.util.module_from_spec(SPEC);SPEC.loader.exec_module(t)
|
||||
POI=ROOT/'config/BGMountains-native-poi-mapping-v094.csv'
|
||||
LINES=ROOT/'config/line-mapping.csv'
|
||||
SCRIPT=ROOT/'scripts/build_bgmountains.py'
|
||||
class V095(unittest.TestCase):
|
||||
def test_mapping_handoff_has_all_named_exceptions(self):
|
||||
bases,conditional=t.read_poi_configuration(POI)
|
||||
self.assertEqual(len(bases),1871)
|
||||
self.assertEqual(len(conditional),79)
|
||||
self.assertEqual(len(t.line_mapping(LINES)),50)
|
||||
self.assertEqual(sum(x['code']=='0x6414' and x['name_contains']=='Сух' for x in conditional),1)
|
||||
with tempfile.TemporaryDirectory() as dd:
|
||||
d=pathlib.Path(dd)
|
||||
t.make_cpp_rules(bases,d/'cpp.csv',d/'labels.csv',conditional)
|
||||
rows=list(csv.DictReader((d/'cpp.csv').open()))
|
||||
self.assertEqual(len(rows),1950)
|
||||
self.assertEqual(next(x for x in rows if x['code']=='0x6414' and x['name_contains']=='')['tags'].split(';')[:2],['amenity=drinking_water','man_made=water_tap'])
|
||||
self.assertEqual(next(x for x in rows if x['code']=='0x6414' and x['name_contains']=='Сух')['tags'].split(';')[:2],['man_made=water_tap','drinking_water=no'])
|
||||
def test_argument_contract_with_users_flags(self):
|
||||
helptext=subprocess.run([sys.executable,SCRIPT,'--help'],capture_output=True,text=True,check=True).stdout
|
||||
for flag in ('--poi-config','--line-config','--download-creator','--jobs','--java-opts','--bbox','--prepare-only','--creator','--template'):
|
||||
self.assertIn(flag,helptext)
|
||||
self.assertNotIn('unrecognized arguments',helptext)
|
||||
def test_smoke_full_orchestration_with_mock_creator(self):
|
||||
with tempfile.TemporaryDirectory() as dd:
|
||||
d=pathlib.Path(dd)
|
||||
creator=d/'creator';creator.mkdir()
|
||||
(creator/'utilities.sh').write_text('#!/bin/sh\nset -eu\n[ "$1" = "generate-obf-no-address" ]\n[ -f "$2" ]\n[ "${JAVA_TOOL_OPTIONS:-}" = "-Xms512M -Xmx25G" ]\nprintf MOCK-OBF > "${2%.osm}.obf"\n')
|
||||
(creator/'utilities.sh').chmod(0o755)
|
||||
(creator/'inspector.sh').write_text('#!/bin/sh\n[ "$1" = "-vpoi" ]\nprintf MOCK-INSPECTION\n')
|
||||
(creator/'inspector.sh').chmod(0o755)
|
||||
raw=d/'minimal.osm';raw.write_text('''<osm version="0.6"><node id="1" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6414"/><tag k="amenity" v="drinking_water"/><tag k="man_made" v="water_tap"/><tag k="name" v="Чешма"/></node></osm>''',encoding='utf-8')
|
||||
cmd=[sys.executable,str(SCRIPT),'--osm',str(raw),'--poi-config',str(POI),'--line-config',str(LINES),'--out',str(d/'out'),'--jobs','8','--java-opts','-Xms512M -Xmx25G','--creator',str(creator)]
|
||||
run=subprocess.run(cmd,cwd=ROOT,capture_output=True,text=True)
|
||||
self.assertEqual(run.returncode,0,run.stderr+'\n'+run.stdout)
|
||||
self.assertTrue((d/'out'/'BGMountains-native.obf').is_file())
|
||||
self.assertEqual(json.loads((d/'out'/'build-manifest.json').read_text())['version'],'v0.95')
|
||||
self.assertEqual(json.loads((d/'out'/'semantic-audit.json').read_text())['status'],'PASS')
|
||||
tags=t.tagdict(ET.parse(d/'out'/'BGMountains-native.osm').getroot().find('node'))
|
||||
self.assertEqual(tags['amenity'],'drinking_water')
|
||||
self.assertEqual(tags['man_made'],'water_tap')
|
||||
def test_future_template_does_not_require_existing_configs(self):
|
||||
with tempfile.TemporaryDirectory() as dd:
|
||||
d=pathlib.Path(dd)
|
||||
# No fake success from a malformed configuration: expected exit nonzero.
|
||||
p=subprocess.run([sys.executable,SCRIPT,'--osm',str(d/'notthere.osm'),'--poi-config',str(d/'missing.csv'),'--line-config',str(LINES),'--prepare-only'],capture_output=True,text=True)
|
||||
self.assertNotEqual(p.returncode,0)
|
||||
self.assertIn('mapping file not found',p.stderr)
|
||||
if __name__=='__main__':unittest.main()
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit whether IMG-derived POIs have both OSM display tags and names.
|
||||
"""Audit IMG-derived POIs for custom indexability and truthfully report native semantic tags.
|
||||
|
||||
This measures converted OSM, NOT actual OsmAnd screen visibility nor .obf indexing.
|
||||
Output inventory always includes Garmin codes and example coordinates.
|
||||
@@ -23,18 +23,20 @@ def main():
|
||||
if tags.get('garmin:feature')!='poi':el.clear();continue
|
||||
code=tags.get('garmin:type','').lower();counts[code]+=1;overview['pois']+=1
|
||||
name=tags.get('name','');kind=[f'{k}={tags[k]}' for k in CLASS_KEYS if k in tags]
|
||||
if not kind:bad.append((code,name,el.get('lat'),el.get('lon'),'no_osm_semantic_tag'));overview['unclassified']+=1
|
||||
if not kind:overview['needs_custom_style']+=1
|
||||
else:overview['has_native_semantics']+=1
|
||||
if not name:overview['nameless']+=1
|
||||
if tags.get('bgm:generated_name')=='yes':stats[code]['generated_name']+=1;overview['generated_name']+=1
|
||||
if tags.get('bgm:synthetic_icon')=='yes':stats[code]['synthetic_icon']+=1;overview['synthetic_icon']+=1
|
||||
if not tags.get('bgm_poi'):overview['missing_raw_type_index_tag']+=1
|
||||
else:overview['has_bgm_poi_tag']+=1
|
||||
if len(samples[code])<args.samples:samples[code].append((el.get('lat'),el.get('lon'),name, '|'.join(kind)))
|
||||
el.clear()
|
||||
rows=[]
|
||||
for code in sorted(counts,key=lambda x:(-counts[x],x)):
|
||||
base=catalog.get(code,{})
|
||||
row=dict(code=code,osm_poi_count=counts[code],nationwide_type_count=base.get('count',''),
|
||||
icon_strategy=('synthetic' if stats[code]['synthetic_icon'] else 'native-osm'),
|
||||
icon_strategy=('native+custom' if samples[code][0][3] else 'custom_style_required'),
|
||||
synthetic_count=stats[code]['synthetic_icon'],generated_name_count=stats[code]['generated_name'],
|
||||
legend=base.get('verified_legend_bg',''),review_status=base.get('review_status',''),
|
||||
example_lat=samples[code][0][0],example_lon=samples[code][0][1],
|
||||
@@ -44,11 +46,11 @@ def main():
|
||||
path=Path(args.csv);path.parent.mkdir(parents=True,exist_ok=True)
|
||||
with path.open('w',encoding='utf-8',newline='') as f:
|
||||
if rows:w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows)
|
||||
summary=dict(status='PASS' if not bad and not overview['nameless'] and not overview['missing_raw_type_index_tag'] else 'FAIL',
|
||||
summary=dict(status='PASS' if not overview['nameless'] and not overview['missing_raw_type_index_tag'] else 'FAIL',
|
||||
counts=dict(overview),catalog_types=len(catalog),exported_types=len(counts),
|
||||
note='Audit is for OSM tags only. A real .obf inspector comparison and OsmAnd visual inspection remain required.',
|
||||
note='POIs without standard OSM categories are valid for this PRIVATE map because bgm_poi uses custom index and renderer. They require importing the style; this does not prove OBF persistence or screen visibility.',
|
||||
errors=bad[:100],missing_types_from_catalog=sorted(set(counts)-set(catalog)))
|
||||
Path(args.json).write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print(json.dumps(summary,ensure_ascii=False,indent=2))
|
||||
return 0 if summary['status']=='PASS' and not summary['missing_types_from_catalog'] else 0
|
||||
return 0 if summary['status']=='PASS' and not summary['missing_types_from_catalog'] else 2
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit Garmin -> OSM POI mappings against an exact OsmAnd poi_types.xml.
|
||||
|
||||
No OBF creation. Exits 0 for ordinary data regressions unless --strict is requested.
|
||||
The existence of a poi_type does NOT guarantee map icon visibility at any zoom.
|
||||
"""
|
||||
import argparse,csv,json,collections,sys
|
||||
from pathlib import Path
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
def registry(path):
|
||||
root=ET.parse(path).getroot()
|
||||
main=collections.defaultdict(list);extras=collections.defaultdict(list)
|
||||
for node in root.iter():
|
||||
if node.tag not in ('poi_type','poi_additional'):continue
|
||||
t=node.attrib.get('tag');v=node.attrib.get('value')
|
||||
if not t or v is None:continue
|
||||
d=main if node.tag=='poi_type' else extras
|
||||
d[f'{t}={v}'].append(node.attrib)
|
||||
return main,extras
|
||||
|
||||
def interpret(row,main,extra):
|
||||
tags=[v.strip() for v in row['osm_tags'].split(';') if '=' in v]
|
||||
supported=[];noindex=[];additional=[];missing=[]
|
||||
for tv in tags:
|
||||
candidates=main.get(tv,[])
|
||||
if any(x.get('no_indx')!='true' for x in candidates):supported.append(tv)
|
||||
elif candidates:noindex.append(tv)
|
||||
elif tv in extra:additional.append(tv)
|
||||
else:missing.append(tv)
|
||||
if supported:kind='native_indexable_type'
|
||||
elif noindex:kind='only_nonindexed_type'
|
||||
elif additional:kind='additional_without_primary_type'
|
||||
else:kind='no_matching_native_type'
|
||||
is_terrain=(row.get('priority')=='terrain' or row.get('code')=='0x6600')
|
||||
priority=row.get('priority','')
|
||||
cnt=int(row.get('source_count') or 0)
|
||||
if is_terrain:severity='terrain_exemption'
|
||||
elif kind=='native_indexable_type':severity='ok'
|
||||
elif priority=='critical':severity='critical'
|
||||
elif cnt>=100:severity='high'
|
||||
else:severity='low'
|
||||
return {'code':row.get('code',''),'count':cnt,'priority':priority,'legend_bg':row.get('legend_bg',''),
|
||||
'osm_tags':row.get('osm_tags',''), 'status':kind, 'severity':severity,
|
||||
'indexable_poi_tags':';'.join(supported),'nonindexed_poi_tags':';'.join(noindex),
|
||||
'additional_only_tags':';'.join(additional),'unrecognized_tags':';'.join(missing),
|
||||
'sample_lat':row.get('example_lat',''),'sample_lon':row.get('example_lon',''),
|
||||
'explanation':'Registry compatibility only; does not guarantee rendering zoom or OBF preservation'}
|
||||
|
||||
def main(argv=None):
|
||||
ap=argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument('--mapping',required=True,help='Single canonical POI mapping CSV')
|
||||
ap.add_argument('--poi-types',required=True,help='OsmAnd official poi_types.xml, preferably pinned version')
|
||||
ap.add_argument('--report',default='poi-native-index-audit.csv')
|
||||
ap.add_argument('--json',default='poi-native-index-audit.json')
|
||||
ap.add_argument('--strict',action='store_true',help='Exit nonzero for unindexable critical/high types')
|
||||
args=ap.parse_args(argv)
|
||||
native,extra=registry(args.poi_types)
|
||||
with open(args.mapping,encoding='utf8-sig',newline='') as f:
|
||||
allrows=list(csv.DictReader(f))
|
||||
# Name-conditional rows are not additional Garmin types; counting them as
|
||||
# independent types would multiply source_count and inflate POI statistics.
|
||||
rows=[r for r in allrows if not r.get('name_contains','')]
|
||||
out=[interpret(r,native,extra) for r in rows]
|
||||
with open(args.report,'w',newline='',encoding='utf8') as f:
|
||||
w=csv.DictWriter(f,fieldnames=out[0].keys());w.writeheader();w.writerows(out)
|
||||
stats=collections.Counter(r['status'] for r in out)
|
||||
weighted=collections.Counter()
|
||||
for r in out:weighted[r['status']]+=r['count']
|
||||
risk=[r for r in out if r['severity'] in ('critical','high')]
|
||||
payload={'type_counts':dict(stats),'weighted_source_counts':dict(weighted),'critical_or_high':len(risk),
|
||||
'top_regressions':sorted(risk,key=lambda z:-z['count'])[:30],
|
||||
'warning':'POI type registry check ONLY. Does not guarantee OBF map index presence, symbol visibility, zoom or Nearby settings.'}
|
||||
Path(args.json).write_text(json.dumps(payload,ensure_ascii=False,indent=2),encoding='utf8')
|
||||
print(f"Inspected {len(out)} Garmin types: {dict(stats)}, high/critical issues: {len(risk)}")
|
||||
print(f"Wrote {args.report} and {args.json}")
|
||||
return 2 if args.strict and risk else 0
|
||||
|
||||
if __name__=='__main__':sys.exit(main())
|
||||
Reference in New Issue
Block a user