diff --git a/README.md b/README.md index eb9525b..bceffd2 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Röntgen features: * detailed icons to display subtypes like [power tower design](#power-tower-design), * can display multiple icons for one entity to cover more features, * use color to visualize [`colour`](https://wiki.openstreetmap.org/wiki/Key:colour) and other features like plant types, - * display [privitive 3D shapes](#levels) for buildings, + * display [primitive 3D shapes](#levels) for buildings, * display [directions](#direction) with gradient sectors, * use width to display roads. @@ -171,14 +171,23 @@ Command: `tile`. | `--cache` | path for temporary OSM files, default value: `cache` | | `-b`, `--boundary-box` | construct the minimum amount of tiles that cover requested boundary box | +### Generate one tile ### + +Specify tile coordinates: + +```bash +python roentgen.py tile --tile ${OSM_ZOOM_LEVEL}/${X}/${Y} +``` + +or specify any geographical coordinates inside a tile: ```bash python roentgen.py tile \ - -c ${LATITUDE},${LONGITUDE} \ - -s ${OSM_ZOOM_LEVEL} + --coordinates ${LATITUDE},${LONGITUDE} \ + --scale ${OSM_ZOOM_LEVEL} ``` -Tile will be stored as SVG file to `out/tiles/tile___.svg`, where `x` and `y` are tile coordinates. +Tile will be stored as SVG file `out/tiles/tile___.svg` and PNG file `out/tiles/tile___.svg`, where `x` and `y` are tile coordinates. Example: @@ -186,7 +195,18 @@ Example: python roentgen.py tile -c 55.7510637,37.6270761 -s 18 ``` -will generate SVG file `out/tiles/tile_18_158471_81953.svg`. +will generate SVG file `out/tiles/tile_18_158471_81953.svg` and PNG file `out/tiles/tile_18_158471_81953.png`. + +### Generate a set of tiles ### + +Specify boundary box to get the minimal set of tiles that covers the area: + +```bash +python roentgen.py tile \ + --boundary-box ${LONGITUDE_1},${LATITUDE_1},${LONGITUDE_2},${LATITUDE_2} +``` + +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, and finally boundary box coordinates will be rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files `out/tiles/tile___.svg` and PNG files `out/tiles/tile___.svg`, where `x` and `y` are tile coordinates. MapCSS 0.2 generation --------------------- @@ -201,7 +221,7 @@ To create MapCSS with only Röntgen icons run `python roentgen.py mapcss --no-wa |---|---| | `--icons` | add icons for nodes and areas, set by default | | `--ways` | add style for ways and relations, set by default | -| `--lifecycle` | add icons for lifecycle tags, set by default | +| `--lifecycle` | add icons for lifecycle tags; be careful: this will increase the number of node and area selectors by 9 times, set by default | ### Use Röntgen as JOSM map paint style ### diff --git a/doc/readme.moi b/doc/readme.moi index 96b6480..da01554 100644 --- a/doc/readme.moi +++ b/doc/readme.moi @@ -25,7 +25,7 @@ Röntgen features\: {detailed icons to display subtypes like \ref {#power-tower-design} {power tower design},} {can display multiple icons for one entity to cover more features,} {use color to visualize \osm {colour} and other features like plant types,} - {display \ref {#levels} {privitive 3D shapes} for buildings,} + {display \ref {#levels} {primitive 3D shapes} for buildings,} {display \ref {#direction} {directions} with gradient sectors,} {use width to display roads.} @@ -42,16 +42,16 @@ Visualization of tree leaf types (broadleaved or needleleaved) and genus or taxo /* \table - {{\osm {natural=tree}} {\icon {tree}}} - {{\osm {leaf_type=broadleaved}} {\icon {tree_with_leaf}}} - {{\osm {leaf_type=needleleaved}} {\icon {needleleaved_tree}}} - {{\osm {taxon=Arecaceae}} {\icon {palm}}} - {{\osm {genus=Betula}} {\icon {betula}}} + {{\osm {natural=tree}} {\icon {tree}}} + {{\osm {leaf_type=broadleaved}} {\icon {tree_with_leaf}}} + {{\osm {leaf_type=needleleaved}} {\icon {needleleaved_tree}}} + {{\osm {taxon=Arecaceae}} {\icon {palm}}} + {{\osm {genus=Betula}} {\icon {betula}}} \table - {{} {\color {#98AC64}}} - {{\osm {leaf_cycle=deciduous}} {\color {#fcaf3e}}} - {{\osm {leaf_cycle=evergreen}} {\color {#688C44}}} + {{} {\color {#98AC64}}} + {{\osm {leaf_cycle=deciduous}} {\color {#fcaf3e}}} + {{\osm {leaf_cycle=evergreen}} {\color {#688C44}}} */ @@ -154,7 +154,7 @@ Visualize element creation time with \m {--mode time}. \image {doc/time.png} {Creation time mode} -\3 {Author mode} {autor_mode} +\3 {Author mode} {author_mode} Every way and node displayed with the random color picked for each author with \m {--mode user-coloring}. @@ -193,17 +193,35 @@ Command\: \m {tile}. \options {tile} -\code {python roentgen.py tile \\ - -c $\{LATITUDE\},$\{LONGITUDE\} \\ - -s $\{OSM_ZOOM_LEVEL\}} {bash} +\3 {Generate one tile} {generate-one-tile} -Tile will be stored as SVG file to \m {out/tiles/tile___.svg}, where \m {x} and \m {y} are tile coordinates. +Specify tile coordinates\: + +\code {python roentgen.py tile --tile $\{OSM_ZOOM_LEVEL\}/$\{X\}/$\{Y\}} {bash} + +or specify any geographical coordinates inside a tile\: + +\code {python roentgen.py tile \\ + --coordinates $\{LATITUDE\},$\{LONGITUDE\} \\ + --scale $\{OSM_ZOOM_LEVEL\}} {bash} + +Tile will be stored as SVG file \m {out/tiles/tile___.svg} and PNG file \m {out/tiles/tile___.svg}, where \m {x} and \m {y} are tile coordinates. Example\: \code {python roentgen.py tile -c 55.7510637,37.6270761 -s 18} {bash} -will generate SVG file \m {out/tiles/tile_18_158471_81953.svg}. +will generate SVG file \m {out/tiles/tile_18_158471_81953.svg} and PNG file \m {out/tiles/tile_18_158471_81953.png}. + +\3 {Generate a set of tiles} {generate-a-set-of-tiles} + +Specify boundary box to get the minimal set of tiles that covers the area\: + +\code {python roentgen.py tile \\ + --boundary-box $\{LONGITUDE_1\},$\{LATITUDE_1\},$\{LONGITUDE_2\},$\{LATITUDE_2\} \\ + --scale $\{OSM_ZOOM_LEVEL\}} {bash} + +Boundary box will be extended to the boundaries of the minimal tile set that covers the area, then it will be extended a bit more to avoid some artifacts on the edges rounded to 3 digits after the decimal point. Map with new boundary box coordinates will be written to the cache directory as SVG and PNG files. All tiles will be stored as SVG files \m {out/tiles/tile___.svg} and PNG files \m {out/tiles/tile___.svg}, where \m {x} and \m {y} are tile coordinates. \2 {MapCSS 0.2 generation} {mapcss-0-2-generation} diff --git a/roentgen/tile.py b/roentgen/tile.py index 983d004..0500c9f 100644 --- a/roentgen/tile.py +++ b/roentgen/tile.py @@ -28,6 +28,8 @@ from roentgen.workspace import workspace __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" +TILE_WIDTH, TILE_HEIGHT = 256, 256 + @dataclass class Tiles: @@ -87,9 +89,9 @@ class Tiles: for tile in self.tiles: file_path: Path = tile.get_file_name(directory) if not file_path.exists(): - tile.draw_for_map(osm_data, directory) + tile.draw_with_osm_data(osm_data, directory) else: - logging.info(f"File {file_path} already exists.") + logging.debug(f"File {file_path} already exists.") output_path: Path = file_path.with_suffix(".png") if not output_path.exists(): @@ -99,7 +101,7 @@ class Tiles: ) logging.info(f"SVG file is rasterized to {output_path}.") else: - logging.info(f"File {output_path} already exists.") + logging.debug(f"File {output_path} already exists.") def draw(self, directory: Path, cache_path: Path) -> None: """ @@ -113,7 +115,6 @@ class Tiles: self.boundary_box.get_format() + ".png" ) self.draw_image(cache_path) - width, height = 256, 256 with input_path.open("rb") as input_file: image = Image.open(input_file) @@ -121,11 +122,14 @@ class Tiles: for tile in self.tiles: x = tile.x - self.tile_1.x y = tile.y - self.tile_1.y - cropped = image.crop( - (x * width, y * height, (x + 1) * width, (y + 1) * height) + area = ( + x * TILE_WIDTH, + y * TILE_HEIGHT, + (x + 1) * TILE_WIDTH, + (y + 1) * TILE_HEIGHT, ) - print(x * width, y * height, (x + 1) * width, (y + 1) * height) - cropped.crop((0, 0, width, height)).save( + cropped = image.crop(area) + cropped.crop((0, 0, TILE_WIDTH, TILE_HEIGHT)).save( tile.get_file_name(directory).with_suffix(".png") ) logging.info(f"Tile 18/{tile.x}/{tile.y} is created.") @@ -173,7 +177,7 @@ class Tiles: with output_path.open("w+") as output_file: svg.write(output_file) else: - logging.info(f"File {output_path} already exists.") + logging.debug(f"File {output_path} already exists.") png_path: Path = cache_path / f"{self.boundary_box.get_format()}.png" if not png_path.exists(): @@ -181,7 +185,7 @@ class Tiles: cairosvg.svg2png(file_obj=input_file, write_to=str(png_path)) logging.info(f"SVG file is rasterized to {png_path}.") else: - logging.info(f"File {png_path} already exists.") + logging.debug(f"File {png_path} already exists.") @dataclass @@ -197,7 +201,12 @@ class Tile: @classmethod def from_coordinates(cls, coordinates: np.array, scale: int): - """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 + :param scale: OpenStreetMap zoom level + """ lat_rad = np.radians(coordinates[0]) n: float = 2.0 ** scale x: int = int((coordinates[1] + 180.0) / 360.0 * n) @@ -262,7 +271,7 @@ class Tile: def draw(self, directory_name: Path, cache_path: Path) -> None: """ - Draw tile to SVG file. + Draw tile to SVG and PNG files. :param directory_name: output directory to storing tiles :param cache_path: directory to store SVG and PNG tiles @@ -270,12 +279,14 @@ class Tile: try: osm_data: OSMData = self.load_osm_data(cache_path) except NetworkError as e: - raise NetworkError(f"Map does not loaded. {e.message}") + raise NetworkError(f"Map is not loaded. {e.message}") - self.draw_for_map(osm_data, directory_name) + self.draw_with_osm_data(osm_data, directory_name) - def draw_for_map(self, osm_data: OSMData, directory_name: Path) -> None: - """Draw tile using existing map.""" + def draw_with_osm_data( + self, osm_data: OSMData, directory_name: Path + ) -> None: + """Draw SVG and PNG tile using OpenStreetMap data.""" lat1, lon1 = self.get_coordinates() lat2, lon2 = Tile(self.x + 1, self.y + 1, self.scale).get_coordinates() @@ -302,9 +313,14 @@ class Tile: painter: Map = Map(flinger=flinger, svg=svg, scheme=scheme) painter.draw(constructor) - logging.info(f"Writing output SVG {output_file_name}...") with output_file_name.open("w") as output_file: svg.write(output_file) + logging.info(f"Tile is drawn to {output_file_name}.") + + output_path: Path = output_file_name.with_suffix(".png") + with output_file_name.open() as input_file: + cairosvg.svg2png(file_obj=input_file, write_to=str(output_path)) + logging.info(f"SVG file is rasterized to {output_path}.") def ui(options) -> None: