Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport master] Add missing output #2635

Merged
merged 2 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tilecloud_chain/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@
- **`name`** _(string)_: The name used in the admin interface.
- **`allowed_commands`** _(array)_: The allowed commands (main configuration). Default: `["generate-tiles", "generate-controller", "generate-cost"]`.
- **Items** _(string)_
- **`allowed_arguments`** _(array)_: The allowed arguments (main configuration). Default: `["--layer", "--get-hash", "--generate-legend-images", "--get-bbox", "--help", "--ignore-error", "--bbox", "--zoom", "--test", "--near", "--time", "--measure-generation-time", "--no-geom", "--dimensions"]`.
- **`allowed_arguments`** _(array)_: The allowed arguments (main configuration). Default: `["--layer", "--get-hash", "--generate-legend-images", "--get-bbox", "--ignore-error", "--bbox", "--zoom", "--test", "--near", "--time", "--measure-generation-time", "--no-geom", "--dimensions"]`.
- **Items** _(string)_
- **`admin_footer`** _(string)_: The footer of the admin interface.
- **`admin_footer_classes`** _(string)_: The CSS classes used on the footer of the admin interface.
Expand Down
16 changes: 12 additions & 4 deletions tilecloud_chain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,11 @@ def __init__(
self,
gene: "TileGeneration",
functions: list[Callable[[Tile], Tile]],
out: IO[str] | None = None,
) -> None:
self.gene = gene
self.functions = functions
self.out = out
self.safe = gene.options is None or not gene.options.debug
daemon = gene.options is not None and getattr(gene.options, "daemon", False)
self.max_consecutive_errors = (
Expand Down Expand Up @@ -268,7 +270,11 @@ def __call__(self, tile: Tile | None) -> Tile | None:
):
assert isinstance(tile.error, str)
tile.error = f"WMS server error: {self._re_rm_xml_tag.sub('', tile.error)}"
print(f"Error with tile {tile.tilecoord} {tile.formated_metadata}:\n{tile.error}")
if self.out:
print(
f"Error with tile {tile.tilecoord} {tile.formated_metadata}:\n{tile.error}",
file=self.out,
)
_LOGGER.warning(
"Error with tile %s %s:\n%s", tile.tilecoord, tile.formated_metadata, tile.error
)
Expand Down Expand Up @@ -435,6 +441,7 @@ def __init__(
configure_logging: bool = True,
multi_thread: bool = True,
maxconsecutive_errors: bool = True,
out: IO[str] | None = None,
):
self.geoms_cache: dict[str, dict[str, DatedGeoms]] = {}
self._close_actions: list[Close] = []
Expand All @@ -446,6 +453,7 @@ def __init__(
self.metatilesplitter_thread_pool: ThreadPoolExecutor | None = None
self.multi_thread = multi_thread
self.maxconsecutive_errors = maxconsecutive_errors
self.out = out
self.grid_cache: dict[str, dict[str, DatedTileGrid]] = {}
self.layer_legends: dict[str, list[Legend]] = {}
self.config_file = config_file
Expand Down Expand Up @@ -975,7 +983,7 @@ def get_splitter(config_file: str, layer_name: str) -> MetaTileSplitterTileStore

store = TimedTileStoreWrapper(MultiTileStore(get_splitter), store_name="splitter")

run = Run(self, self.functions_tiles)
run = Run(self, self.functions_tiles, out=self.out)
nb_thread = int(os.environ.get("TILE_NB_THREAD", "1"))
if nb_thread == 1 or not self.multi_thread:

Expand Down Expand Up @@ -1379,7 +1387,7 @@ def consume(self, test: int | None = None) -> None:

start = datetime.now()

run = Run(self, self.functions_metatiles)
run = Run(self, self.functions_metatiles, out=self.out)

if test is None:
if TYPE_CHECKING:
Expand Down Expand Up @@ -1579,7 +1587,7 @@ def __call__(self, tile: Tile) -> Tile:
if ref is None:
ref = px
elif px != ref:
print("Error: image is not uniform.")
print("Error: image is not uniform.", file=self.out)
_LOGGER.debug("Error: image is not uniform.")
sys.exit(1)

Expand Down
2 changes: 0 additions & 2 deletions tilecloud_chain/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"--get-hash",
"--generate-legend-images",
"--get-bbox",
"--help",
"--ignore-error",
"--bbox",
"--zoom",
Expand Down Expand Up @@ -2526,7 +2525,6 @@ class Server(TypedDict, total=False):
- --get-hash
- --generate-legend-images
- --get-bbox
- --help
- --ignore-error
- --bbox
- --zoom
Expand Down
209 changes: 120 additions & 89 deletions tilecloud_chain/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from urllib.parse import urlencode, urljoin

import botocore.exceptions
import PIL.ImageFile
import requests
import ruamel.yaml
import tilecloud.store.redis
Expand Down Expand Up @@ -44,8 +45,6 @@

def main(args: list[str] | None = None, out: IO[str] | None = None) -> None:
"""Generate the contextual file like the legends."""
del out

try:
parser = ArgumentParser(
description="Used to generate the contextual file like the capabilities, the legends, "
Expand All @@ -72,7 +71,7 @@ def main(args: list[str] | None = None, out: IO[str] | None = None) -> None:
)

options = parser.parse_args(args[1:] if args else sys.argv[1:])
gene = TileGeneration(options.config, options, layer_name=options.layer)
gene = TileGeneration(options.config, options, layer_name=options.layer, out=out)
assert gene.config_file
config = gene.get_config(gene.config_file)

Expand All @@ -94,7 +93,7 @@ def main(args: list[str] | None = None, out: IO[str] | None = None) -> None:
sys.exit(0)

if options.legends:
_generate_legend_images(gene)
_generate_legend_images(gene, out)

except SystemExit:
raise
Expand Down Expand Up @@ -362,7 +361,113 @@ def _fill_legend(
previous_resolution = resolution


def _generate_legend_images(gene: TileGeneration) -> None:
def _get_legend_image(
layer_name: str,
wms_layer: str,
resolution: float,
url: str,
session: requests.Session,
main_mime_type: str,
out: IO[str] | None = None,
) -> PIL.ImageFile.ImageFile | None:
_LOGGER.debug(
"Get legend image for layer '%s'-'%s', resolution '%s': %s",
layer_name,
wms_layer,
resolution,
url,
)
try:
response = session.get(url)
except Exception as e: # pylint: disable=broad-exception-caught
if out is not None:
print(
"\n".join(
[
f"Unable to get legend image for layer '{layer_name}'-'{wms_layer}', resolution '{resolution}'",
url,
str(e),
]
),
file=out,
)
_LOGGER.debug(
"Unable to get legend image for layer '%s'-'%s', resolution '%s'",
layer_name,
wms_layer,
resolution,
exc_info=True,
)
return None
if response.status_code != 200:
if out is not None:
print(
"\n".join(
[
f"Unable to get legend image for layer '{layer_name}'-'{wms_layer}', resolution '{resolution}'",
url,
f"status code: {response.status_code}: {response.reason}",
response.text,
]
),
file=out,
)
_LOGGER.debug(
"Unable to get legend image for layer '%s'-'%s', resolution '%s': %s",
layer_name,
wms_layer,
resolution,
response.text,
)
return None
if not response.headers["Content-Type"].startswith(main_mime_type):
if out is not None:
print(
"\n".join(
[
f"Unable to get legend image for layer '{layer_name}'-'{wms_layer}', resolution '{resolution}'",
url,
f"Content-Type: {response.headers['Content-Type']}",
response.text,
]
),
file=out,
)
_LOGGER.debug(
"Unable to get legend image for layer '%s'-'%s', resolution '%s', content-type: %s: %s",
layer_name,
wms_layer,
resolution,
response.headers["Content-Type"],
response.text,
)
return None
try:
return Image.open(BytesIO(response.content))
except Exception: # pylint: disable=broad-exception-caught
if out is not None:
print(
"\n".join(
[
f"Unable to read legend image for layer '{layer_name}'-'{wms_layer}', resolution '{resolution}'",
url,
response.text,
]
),
file=out,
)
_LOGGER.debug(
"Unable to read legend image for layer '%s'-'%s', resolution '%s': %s",
layer_name,
wms_layer,
resolution,
response.text,
exc_info=True,
)
return None


def _generate_legend_images(gene: TileGeneration, out: IO[str] | None = None) -> None:
assert gene.config_file
config = gene.get_config(gene.config_file)
cache = config.config["caches"][gene.options.cache]
Expand All @@ -374,7 +479,7 @@ def _generate_legend_images(gene: TileGeneration) -> None:
previous_hash = None
for zoom, resolution in enumerate(config.config["grids"][layer["grid"]]["resolutions"]):
legends = []
for wmslayer in layer["layers"].split(","):
for wms_layer in layer["layers"].split(","):
url = (
layer["url"]
+ "?"
Expand All @@ -383,100 +488,26 @@ def _generate_legend_images(gene: TileGeneration) -> None:
"SERVICE": "WMS",
"VERSION": layer.get("version", "1.0.0"),
"REQUEST": "GetLegendGraphic",
"LAYER": wmslayer,
"LAYER": wms_layer,
"FORMAT": layer["legend_mime"],
"TRANSPARENT": "TRUE" if layer["legend_mime"] == "image/png" else "FALSE",
"STYLE": layer["wmts_style"],
"SCALE": resolution / 0.00028,
}
)
)
_LOGGER.debug(
"Get legend image for layer '%s'-'%s', resolution '%s': %s",
legend_image = _get_legend_image(
layer_name,
wmslayer,
wms_layer,
resolution,
url,
session,
layer["legend_mime"].split("/")[0],
out,
)
try:
response = session.get(url)
except Exception as e: # pylint: disable=broad-exception-caught
print(
"\n".join(
[
f"Unable to get legend image for layer '{layer_name}'-'{wmslayer}', resolution '{resolution}'",
url,
str(e),
]
)
)
_LOGGER.debug(
"Unable to get legend image for layer '%s'-'%s', resolution '%s'",
layer_name,
wmslayer,
resolution,
exc_info=True,
)
continue
if response.status_code != 200:
print(
"\n".join(
[
f"Unable to get legend image for layer '{layer_name}'-'{wmslayer}', resolution '{resolution}'",
url,
f"status code: {response.status_code}: {response.reason}",
response.text,
]
)
)
_LOGGER.debug(
"Unable to get legend image for layer '%s'-'%s', resolution '%s': %s",
layer_name,
wmslayer,
resolution,
response.text,
)
continue
if not response.headers["Content-Type"].startswith(layer["legend_mime"].split("/")[0]):
print(
"\n".join(
[
f"Unable to get legend image for layer '{layer_name}'-'{wmslayer}', resolution '{resolution}'",
url,
f"Content-Type: {response.headers['Content-Type']}",
response.text,
]
)
)
_LOGGER.debug(
"Unable to get legend image for layer '%s'-'%s', resolution '%s', content-type: %s: %s",
layer_name,
wmslayer,
resolution,
response.headers["Content-Type"],
response.text,
)
continue
try:
legends.append(Image.open(BytesIO(response.content)))
except Exception: # pylint: disable=broad-exception-caught
print(
"\n".join(
[
f"Unable to read legend image for layer '{layer_name}'-'{wmslayer}', resolution '{resolution}'",
url,
response.text,
]
)
)
_LOGGER.debug(
"Unable to read legend image for layer '%s'-'%s', resolution '%s': %s",
layer_name,
wmslayer,
resolution,
response.text,
exc_info=True,
)
if legend_image is not None:
legends.append(legend_image)

width = max(1, max(i.size[0] for i in legends) if legends else 0)
height = max(1, sum(i.size[1] for i in legends) if legends else 0)
image = Image.new("RGBA", (width, height))
Expand Down
1 change: 1 addition & 0 deletions tilecloud_chain/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ def main(args: list[str] | None = None, out: IO[str] | None = None) -> None:
config_file=options.config or os.environ.get("TILEGENERATION_CONFIGFILE"),
options=options,
multi_thread=options.get_hash is None,
out=out,
)

if (
Expand Down
1 change: 0 additions & 1 deletion tilecloud_chain/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,6 @@
"--get-hash",
"--generate-legend-images",
"--get-bbox",
"--help",
"--ignore-error",
"--bbox",
"--zoom",
Expand Down
6 changes: 3 additions & 3 deletions tilecloud_chain/store/postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,13 @@ def _start_job(
_LOGGER.info("Running the command `%s` using the function directly", display_command)
main(final_command, out)
_LOGGER.info("Successfully ran the command `%s` using the function directly", display_command)
except Exception: # pylint: disable=broad-exception-caught
_LOGGER.exception("Error while running the command `%s`", display_command)
error = True
except SystemExit as exception:
if exception.code != 0:
_LOGGER.exception("Error while running the command `%s`", display_command)
error = True
except Exception: # pylint: disable=broad-exception-caught
_LOGGER.exception("Error while running the command `%s`", display_command)
error = True

with SessionMaker() as session:
job = session.query(Job).where(Job.id == job_id).with_for_update(of=Job).one()
Expand Down
Loading