Issue #74: rename scale to zoom level.

This commit is contained in:
Sergey Vartanov 2021-08-28 19:25:35 +03:00
parent 00f97131f2
commit 9c4873c5ae
9 changed files with 121 additions and 104 deletions

View file

@ -131,7 +131,7 @@ Command `render` is used to generates SVG map from OpenStreetMap data. You can r
roentgen render \ roentgen render \
-b <min longitude>,<min latitude>,<max longitude>,<max latitude> \ -b <min longitude>,<min latitude>,<max longitude>,<max latitude> \
-o <output file name> \ -o <output file name> \
-s <osm zoom level> \ -z <OSM zoom level> \
<other arguments> <other arguments>
``` ```
@ -153,7 +153,7 @@ will download OSM data to `cache/2.284,48.860,2.290,48.865.osm` and write output
| <span style="white-space: nowrap;">`-o`</span>, <span style="white-space: nowrap;">`--output`</span> `<path>` | output SVG file name, default value: `out/map.svg` | | <span style="white-space: nowrap;">`-o`</span>, <span style="white-space: nowrap;">`--output`</span> `<path>` | output SVG file name, default value: `out/map.svg` |
| <span style="white-space: nowrap;">`-b`</span>, <span style="white-space: nowrap;">`--boundary-box`</span> `<lon1>,<lat1>,<lon2>,<lat2>` | geo boundary box; if first value is negative, enclose the value with quotes and use space before `-` | | <span style="white-space: nowrap;">`-b`</span>, <span style="white-space: nowrap;">`--boundary-box`</span> `<lon1>,<lat1>,<lon2>,<lat2>` | geo boundary box; if first value is negative, enclose the value with quotes and use space before `-` |
| <span style="white-space: nowrap;">`--cache`</span> `<path>` | path for temporary OSM files, default value: `cache` | | <span style="white-space: nowrap;">`--cache`</span> `<path>` | path for temporary OSM files, default value: `cache` |
| <span style="white-space: nowrap;">`-s`</span>, <span style="white-space: nowrap;">`--scale`</span> `<integer>` | OSM zoom level, default value: 18 | | <span style="white-space: nowrap;">`-z`</span>, <span style="white-space: nowrap;">`--zoom`</span> `<integer>` | OSM zoom level, default value: 18 |
+ see [map configuration options](#map-options) + see [map configuration options](#map-options)
@ -165,10 +165,10 @@ Command `tile` is used to generate PNG tiles for [slippy maps](https://wiki.open
| Option | Description | | Option | Description |
|---|---| |---|---|
| <span style="white-space: nowrap;">`-c`</span>, <span style="white-space: nowrap;">`--coordinates`</span> `<latitude>,<longitude>` | coordinates of any location inside the tile | | <span style="white-space: nowrap;">`-c`</span>, <span style="white-space: nowrap;">`--coordinates`</span> `<latitude>,<longitude>` | coordinates of any location inside the tile |
| <span style="white-space: nowrap;">`-t`</span>, <span style="white-space: nowrap;">`--tile`</span> `<scale>/<x>/<y>` | tile specification | | <span style="white-space: nowrap;">`-t`</span>, <span style="white-space: nowrap;">`--tile`</span> `<zoom level>/<x>/<y>` | tile specification |
| <span style="white-space: nowrap;">`--cache`</span> `<path>` | path for temporary OSM files, default value: `cache` | | <span style="white-space: nowrap;">`--cache`</span> `<path>` | path for temporary OSM files, default value: `cache` |
| <span style="white-space: nowrap;">`-b`</span>, <span style="white-space: nowrap;">`--boundary-box`</span> `<lon1>,<lat1>,<lon2>,<lat2>` | construct the minimum amount of tiles that cover requested boundary box | | <span style="white-space: nowrap;">`-b`</span>, <span style="white-space: nowrap;">`--boundary-box`</span> `<lon1>,<lat1>,<lon2>,<lat2>` | construct the minimum amount of tiles that cover requested boundary box |
| <span style="white-space: nowrap;">`-s`</span>, <span style="white-space: nowrap;">`--scales`</span> `<integer>` | OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, `16,17,18`, or `16,18-20`, default value: `18` | | <span style="white-space: nowrap;">`-z`</span>, <span style="white-space: nowrap;">`--zoom`</span> `<integer>` | OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, `16,17,18`, or `16,18-20`, default value: `18` |
+ see [map configuration options](#map-options) + see [map configuration options](#map-options)
@ -185,15 +185,15 @@ or specify any geographical coordinates inside a tile:
```bash ```bash
roentgen tile \ roentgen tile \
--coordinates <latitude>,<longitude> \ --coordinates <latitude>,<longitude> \
--scales <OSM zoom levels> --zoom <OSM zoom levels>
``` ```
Tile will be stored as SVG file `out/tiles/tile_<zoom level>_<x>_<y>.svg` and PNG file `out/tiles/tile_<zoom level>_<x>_<y>.svg`, where `x` and `y` are tile coordinates. `--scales` option will be ignored if it is used with `--tile` option. Tile will be stored as SVG file `out/tiles/tile_<zoom level>_<x>_<y>.svg` and PNG file `out/tiles/tile_<zoom level>_<x>_<y>.svg`, where `x` and `y` are tile coordinates. `--zoom` option will be ignored if it is used with `--tile` option.
Example: Example:
```bash ```bash
roentgen tile -c 55.7510637,37.6270761 -s 18 roentgen tile -c 55.7510637,37.6270761 -z 18
``` ```
will generate SVG file `out/tiles/tile_18_158471_81953.svg` and PNG file `out/tiles/tile_18_158471_81953.png`. will generate SVG file `out/tiles/tile_18_158471_81953.svg` and PNG file `out/tiles/tile_18_158471_81953.png`.
@ -205,7 +205,7 @@ Specify boundary box to get the minimal set of tiles that covers the area:
```bash ```bash
roentgen tile \ roentgen tile \
--boundary-box <min longitude>,<min latitude>,<max longitude>,<max latitude> \ --boundary-box <min longitude>,<min latitude>,<max longitude>,<max latitude> \
--scales <OSM zoom levels> --zoom <OSM zoom levels>
``` ```
Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files `out/tiles/tile_<zoom level>_<x>_<y>.svg` and PNG files `out/tiles/tile_<zoom level>_<x>_<y>.svg`, where `x` and `y` are tile coordinates. Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files `out/tiles/tile_<zoom level>_<x>_<y>.svg` and PNG files `out/tiles/tile_<zoom level>_<x>_<y>.svg`, where `x` and `y` are tile coordinates.
@ -216,7 +216,7 @@ Example:
roentgen tile -b 2.361,48.871,2.368,48.875 roentgen tile -b 2.361,48.871,2.368,48.875
``` ```
will generate 36 PNG tiles at scale 18 from tile 18/132791/90164 all the way to 18/132796/90169 and two cached files `cache/2.360,48.869,2.370,48.877_18.svg` and `cache/2.360,48.869,2.370,48.877_18.png`. will generate 36 PNG tiles at zoom level 18 from tile 18/132791/90164 all the way to 18/132796/90169 and two cached files `cache/2.360,48.869,2.370,48.877_18.svg` and `cache/2.360,48.869,2.370,48.877_18.png`.
Tile server Tile server
----------- -----------
@ -234,7 +234,7 @@ Stop server interrupting process with <kbd>Ctrl</kbd> + <kbd>C</kbd>.
Create a minimal amount of tiles that cover specified boundary box for zoom levels 16, 17, 18, and 19: Create a minimal amount of tiles that cover specified boundary box for zoom levels 16, 17, 18, and 19:
```bash ```bash
roentgen tile -b 2.364,48.854,2.367,48.857 -s 16-19 roentgen tile -b 2.364,48.854,2.367,48.857 -z 16-19
``` ```
Run tile server on 127.0.0.1:8080: Run tile server on 127.0.0.1:8080:

View file

@ -175,7 +175,7 @@ Command \m {render} is used to generates SVG map from OpenStreetMap data. You c
\code {roentgen render \\ \code {roentgen render \\
-b \formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\ -b \formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\
-o \formal {output file name} \\ -o \formal {output file name} \\
-s \formal {osm zoom level} \\ -z \formal {OSM zoom level} \\
\formal {other arguments}} {bash} \formal {other arguments}} {bash}
\3 {Example} {example-2} \3 {Example} {example-2}
@ -210,13 +210,13 @@ or specify any geographical coordinates inside a tile\:
\code {roentgen tile \\ \code {roentgen tile \\
--coordinates \formal {latitude},\formal {longitude} \\ --coordinates \formal {latitude},\formal {longitude} \\
--scales \formal {OSM zoom levels}} {bash} --zoom \formal {OSM zoom levels}} {bash}
Tile will be stored as SVG file \m {out/tiles/tile_<zoom level>_<x>_<y>.svg} and PNG file \m {out/tiles/tile_<zoom level>_<x>_<y>.svg}, where \m {x} and \m {y} are tile coordinates. \m {--scales} option will be ignored if it is used with \m {--tile} option. Tile will be stored as SVG file \m {out/tiles/tile_<zoom level>_<x>_<y>.svg} and PNG file \m {out/tiles/tile_<zoom level>_<x>_<y>.svg}, where \m {x} and \m {y} are tile coordinates. \m {--zoom} option will be ignored if it is used with \m {--tile} option.
Example\: Example\:
\code {roentgen tile -c 55.7510637,37.6270761 -s 18} {bash} \code {roentgen tile -c 55.7510637,37.6270761 -z 18} {bash}
will generate SVG file \m {out/tiles/tile_18_158471_81953.svg} and PNG file \m {out/tiles/tile_18_158471_81953.png}. will generate SVG file \m {out/tiles/tile_18_158471_81953.svg} and PNG file \m {out/tiles/tile_18_158471_81953.png}.
@ -226,7 +226,7 @@ Specify boundary box to get the minimal set of tiles that covers the area\:
\code {roentgen tile \\ \code {roentgen tile \\
--boundary-box \formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\ --boundary-box \formal {min longitude},\formal {min latitude},\formal {max longitude},\formal {max latitude} \\
--scales \formal {OSM zoom levels}} {bash} --zoom \formal {OSM zoom levels}} {bash}
Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files \m {out/tiles/tile_<zoom level>_<x>_<y>.svg} and PNG files \m {out/tiles/tile_<zoom level>_<x>_<y>.svg}, where \m {x} and \m {y} are tile coordinates. Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files \m {out/tiles/tile_<zoom level>_<x>_<y>.svg} and PNG files \m {out/tiles/tile_<zoom level>_<x>_<y>.svg}, where \m {x} and \m {y} are tile coordinates.
@ -234,7 +234,7 @@ Example\:
\code {roentgen tile -b 2.361,48.871,2.368,48.875} {bash} \code {roentgen tile -b 2.361,48.871,2.368,48.875} {bash}
will generate 36 PNG tiles at scale 18 from tile 18/132791/90164 all the way to 18/132796/90169 and two cached files \m {cache/2.360,48.869,2.370,48.877_18.svg} and \m {cache/2.360,48.869,2.370,48.877_18.png}. will generate 36 PNG tiles at zoom level 18 from tile 18/132791/90164 all the way to 18/132796/90169 and two cached files \m {cache/2.360,48.869,2.370,48.877_18.svg} and \m {cache/2.360,48.869,2.370,48.877_18.png}.
\2 {Tile server} {tile-server} \2 {Tile server} {tile-server}
@ -248,7 +248,7 @@ Stop server interrupting process with \kbd {Ctrl} + \kbd {C}.
Create a minimal amount of tiles that cover specified boundary box for zoom levels 16, 17, 18, and 19\: Create a minimal amount of tiles that cover specified boundary box for zoom levels 16, 17, 18, and 19\:
\code {roentgen tile -b 2.364,48.854,2.367,48.857 -s 16-19} {bash} \code {roentgen tile -b 2.364,48.854,2.367,48.857 -z 16-19} {bash}
Run tile server on 127.0.0.1\:8080\: Run tile server on 127.0.0.1\:8080\:

View file

@ -46,25 +46,28 @@ class Flinger:
def __init__( def __init__(
self, self,
geo_boundaries: BoundaryBox, geo_boundaries: BoundaryBox,
scale: float = 18, zoom_level: float = 18,
border: np.ndarray = np.array((0, 0)), border: np.ndarray = np.array((0, 0)),
) -> None: ) -> None:
""" """
:param geo_boundaries: minimum and maximum latitude and longitude :param geo_boundaries: minimum and maximum latitude and longitude
:param scale: OSM zoom level :param zoom_level: zoom level in OpenStreetMap terminology
:param border: size of padding in pixels :param border: size of padding in pixels
""" """
self.geo_boundaries: BoundaryBox = geo_boundaries self.geo_boundaries: BoundaryBox = geo_boundaries
self.border: np.ndarray = border self.border: np.ndarray = border
self.ratio: float = ( self.ratio: float = (
osm_zoom_level_to_pixels_per_meter(scale) * EQUATOR_LENGTH / 360 osm_zoom_level_to_pixels_per_meter(zoom_level)
* EQUATOR_LENGTH
/ 360
) )
self.size: np.ndarray = border * 2 + self.ratio * ( self.size: np.ndarray = border * 2 + self.ratio * (
pseudo_mercator(self.geo_boundaries.max_()) pseudo_mercator(self.geo_boundaries.max_())
- pseudo_mercator(self.geo_boundaries.min_()) - pseudo_mercator(self.geo_boundaries.min_())
) )
self.pixels_per_meter: float = osm_zoom_level_to_pixels_per_meter(scale) self.pixels_per_meter: float = osm_zoom_level_to_pixels_per_meter(
zoom_level
)
self.size: np.ndarray = self.size.astype(int).astype(float) self.size: np.ndarray = self.size.astype(int).astype(float)
def fling(self, coordinates: np.ndarray) -> np.ndarray: def fling(self, coordinates: np.ndarray) -> np.ndarray:

View file

@ -273,7 +273,7 @@ def ui(options: argparse.Namespace) -> None:
else: else:
view_box = osm_data.view_box view_box = osm_data.view_box
flinger: Flinger = Flinger(view_box, options.scale) flinger: Flinger = Flinger(view_box, options.zoom)
size: np.ndarray = flinger.size size: np.ndarray = flinger.size
svg: svgwrite.Drawing = svgwrite.Drawing( svg: svgwrite.Drawing = svgwrite.Drawing(

View file

@ -39,17 +39,17 @@ class _Handler(SimpleHTTPRequestHandler):
if not (len(parts) == 5 and not parts[0] and parts[1] == "tiles"): if not (len(parts) == 5 and not parts[0] and parts[1] == "tiles"):
return return
scale: int = int(parts[2]) zoom_level: int = int(parts[2])
x: int = int(parts[3]) x: int = int(parts[3])
y: int = int(parts[4]) y: int = int(parts[4])
tile_path: Path = workspace.get_tile_path() tile_path: Path = workspace.get_tile_path()
png_path: Path = tile_path / f"tile_{scale}_{x}_{y}.png" png_path: Path = tile_path / f"tile_{zoom_level}_{x}_{y}.png"
if self.update_cache: if self.update_cache:
svg_path: Path = png_path.with_suffix(".svg") svg_path: Path = png_path.with_suffix(".svg")
if not png_path.exists(): if not png_path.exists():
if not svg_path.exists(): if not svg_path.exists():
tile = Tile(x, y, scale) tile = Tile(x, y, zoom_level)
tile.draw(tile_path, self.cache, self.options) tile.draw(tile_path, self.cache, self.options)
with svg_path.open() as input_file: with svg_path.open() as input_file:
cairosvg.svg2png( cairosvg.svg2png(

View file

@ -41,21 +41,23 @@ class Tile:
x: int x: int
y: int y: int
scale: int zoom_level: int
@classmethod @classmethod
def from_coordinates(cls, coordinates: np.ndarray, scale: int) -> "Tile": def from_coordinates(
cls, coordinates: np.ndarray, zoom_level: int
) -> "Tile":
""" """
Code from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames Code from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
:param coordinates: any coordinates inside tile, (latitude, longitude) :param coordinates: any coordinates inside tile, (latitude, longitude)
:param scale: OpenStreetMap zoom level :param zoom_level: zoom level in OpenStreetMap terminology
""" """
lat_rad: np.ndarray = np.radians(coordinates[0]) lat_rad: np.ndarray = np.radians(coordinates[0])
n: float = 2.0 ** scale n: float = 2.0 ** zoom_level
x: int = int((coordinates[1] + 180.0) / 360.0 * n) x: int = int((coordinates[1] + 180.0) / 360.0 * n)
y: int = int((1.0 - np.arcsinh(np.tan(lat_rad)) / np.pi) / 2.0 * n) y: int = int((1.0 - np.arcsinh(np.tan(lat_rad)) / np.pi) / 2.0 * n)
return cls(x, y, scale) return cls(x, y, zoom_level)
def get_coordinates(self) -> np.ndarray: def get_coordinates(self) -> np.ndarray:
""" """
@ -63,7 +65,7 @@ class Tile:
Code from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames Code from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
""" """
n: float = 2.0 ** self.scale n: float = 2.0 ** self.zoom_level
lon_deg: float = self.x / n * 360.0 - 180.0 lon_deg: float = self.x / n * 360.0 - 180.0
lat_rad: float = np.arctan(np.sinh(np.pi * (1 - 2 * self.y / n))) lat_rad: float = np.arctan(np.sinh(np.pi * (1 - 2 * self.y / n)))
lat_deg: np.ndarray = np.degrees(lat_rad) lat_deg: np.ndarray = np.degrees(lat_rad)
@ -76,14 +78,14 @@ class Tile:
""" """
return ( return (
self.get_coordinates(), self.get_coordinates(),
Tile(self.x + 1, self.y + 1, self.scale).get_coordinates(), Tile(self.x + 1, self.y + 1, self.zoom_level).get_coordinates(),
) )
def get_extended_boundary_box(self) -> BoundaryBox: def get_extended_boundary_box(self) -> BoundaryBox:
"""Same as get_boundary_box, but with extended boundaries.""" """Same as get_boundary_box, but with extended boundaries."""
point_1: np.ndarray = self.get_coordinates() point_1: np.ndarray = self.get_coordinates()
point_2: np.ndarray = Tile( point_2: np.ndarray = Tile(
self.x + 1, self.y + 1, self.scale self.x + 1, self.y + 1, self.zoom_level
).get_coordinates() ).get_coordinates()
return BoundaryBox( return BoundaryBox(
@ -105,7 +107,7 @@ class Tile:
def get_file_name(self, directory_name: Path) -> Path: def get_file_name(self, directory_name: Path) -> Path:
"""Get tile output SVG file path.""" """Get tile output SVG file path."""
return directory_name / f"tile_{self.scale}_{self.x}_{self.y}.svg" return directory_name / f"tile_{self.zoom_level}_{self.x}_{self.y}.svg"
def exists(self, directory_name: Path) -> bool: def exists(self, directory_name: Path) -> bool:
"""Check whether the tile is drawn.""" """Check whether the tile is drawn."""
@ -114,7 +116,8 @@ class Tile:
def get_carto_address(self) -> str: def get_carto_address(self) -> str:
"""Get URL of this tile from the OpenStreetMap server.""" """Get URL of this tile from the OpenStreetMap server."""
return ( return (
f"https://tile.openstreetmap.org/{self.scale}/{self.x}/{self.y}.png" f"https://tile.openstreetmap.org/"
f"{self.zoom_level}/{self.x}/{self.y}.png"
) )
def draw( def draw(
@ -146,11 +149,11 @@ class Tile:
"""Draw SVG and PNG tile using OpenStreetMap data.""" """Draw SVG and PNG tile using OpenStreetMap data."""
top, left = self.get_coordinates() top, left = self.get_coordinates()
bottom, right = Tile( bottom, right = Tile(
self.x + 1, self.y + 1, self.scale self.x + 1, self.y + 1, self.zoom_level
).get_coordinates() ).get_coordinates()
flinger: Flinger = Flinger( flinger: Flinger = Flinger(
BoundaryBox(left, bottom, right, top), self.scale BoundaryBox(left, bottom, right, top), self.zoom_level
) )
size: np.ndarray = flinger.size size: np.ndarray = flinger.size
@ -192,31 +195,33 @@ class Tiles:
tiles: list[Tile] tiles: list[Tile]
tile_1: Tile # Left top tile. tile_1: Tile # Left top tile.
tile_2: Tile # Right bottom tile. tile_2: Tile # Right bottom tile.
scale: int # OpenStreetMap zoom level. zoom_level: int # OpenStreetMap zoom level.
boundary_box: BoundaryBox boundary_box: BoundaryBox
@classmethod @classmethod
def from_boundary_box( def from_boundary_box(
cls, boundary_box: BoundaryBox, scale: int cls, boundary_box: BoundaryBox, zoom_level: int
) -> "Tiles": ) -> "Tiles":
""" """
Create minimal set of tiles that covers boundary box. Create minimal set of tiles that covers boundary box.
:param boundary_box: area to be covered by tiles :param boundary_box: area to be covered by tiles
:param scale: OpenStreetMap zoom level :param zoom_level: zoom level in OpenStreetMap terminology
""" """
tiles: list[Tile] = [] tiles: list[Tile] = []
tile_1: Tile = Tile.from_coordinates(boundary_box.get_left_top(), scale) tile_1: Tile = Tile.from_coordinates(
boundary_box.get_left_top(), zoom_level
)
tile_2: Tile = Tile.from_coordinates( tile_2: Tile = Tile.from_coordinates(
boundary_box.get_right_bottom(), scale boundary_box.get_right_bottom(), zoom_level
) )
for x in range(tile_1.x, tile_2.x + 1): for x in range(tile_1.x, tile_2.x + 1):
for y in range(tile_1.y, tile_2.y + 1): for y in range(tile_1.y, tile_2.y + 1):
tiles.append(Tile(x, y, scale)) tiles.append(Tile(x, y, zoom_level))
latitude_2, longitude_1 = tile_1.get_coordinates() latitude_2, longitude_1 = tile_1.get_coordinates()
latitude_1, longitude_2 = Tile( latitude_1, longitude_2 = Tile(
tile_2.x + 1, tile_2.y + 1, scale tile_2.x + 1, tile_2.y + 1, zoom_level
).get_coordinates() ).get_coordinates()
assert longitude_2 > longitude_1 assert longitude_2 > longitude_1
assert latitude_2 > latitude_1 assert latitude_2 > latitude_1
@ -225,7 +230,7 @@ class Tiles:
longitude_1, latitude_1, longitude_2, latitude_2 longitude_1, latitude_1, longitude_2, latitude_2
).round() ).round()
return cls(tiles, tile_1, tile_2, scale, extended_boundary_box) return cls(tiles, tile_1, tile_2, zoom_level, extended_boundary_box)
def load_osm_data(self, cache_path: Path) -> OSMData: def load_osm_data(self, cache_path: Path) -> OSMData:
"""Load OpenStreetMap data.""" """Load OpenStreetMap data."""
@ -307,11 +312,16 @@ class Tiles:
cropped.crop((0, 0, TILE_WIDTH, TILE_HEIGHT)).save( cropped.crop((0, 0, TILE_WIDTH, TILE_HEIGHT)).save(
tile.get_file_name(directory).with_suffix(".png") tile.get_file_name(directory).with_suffix(".png")
) )
logging.info(f"Tile {tile.scale}/{tile.x}/{tile.y} is created.") logging.info(
f"Tile {tile.zoom_level}/{tile.x}/{tile.y} is created."
)
def get_file_path(self, cache_path: Path) -> Path: def get_file_path(self, cache_path: Path) -> Path:
"""Get path of the output SVG file.""" """Get path of the output SVG file."""
return cache_path / f"{self.boundary_box.get_format()}_{self.scale}.svg" return (
cache_path
/ f"{self.boundary_box.get_format()}_{self.zoom_level}.svg"
)
def draw_image( def draw_image(
self, cache_path: Path, configuration: MapConfiguration self, cache_path: Path, configuration: MapConfiguration
@ -337,11 +347,11 @@ class Tiles:
if not output_path.exists(): if not output_path.exists():
top, left = self.tile_1.get_coordinates() top, left = self.tile_1.get_coordinates()
bottom, right = Tile( bottom, right = Tile(
self.tile_2.x + 1, self.tile_2.y + 1, self.scale self.tile_2.x + 1, self.tile_2.y + 1, self.zoom_level
).get_coordinates() ).get_coordinates()
flinger: Flinger = Flinger( flinger: Flinger = Flinger(
BoundaryBox(left, bottom, right, top), self.scale BoundaryBox(left, bottom, right, top), self.zoom_level
) )
extractor: ShapeExtractor = ShapeExtractor( extractor: ShapeExtractor = ShapeExtractor(
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
@ -377,32 +387,32 @@ class ScaleConfigurationException(Exception):
"""Wrong configuration format.""" """Wrong configuration format."""
def parse_scale(scale_specification: str) -> list[int]: def parse_zoom_level(zoom_level_specification: str) -> list[int]:
"""Parse scale specification.""" """Parse zoom level specification."""
parts: list[str] parts: list[str]
if "," in scale_specification: if "," in zoom_level_specification:
parts = scale_specification.split(",") parts = zoom_level_specification.split(",")
else: else:
parts = [scale_specification] parts = [zoom_level_specification]
def parse(scale: str) -> int: def parse(zoom_level: str) -> int:
"""Parse scale.""" """Parse zoom level."""
parsed_scale: int = int(scale) parsed_zoom_level: int = int(zoom_level)
if parsed_scale <= 0: if parsed_zoom_level <= 0:
raise ScaleConfigurationException("Non positive scale.") raise ScaleConfigurationException("Non positive zoom level.")
if parsed_scale > 20: if parsed_zoom_level > 20:
raise ScaleConfigurationException("Scale is too big.") raise ScaleConfigurationException("Scale is too big.")
return parsed_scale return parsed_zoom_level
result: list[int] = [] result: list[int] = []
for part in parts: for part in parts:
if "-" in part: if "-" in part:
from_, to = part.split("-") from_, to = part.split("-")
from_scale: int = parse(from_) from_zoom_level: int = parse(from_)
to_scale: int = parse(to) to_zoom_level: int = parse(to)
if from_scale > to_scale: if from_zoom_level > to_zoom_level:
raise ScaleConfigurationException("Wrong range.") raise ScaleConfigurationException("Wrong range.")
result += range(from_scale, to_scale + 1) result += range(from_zoom_level, to_zoom_level + 1)
else: else:
result.append(parse(part)) result.append(parse(part))
@ -414,28 +424,32 @@ def ui(options: argparse.Namespace) -> None:
directory: Path = workspace.get_tile_path() directory: Path = workspace.get_tile_path()
configuration: MapConfiguration = MapConfiguration.from_options(options) configuration: MapConfiguration = MapConfiguration.from_options(options)
scales: list[int] = parse_scale(options.scales) zoom_levels: list[int] = parse_zoom_level(options.zoom)
min_scale: int = min(scales) min_zoom_level: int = min(zoom_levels)
if options.coordinates: if options.coordinates:
coordinates: list[float] = list( coordinates: list[float] = list(
map(float, options.coordinates.strip().split(",")) map(float, options.coordinates.strip().split(","))
) )
min_tile: Tile = Tile.from_coordinates(np.array(coordinates), min_scale) min_tile: Tile = Tile.from_coordinates(
np.array(coordinates), min_zoom_level
)
try: try:
osm_data: OSMData = min_tile.load_osm_data(Path(options.cache)) osm_data: OSMData = min_tile.load_osm_data(Path(options.cache))
except NetworkError as e: except NetworkError as e:
raise NetworkError(f"Map is not loaded. {e.message}") raise NetworkError(f"Map is not loaded. {e.message}")
for scale in scales: for zoom_level in zoom_levels:
tile: Tile = Tile.from_coordinates(np.array(coordinates), scale) tile: Tile = Tile.from_coordinates(
np.array(coordinates), zoom_level
)
try: try:
tile.draw_with_osm_data(osm_data, directory, configuration) tile.draw_with_osm_data(osm_data, directory, configuration)
except NetworkError as e: except NetworkError as e:
logging.fatal(e.message) logging.fatal(e.message)
elif options.tile: elif options.tile:
scale, x, y = map(int, options.tile.split("/")) zoom_level, x, y = map(int, options.tile.split("/"))
tile: Tile = Tile(x, y, scale) tile: Tile = Tile(x, y, zoom_level)
tile.draw(directory, Path(options.cache), configuration) tile.draw(directory, Path(options.cache), configuration)
elif options.boundary_box: elif options.boundary_box:
boundary_box: Optional[BoundaryBox] = BoundaryBox.from_text( boundary_box: Optional[BoundaryBox] = BoundaryBox.from_text(
@ -443,14 +457,14 @@ def ui(options: argparse.Namespace) -> None:
) )
if boundary_box is None: if boundary_box is None:
sys.exit(1) sys.exit(1)
min_tiles: Tiles = Tiles.from_boundary_box(boundary_box, min_scale) min_tiles: Tiles = Tiles.from_boundary_box(boundary_box, min_zoom_level)
try: try:
osm_data: OSMData = min_tiles.load_osm_data(Path(options.cache)) osm_data: OSMData = min_tiles.load_osm_data(Path(options.cache))
except NetworkError as e: except NetworkError as e:
raise NetworkError(f"Map is not loaded. {e.message}") raise NetworkError(f"Map is not loaded. {e.message}")
for scale in scales: for zoom_level in zoom_levels:
tiles: Tiles = Tiles.from_boundary_box(boundary_box, scale) tiles: Tiles = Tiles.from_boundary_box(boundary_box, zoom_level)
tiles.draw(directory, Path(options.cache), configuration, osm_data) tiles.draw(directory, Path(options.cache), configuration, osm_data)
else: else:
logging.fatal( logging.fatal(

View file

@ -98,7 +98,7 @@ def add_tile_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"-t", "-t",
"--tile", "--tile",
metavar="<scale>/<x>/<y>", metavar="<zoom level>/<x>/<y>",
help="tile specification", help="tile specification",
) )
parser.add_argument( parser.add_argument(
@ -115,8 +115,8 @@ def add_tile_arguments(parser: argparse.ArgumentParser) -> None:
metavar="<lon1>,<lat1>,<lon2>,<lat2>", metavar="<lon1>,<lat1>,<lon2>,<lat2>",
) )
parser.add_argument( parser.add_argument(
"-s", "-z",
"--scales", "--zoom",
type=str, type=str,
metavar="<integer>", metavar="<integer>",
help="OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, " help="OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, "
@ -175,8 +175,8 @@ def add_render_arguments(parser: argparse.ArgumentParser) -> None:
metavar="<path>", metavar="<path>",
) )
parser.add_argument( parser.add_argument(
"-s", "-z",
"--scale", "--zoom",
type=int, type=int,
metavar="<integer>", metavar="<integer>",
help="OSM zoom level", help="OSM zoom level",

View file

@ -1,23 +0,0 @@
"""
Test scale specification parsing.
"""
from roentgen.tile import parse_scale
def test_scale_1() -> None:
assert parse_scale("18") == [18]
def test_scale_list() -> None:
assert parse_scale("17,18") == [17, 18]
assert parse_scale("16,17,18") == [16, 17, 18]
def test_scale_range() -> None:
assert parse_scale("16-18") == [16, 17, 18]
assert parse_scale("18-18") == [18]
def test_scale_mixed() -> None:
assert parse_scale("15,16-18") == [15, 16, 17, 18]
assert parse_scale("15,16-18,20") == [15, 16, 17, 18, 20]

23
test/test_zoom_level.py Normal file
View file

@ -0,0 +1,23 @@
"""
Test zoom level specification parsing.
"""
from roentgen.tile import parse_zoom_level
def test_zoom_level_1() -> None:
assert parse_zoom_level("18") == [18]
def test_zoom_level_list() -> None:
assert parse_zoom_level("17,18") == [17, 18]
assert parse_zoom_level("16,17,18") == [16, 17, 18]
def test_zoom_level_range() -> None:
assert parse_zoom_level("16-18") == [16, 17, 18]
assert parse_zoom_level("18-18") == [18]
def test_zoom_level_mixed() -> None:
assert parse_zoom_level("15,16-18") == [15, 16, 17, 18]
assert parse_zoom_level("15,16-18,20") == [15, 16, 17, 18, 20]