From f6d9436fb8f0098335e5eed36257b76d4a8cf599 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 8 Dec 2025 11:16:48 +0100 Subject: [PATCH 01/94] Implement DuckDB-based converter --- fiboa_cli/conversion/duckdb.py | 111 +++++++++++++++++++++++++++++++++ fiboa_cli/datasets/jp.py | 21 ++----- 2 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 fiboa_cli/conversion/duckdb.py diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py new file mode 100644 index 00000000..932cb422 --- /dev/null +++ b/fiboa_cli/conversion/duckdb.py @@ -0,0 +1,111 @@ +import json +import os + +import duckdb +from vecorel_cli.encoding.geojson import VecorelJSONEncoder + +from .fiboa_converter import FiboaBaseConverter + + +class FiboaDuckDBBaseConverter(FiboaBaseConverter): + def convert( + self, + output_file, + cache=None, + input_files=None, + variant=None, + compression=None, + geoparquet_version=None, + original_geometries=False, + **kwargs, + ) -> str: + self.variant = variant + cid = self.id.strip() + if self.bbox is not None and len(self.bbox) != 4: + raise ValueError("If provided, the bounding box must consist of 4 numbers") + + # Create output folder if it doesn't exist + directory = os.path.dirname(output_file) + if directory: + os.makedirs(directory, exist_ok=True) + + if input_files is not None and isinstance(input_files, dict) and len(input_files) > 0: + self.warning("Using user provided input file(s) instead of the pre-defined file(s)") + urls = input_files + else: + urls = self.get_urls() + if urls is None: + raise ValueError("No input files provided") + + selections = [] + geom_column = None + for k, v in self.columns.items(): + if k in self.column_migrations: + selections.append(f'{self.column_migrations.get(k)} as "{v}"') + else: + selections.append(f'"{k}" as "{v}"') + if v == "geometry": + geom_column = k + + filters = [] + where = "" + if self.bbox is not None: + filters.append( + f"ST_Intersects(geometry, ST_MakeEnvelope({self.bbox[0]}, {self.bbox[1]}, {self.bbox[2]}, {self.bbox[3]}))" + ) + for k, v in self.column_filters.items(): + filters.append(v) + if len(filters) > 0: + where = f"WHERE {' AND '.join(filters)}" + + selection = ", ".join(selections) + if isinstance(urls, str): + sources = f'"{urls}"' + else: + sources = "[" + ",".join([f'"{url}"' for url in urls]) + "]" + + _collection = self.create_collection(cid) + _collection.update(self.column_additions) + collection = json.dumps(_collection, cls=VecorelJSONEncoder).encode("utf-8") + + # TODO how to get metadata ARROW:schema ? + # from vecorel_cli.parquet.types import get_pyarrow_field + # schemas = _collection.merge_schemas({}) + # props = schemas.get("properties", {}) + # pq_fields = [] + # for column in self.columns.values(): + # schema = props.get(column, {}) + # dtype = schema.get("type") + # if dtype is None: + # self.warning(f"{column}: No mapping") + # continue + # try: + # field = get_pyarrow_field(column, schema=schema) + # pq_fields.append(field) + # except Exception as e: + # self.warning(f"{column}: Skipped - {e}") + # + # pq_schema = pa.schema(pq_fields) + # pq_schema = pq_schema.with_metadata({"collection": collection}) + + con = duckdb.connect() + con.install_extension("spatial") + con.load_extension("spatial") + con.execute( + f""" + COPY ( + SELECT {selection} FROM read_parquet({sources}) + {where} + ORDER BY ST_Hilbert({geom_column}) + ) TO ? ( + FORMAT parquet, + compression 'brotli', + KV_METADATA {{ + collection: ?, + }} + ) + """, + [output_file, collection], + ) + + return output_file diff --git a/fiboa_cli/datasets/jp.py b/fiboa_cli/datasets/jp.py index 57c31a24..1799c7f4 100644 --- a/fiboa_cli/datasets/jp.py +++ b/fiboa_cli/datasets/jp.py @@ -1,10 +1,9 @@ -import pandas as pd +from fiboa_cli.conversion.duckdb import FiboaDuckDBBaseConverter -from ..conversion.fiboa_converter import FiboaBaseConverter - -class JPConverter(FiboaBaseConverter): +class JPConverter(FiboaDuckDBBaseConverter): variants = { + "test": "./tests/data-files/convert/jp/jp_field_polygons_2024.parquet", "2024": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2024.parquet", "2023": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2023.parquet", "2022": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2022.parquet", @@ -30,23 +29,11 @@ class JPConverter(FiboaBaseConverter): "polygon_uuid": "id", "land_type_en": "land_type_en", "local_government_cd": "admin_local_code", - "issue_year": "determination:datetime", - } - column_migrations = { - "issue_year": lambda col: pd.to_datetime(col, format="%Y"), } - + column_additions = {"determination:datetime": "2024-01-01T00:00:00Z"} missing_schemas = { "properties": { "land_type_en": {"type": "string"}, "admin_local_code": {"type": "string"}, } } - - def convert(self, *args, **kwargs): - # Open only these columns to limit memory usage - super().convert( - *args, - columns=["GEOM", "polygon_uuid", "land_type_en", "local_government_cd", "issue_year"], - **kwargs, - ) From 6f52d9732e935cacbac9edb4c6979ba79e9eb03c Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 8 Dec 2025 14:13:14 +0100 Subject: [PATCH 02/94] Not sure if this is right --- fiboa_cli/conversion/duckdb.py | 40 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 932cb422..a43b7ff9 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -2,7 +2,9 @@ import os import duckdb +import pyarrow as pa from vecorel_cli.encoding.geojson import VecorelJSONEncoder +from vecorel_cli.parquet.types import get_pyarrow_field from .fiboa_converter import FiboaBaseConverter @@ -68,24 +70,23 @@ def convert( _collection.update(self.column_additions) collection = json.dumps(_collection, cls=VecorelJSONEncoder).encode("utf-8") - # TODO how to get metadata ARROW:schema ? - # from vecorel_cli.parquet.types import get_pyarrow_field - # schemas = _collection.merge_schemas({}) - # props = schemas.get("properties", {}) - # pq_fields = [] - # for column in self.columns.values(): - # schema = props.get(column, {}) - # dtype = schema.get("type") - # if dtype is None: - # self.warning(f"{column}: No mapping") - # continue - # try: - # field = get_pyarrow_field(column, schema=schema) - # pq_fields.append(field) - # except Exception as e: - # self.warning(f"{column}: Skipped - {e}") - # - # pq_schema = pa.schema(pq_fields) + schemas = _collection.merge_schemas({}) + props = schemas.get("properties", {}) + pq_fields = [] + for column in self.columns.values(): + schema = props.get(column, {}) + dtype = schema.get("type") + if dtype is None: + self.warning(f"{column}: No mapping") + continue + try: + field = get_pyarrow_field(column, schema=schema) + pq_fields.append(field) + except Exception as e: + self.warning(f"{column}: Skipped - {e}") + + pq_schema = pa.schema(pq_fields) + schema_bytes = pq_schema.serialize().to_pybytes() # pq_schema = pq_schema.with_metadata({"collection": collection}) con = duckdb.connect() @@ -102,10 +103,11 @@ def convert( compression 'brotli', KV_METADATA {{ collection: ?, + "PYARROW:schema": ? }} ) """, - [output_file, collection], + [output_file, collection, schema_bytes], ) return output_file From 0a854ad05de581e6c53464e25fb8062327c0cf0a Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 8 Dec 2025 21:42:18 +0100 Subject: [PATCH 03/94] Update project --- CHANGELOG.md | 1 + pixi.lock | 67 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4960ebe..86215207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +- Add DuckDB BaseConverter for efficiently transforming large datasets - Extend create_stac, include include fiboa data - Publish command; skip hidden files, generate better texts - Fix to vecorel: converter.license and provider should be string diff --git a/pixi.lock b/pixi.lock index f7ff81e9..fc4ca0a4 100644 --- a/pixi.lock +++ b/pixi.lock @@ -141,6 +141,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/29/153d1b4fc14c68e6766d7712d35a7ab6272a801c52160126ac7df681f758/duckdb-1.4.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -306,6 +307,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h8210216_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/b8/97f4f07d9459f5d262751cccfb2f4256debb8fe5ca92370cebe21aab1ee2/duckdb-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -472,6 +474,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/ea/112f33ace03682bafd4aaf0a3336da689b9834663e7032b3d678fd2902c9/duckdb-1.4.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -636,6 +639,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/b7/8d3a58b5ebfb9e79ed4030a0f2fbd7e404c52602e977b1e7ab51651816c7/duckdb-1.4.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -793,6 +797,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py313h54dd161_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/29/153d1b4fc14c68e6766d7712d35a7ab6272a801c52160126ac7df681f758/duckdb-1.4.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -933,6 +938,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstandard-0.25.0-py313hcb05632_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h8210216_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/b8/97f4f07d9459f5d262751cccfb2f4256debb8fe5ca92370cebe21aab1ee2/duckdb-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1074,6 +1080,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py313h9734d34_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/ea/112f33ace03682bafd4aaf0a3336da689b9834663e7032b3d678fd2902c9/duckdb-1.4.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1214,6 +1221,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py313h5fd188c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/b7/8d3a58b5ebfb9e79ed4030a0f2fbd7e404c52602e977b1e7ab51651816c7/duckdb-1.4.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1333,6 +1341,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py313h54dd161_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/29/153d1b4fc14c68e6766d7712d35a7ab6272a801c52160126ac7df681f758/duckdb-1.4.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1436,6 +1445,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstandard-0.25.0-py313hcb05632_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h8210216_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/b8/97f4f07d9459f5d262751cccfb2f4256debb8fe5ca92370cebe21aab1ee2/duckdb-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1540,6 +1550,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py313h9734d34_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/ea/112f33ace03682bafd4aaf0a3336da689b9834663e7032b3d678fd2902c9/duckdb-1.4.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1641,6 +1652,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py313h5fd188c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/b7/8d3a58b5ebfb9e79ed4030a0f2fbd7e404c52602e977b1e7ab51651816c7/duckdb-1.4.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1784,6 +1796,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/29/153d1b4fc14c68e6766d7712d35a7ab6272a801c52160126ac7df681f758/duckdb-1.4.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -1912,6 +1925,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h8210216_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/b8/97f4f07d9459f5d262751cccfb2f4256debb8fe5ca92370cebe21aab1ee2/duckdb-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -2041,6 +2055,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/ea/112f33ace03682bafd4aaf0a3336da689b9834663e7032b3d678fd2902c9/duckdb-1.4.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -2166,6 +2181,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda - pypi: https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/b7/8d3a58b5ebfb9e79ed4030a0f2fbd7e404c52602e977b1e7ab51651816c7/duckdb-1.4.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/3e/0d/424de6e5612f1399ff69bf86500d6a62ff0a4843979701ae97f120c7f1fe/flatdict-4.0.1.tar.gz - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0b/70/d5cd0696eff08e62fdbdebe5b46527facb4e7220eabe0ac6225efab50168/geopandas-1.1.1-py3-none-any.whl @@ -2903,6 +2919,54 @@ packages: - pkg:pypi/distlib?source=hash-mapping size: 275642 timestamp: 1752823081585 +- pypi: https://files.pythonhosted.org/packages/2c/b8/97f4f07d9459f5d262751cccfb2f4256debb8fe5ca92370cebe21aab1ee2/duckdb-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl + name: duckdb + version: 1.4.2 + sha256: f1fac31babda2045d4cdefe6d0fd2ebdd8d4c2a333fbcc11607cfeaec202d18d + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/58/b7/8d3a58b5ebfb9e79ed4030a0f2fbd7e404c52602e977b1e7ab51651816c7/duckdb-1.4.2-cp313-cp313-win_amd64.whl + name: duckdb + version: 1.4.2 + sha256: 2f7c61617d2b1da3da5d7e215be616ad45aa3221c4b9e2c4d1c28ed09bc3c1c4 + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/82/29/153d1b4fc14c68e6766d7712d35a7ab6272a801c52160126ac7df681f758/duckdb-1.4.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + name: duckdb + version: 1.4.2 + sha256: a456adbc3459c9dcd99052fad20bd5f8ef642be5b04d09590376b2eb3eb84f5c + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/a4/ea/112f33ace03682bafd4aaf0a3336da689b9834663e7032b3d678fd2902c9/duckdb-1.4.2-cp313-cp313-macosx_11_0_arm64.whl + name: duckdb + version: 1.4.2 + sha256: 43ac632f40ab1aede9b4ce3c09ea043f26f3db97b83c07c632c84ebd7f7c0f4a + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda sha256: ce61f4f99401a4bd455b89909153b40b9c823276aefcbb06f2044618696009ca md5: 72e42d28960d875c7654614f8b50939a @@ -2917,10 +2981,11 @@ packages: - pypi: ./ name: fiboa-cli version: 0.20.3 - sha256: 0b3743a9a5f590df1aa36c60901b73456e1f2f7dbc25163ba4597134928c1132 + sha256: f2957643698e34ef54cbf2f15bac13fdc0b228e6d7c3034f57a139b8d92cdcb5 requires_dist: - vecorel-cli==0.2.11 - spdx-license-list==3.27.0 + - duckdb==1.4.2 requires_python: '>=3.10,<3.14' editable: true - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda diff --git a/pyproject.toml b/pyproject.toml index cb36db97..46e70763 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ requires-python = ">=3.10,<3.14" dependencies = [ "vecorel-cli==0.2.11", "spdx-license-list==3.27.0", + "duckdb==1.4.2", ] [project.scripts] From 996cc380cc8a535dcf585496fd9d1b7a8208c4b2 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 8 Dec 2025 23:32:51 +0100 Subject: [PATCH 04/94] Fix tests --- fiboa_cli/conversion/duckdb.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index a43b7ff9..746b1e35 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -1,5 +1,6 @@ import json import os +from pathlib import Path import duckdb import pyarrow as pa @@ -85,6 +86,9 @@ def convert( except Exception as e: self.warning(f"{column}: Skipped - {e}") + if isinstance(output_file, Path): + output_file = str(output_file) + pq_schema = pa.schema(pq_fields) schema_bytes = pq_schema.serialize().to_pybytes() # pq_schema = pq_schema.with_metadata({"collection": collection}) From 51bea87f8fbca4f41728508161a8313d912b8a25 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 9 Dec 2025 11:46:20 +0100 Subject: [PATCH 05/94] Add collection to metadata --- fiboa_cli/conversion/duckdb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 746b1e35..3f6ae211 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -69,6 +69,7 @@ def convert( _collection = self.create_collection(cid) _collection.update(self.column_additions) + _collection["collection"] = self.id collection = json.dumps(_collection, cls=VecorelJSONEncoder).encode("utf-8") schemas = _collection.merge_schemas({}) From 2d3fc50718f12715ea07a9fa9fe4d33a415644d2 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 9 Dec 2025 11:49:38 +0100 Subject: [PATCH 06/94] Add required to arrow definition --- fiboa_cli/conversion/duckdb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 3f6ae211..ef8a6468 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -49,6 +49,7 @@ def convert( selections.append(f'"{k}" as "{v}"') if v == "geometry": geom_column = k + selection = ", ".join(selections) filters = [] where = "" @@ -61,7 +62,6 @@ def convert( if len(filters) > 0: where = f"WHERE {' AND '.join(filters)}" - selection = ", ".join(selections) if isinstance(urls, str): sources = f'"{urls}"' else: @@ -74,6 +74,7 @@ def convert( schemas = _collection.merge_schemas({}) props = schemas.get("properties", {}) + required = schemas.get("required", []) pq_fields = [] for column in self.columns.values(): schema = props.get(column, {}) @@ -82,7 +83,7 @@ def convert( self.warning(f"{column}: No mapping") continue try: - field = get_pyarrow_field(column, schema=schema) + field = get_pyarrow_field(column, schema=schema, required=column in required) pq_fields.append(field) except Exception as e: self.warning(f"{column}: Skipped - {e}") From 852fb6aa102d6583f52e182d46d8fa8000286994 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 10 Dec 2025 10:12:55 +0100 Subject: [PATCH 07/94] Support for sources with different schemas --- fiboa_cli/conversion/duckdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index ef8a6468..2ee6fd17 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -101,7 +101,7 @@ def convert( con.execute( f""" COPY ( - SELECT {selection} FROM read_parquet({sources}) + SELECT {selection} FROM read_parquet({sources}, union_by_name=true) {where} ORDER BY ST_Hilbert({geom_column}) ) TO ? ( From 3cf3b8d1f3eb0030e0b0ee24f297beeb23bdc755 Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Wed, 10 Dec 2025 11:26:29 +0100 Subject: [PATCH 08/94] Export collection and set compression --- fiboa_cli/conversion/duckdb.py | 35 ++++++++-------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 2ee6fd17..1541e537 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -67,33 +67,14 @@ def convert( else: sources = "[" + ",".join([f'"{url}"' for url in urls]) + "]" - _collection = self.create_collection(cid) - _collection.update(self.column_additions) - _collection["collection"] = self.id - collection = json.dumps(_collection, cls=VecorelJSONEncoder).encode("utf-8") - - schemas = _collection.merge_schemas({}) - props = schemas.get("properties", {}) - required = schemas.get("required", []) - pq_fields = [] - for column in self.columns.values(): - schema = props.get(column, {}) - dtype = schema.get("type") - if dtype is None: - self.warning(f"{column}: No mapping") - continue - try: - field = get_pyarrow_field(column, schema=schema, required=column in required) - pq_fields.append(field) - except Exception as e: - self.warning(f"{column}: Skipped - {e}") + collection = self.create_collection(cid) + collection.update(self.column_additions) + collection["collection"] = self.id if isinstance(output_file, Path): output_file = str(output_file) - pq_schema = pa.schema(pq_fields) - schema_bytes = pq_schema.serialize().to_pybytes() - # pq_schema = pq_schema.with_metadata({"collection": collection}) + collection_json = json.dumps(collection, cls=VecorelJSONEncoder).encode("utf-8") con = duckdb.connect() con.install_extension("spatial") @@ -101,19 +82,19 @@ def convert( con.execute( f""" COPY ( - SELECT {selection} FROM read_parquet({sources}, union_by_name=true) + SELECT {selection} + FROM read_parquet({sources}, union_by_name=true) {where} ORDER BY ST_Hilbert({geom_column}) ) TO ? ( FORMAT parquet, - compression 'brotli', + compression '{compression}', KV_METADATA {{ collection: ?, - "PYARROW:schema": ? }} ) """, - [output_file, collection, schema_bytes], + [output_file, collection_json], ) return output_file From c5241424dbc49ddee77c525c2d3c1a00da5611cb Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Wed, 10 Dec 2025 11:53:45 +0100 Subject: [PATCH 09/94] Add caching and warnings --- fiboa_cli/conversion/duckdb.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 1541e537..45579d78 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -3,9 +3,7 @@ from pathlib import Path import duckdb -import pyarrow as pa from vecorel_cli.encoding.geojson import VecorelJSONEncoder -from vecorel_cli.parquet.types import get_pyarrow_field from .fiboa_converter import FiboaBaseConverter @@ -22,6 +20,11 @@ def convert( original_geometries=False, **kwargs, ) -> str: + if geoparquet_version is not None: + self.warning("geoparquet_version is not supported for DuckDB-based converters and will always write GeoParquet v1.0") + if not original_geometries: + self.warning("original_geometries is not supported for DuckDB-based converters and will always write original geometries") + self.variant = variant cid = self.id.strip() if self.bbox is not None and len(self.bbox) != 4: @@ -40,6 +43,15 @@ def convert( if urls is None: raise ValueError("No input files provided") + self.info("Getting file(s) if not cached yet") + if cache: + request_args = {} + if self.avoid_range_request: + request_args["block_size"] = 0 + urls = self.download_files(urls, cache, **request_args) + elif self.avoid_range_request: + self.warning("avoid_range_request is set, but cache is not used, so this setting has no effect") + selections = [] geom_column = None for k, v in self.columns.items(): From 0d5a9f91adadb7167ccff00a155f25e77792b547 Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Wed, 10 Dec 2025 12:10:16 +0100 Subject: [PATCH 10/94] Add todos --- fiboa_cli/conversion/duckdb.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 45579d78..32f13179 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -109,4 +109,10 @@ def convert( [output_file, collection_json], ) + # todo: write the file again to do the following: + # - update geoparquet version to 1.1 + # - add bounding box + metadata + # - add the non-nullability to the respective columns + # Ideally do this in improve... + return output_file From 07d7aac02e3067261f31d41b039acd005fae3321 Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Wed, 10 Dec 2025 12:33:12 +0100 Subject: [PATCH 11/94] Fix tests --- fiboa_cli/conversion/duckdb.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 32f13179..20e50352 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -77,7 +77,13 @@ def convert( if isinstance(urls, str): sources = f'"{urls}"' else: - sources = "[" + ",".join([f'"{url}"' for url in urls]) + "]" + paths = [] + for url in urls: + if isinstance(url, tuple): + paths.append(f'"{url[0]}"') + else: + paths.append(f'"{url}"') + sources = "[" + ",".join(paths) + "]" collection = self.create_collection(cid) collection.update(self.column_additions) @@ -100,13 +106,13 @@ def convert( ORDER BY ST_Hilbert({geom_column}) ) TO ? ( FORMAT parquet, - compression '{compression}', + compression ?, KV_METADATA {{ collection: ?, }} ) """, - [output_file, collection_json], + [output_file, compression or 'brotli', collection_json], ) # todo: write the file again to do the following: From 78fb00d7eb85765953a0da58e779e7b401a4b95f Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 10 Dec 2025 21:31:04 +0100 Subject: [PATCH 12/94] Implement rewrite to correct GeoParquet --- fiboa_cli/conversion/duckdb.py | 114 ++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 10 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 20e50352..6da3ac12 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -1,8 +1,14 @@ import json import os from pathlib import Path +from tempfile import NamedTemporaryFile import duckdb +import numpy as np +import pyarrow as pa +import pyarrow.parquet as pq +from geopandas.array import from_wkb +from pyarrow.lib import StructArray from vecorel_cli.encoding.geojson import VecorelJSONEncoder from .fiboa_converter import FiboaBaseConverter @@ -20,10 +26,13 @@ def convert( original_geometries=False, **kwargs, ) -> str: - if geoparquet_version is not None: - self.warning("geoparquet_version is not supported for DuckDB-based converters and will always write GeoParquet v1.0") if not original_geometries: - self.warning("original_geometries is not supported for DuckDB-based converters and will always write original geometries") + self.warning( + "original_geometries is not supported for DuckDB-based converters and will always write original geometries" + ) + + geoparquet_version = geoparquet_version or "1.1.0" + compression = compression or "brotli" self.variant = variant cid = self.id.strip() @@ -50,7 +59,9 @@ def convert( request_args["block_size"] = 0 urls = self.download_files(urls, cache, **request_args) elif self.avoid_range_request: - self.warning("avoid_range_request is set, but cache is not used, so this setting has no effect") + self.warning( + "avoid_range_request is set, but cache is not used, so this setting has no effect" + ) selections = [] geom_column = None @@ -112,13 +123,96 @@ def convert( }} ) """, - [output_file, compression or 'brotli', collection_json], + [output_file, compression, collection_json], ) - # todo: write the file again to do the following: - # - update geoparquet version to 1.1 - # - add bounding box + metadata - # - add the non-nullability to the respective columns - # Ideally do this in improve... + # Post-process the written Parquet to proper GeoParquet v1.1 with bbox and nullability + try: + pq_file = pq.ParquetFile(output_file) + + existing_schema = pq_file.schema_arrow + col_names = existing_schema.names + assert "geometry" in col_names, "Missing geometry column in output parquet file" + + schemas = collection.merge_schemas({}) + collection_only = {k for k, v in schemas.get("collection", {}).items() if v} + required_columns = {"geometry"} | { + r + for r in schemas.get("required", []) + if r in col_names and r not in collection_only + } + if "id" in col_names: + required_columns.add("id") + + # Update for version 1.1.0 + metadata = existing_schema.metadata + if geoparquet_version > "1.0.0": + geo_meta = json.loads(existing_schema.metadata[b"geo"]) + geo_meta["version"] = geoparquet_version + metadata[b"geo"] = json.dumps(geo_meta).encode("utf-8") + + # Build a new Arrow schema with adjusted nullability + new_fields = [] + for field in existing_schema: + if field.name in required_columns and field.nullable: + new_fields.append( + pa.field(field.name, field.type, nullable=False, metadata=field.metadata) + ) + else: + new_fields.append(field) + + add_bbox = geoparquet_version > "1.0.0" and "bbox" not in col_names + if add_bbox: + new_fields.append( + pa.field( + "bbox", + pa.struct( + [ + ("xmin", pa.float64()), + ("ymin", pa.float64()), + ("xmax", pa.float64()), + ("ymax", pa.float64()), + ] + ), + ) + ) + new_schema = pa.schema(new_fields, metadata=metadata) + + # 7) Streamingly rewrite the file to a temp file and replace atomically + with NamedTemporaryFile( + "wb", delete=False, dir=os.path.dirname(output_file), suffix=".parquet" + ) as tmp: + tmp_path = tmp.name + + writer = pq.ParquetWriter( + tmp_path, + new_schema, + compression=compression, + use_dictionary=True, + write_statistics=True, + ) + try: + bbox_names = ["ymax", "xmax", "ymin", "xmin"] + for rg in range(pq_file.num_row_groups): + tbl = pq_file.read_row_group(rg) + if add_bbox: + # determine bounds, change to StructArray type + bounds = from_wkb(tbl["geometry"]).bounds + bbox_array = StructArray.from_arrays( + np.rot90(bounds), + names=bbox_names, + ) + tbl = tbl.append_column("bbox", bbox_array) + # Ensure table adheres to the new schema (mainly nullability); cast if needed + if tbl.schema != new_schema: + # Align field order/types; this does not materialize data beyond the batch + tbl = tbl.cast(new_schema, safe=False) + writer.write_table(tbl) + finally: + writer.close() + + os.replace(tmp_path, output_file) + except Exception as e: + self.warning(f"GeoParquet 1.1 post-processing failed: {e}") return output_file From 6017ef0756ff6b45c72b7a6684c3045fa3767122 Mon Sep 17 00:00:00 2001 From: Ivor Date: Fri, 13 Feb 2026 14:39:40 +0100 Subject: [PATCH 13/94] Update fiboa_cli/conversion/duckdb.py Co-authored-by: Matthias Mohr --- fiboa_cli/conversion/duckdb.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 6da3ac12..9b4f9fc1 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -14,6 +14,9 @@ from .fiboa_converter import FiboaBaseConverter +# This converter is experimental, use with caution. +# Results may not be fully fiboa compliant yet. +# Use this primarily for datasets that are too large to be processed by the default converter class FiboaDuckDBBaseConverter(FiboaBaseConverter): def convert( self, From 920b39d629ea35dcf6c80fd65a1c4097977aa27e Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 16 Feb 2026 14:03:25 +0100 Subject: [PATCH 14/94] Update pixi.lock for duckdb --- pixi.lock | 183 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 103 insertions(+), 80 deletions(-) diff --git a/pixi.lock b/pixi.lock index 8bdfcf93..11afdf14 100644 --- a/pixi.lock +++ b/pixi.lock @@ -32,6 +32,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.5-py314h7fe84b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -110,6 +111,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda @@ -201,6 +203,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-46.0.5-py314h6a45124_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -271,6 +274,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda @@ -362,6 +366,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-46.0.5-py314h2cafa77_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -433,6 +438,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda @@ -524,6 +530,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/cryptography-46.0.5-py314he884d78_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -588,6 +595,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda @@ -688,6 +696,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.5-py314h7fe84b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -755,6 +764,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda @@ -832,6 +842,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-46.0.5-py314h6a45124_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -891,6 +902,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda @@ -968,6 +980,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-46.0.5-py314h2cafa77_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -1028,6 +1041,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda @@ -1106,6 +1120,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cryptography-46.0.5-py314he884d78_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -1159,6 +1174,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda @@ -1242,6 +1258,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1284,6 +1301,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda @@ -1348,6 +1366,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1383,6 +1402,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda @@ -1447,6 +1467,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1483,6 +1504,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda @@ -1547,6 +1569,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyha7b4d00_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1576,6 +1599,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda @@ -1657,6 +1681,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.4-py314h67df5f8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -1711,6 +1736,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda @@ -1790,6 +1816,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.4-py314h10d0514_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -1837,6 +1864,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda @@ -1916,6 +1944,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.4-py314h6e9b3f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -1964,6 +1993,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda @@ -2042,6 +2072,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.13.4-py314h2359020_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -2083,6 +2114,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda @@ -2789,6 +2821,16 @@ packages: - pkg:pypi/distlib?source=hash-mapping size: 275642 timestamp: 1752823081585 +- conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda + sha256: 2cc2cdf4a5a0ac975ed2f42b9a9f9cc5ee62213eb7eee11aabdde04127239d7c + md5: 4fff74996eacd532d60e2c544660a507 + depends: + - python-duckdb >=1.4.2,<1.4.3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 7530 + timestamp: 1763377721341 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 md5: 8e662bd460bda79b1ea39194e3c4c9ab @@ -2803,10 +2845,11 @@ packages: - pypi: ./ name: fiboa-cli version: 0.21.0 - sha256: 5a8a4c3d9234870ac9a54c0cc925c41f59ca033ee111dd241c1af9633591f47b + sha256: da5f2208753c0eb13ecc6e90d3d968fc4080f074550edbb6cb7bc88ab5c30d72 requires_dist: - vecorel-cli==0.2.15 - spdx-license-list==3.27.0 + - duckdb==1.4.2 requires_python: '>=3.11' editable: true - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda @@ -6297,6 +6340,65 @@ packages: - pkg:pypi/python-dateutil?source=hash-mapping size: 233310 timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda + sha256: e5d655846320be8b2cdb52302727b86748dbe25b28460640be2c5a234a9ab506 + md5: b1463a8e885b875d931dba7dc76cb250 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + purls: + - pkg:pypi/duckdb?source=hash-mapping + size: 16377782 + timestamp: 1763377103187 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda + sha256: a0ee1056d10fb8d271b6ffc07030534b86ac5b7788d2907a9caf55a6e9565f08 + md5: ef180499a3ddff68c2b50f79733c9cf8 + depends: + - __osx >=10.13 + - libcxx >=19 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + purls: + - pkg:pypi/duckdb?source=hash-mapping + size: 12398574 + timestamp: 1763377914269 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda + sha256: 71f7644bd393f2508f077b1dd68b7354329457c8ea903354d4f2341f105ef4ac + md5: 7fd8e05bc4116c3cf05513d844d496d3 + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + purls: + - pkg:pypi/duckdb?source=hash-mapping + size: 10777580 + timestamp: 1763377988015 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda + sha256: 7900099f892b9d80d4c4cce60a85dd410ac38ec73f47005250982d84c9e7eea7 + md5: 3f300010eff0b5880995e98ec5753930 + depends: + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: + - pkg:pypi/duckdb?source=hash-mapping + size: 9621168 + timestamp: 1763379001464 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda build_number: 8 sha256: ad6d2e9ac39751cc0529dd1566a26751a0bf2542adb0c232533d32e176e21db5 @@ -7310,82 +7412,3 @@ packages: purls: [] size: 388453 timestamp: 1764777142545 - - conda: https://conda.anaconda.org/conda-forge/noarch/google-cloud-storage-3.9.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py314hd6bf2bd_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/google-resumable-media-2.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.0-py314h5885658_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.16-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jmespath-1.1.0-pyhcf101f3_1.conda - - async-timeout >=4.0,<6.0 - track_features: - - aiohttp_no_compile - - python - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/async-timeout?source=hash-mapping - size: 13559 - timestamp: 1767290444597 -- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyhcf101f3_1.conda - sha256: c13d5e42d187b1d0255f591b7ce91201d4ed8a5370f0d986707a802c20c9d32f - md5: 537296d57ea995666c68c821b00e360b - depends: - - python >=3.10 - - python - - python >=3.10 - - colorama - - __win - - python - - duckdb==1.4.2 - - libabseil * cxx17* - - libabseil >=20260107.0,<20260108.0a0 - - libabseil * cxx17* - - libabseil >=20260107.0,<20260108.0a0 - - libabseil * cxx17* - - libabseil >=20260107.0,<20260108.0a0 - - zstd >=1.5.7,<1.6.0a0 - license_family: GPL - license_family: GPL - license_family: GPL - - icu >=78.2,<79.0a0 - license_family: GPL - license_family: GPL - license_family: MIT - - python - - sqlite - - libtiff - - libcurl - - sqlite - - libtiff - - libcurl - - ucrt >=10.0.20348.0 - - libsqlite >=3.51.2,<4.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libcurl >=8.18.0,<9.0a0 - - psleak ; extra == 'dev' - - validate-pyproject[all] ; extra == 'dev' - - psleak ; extra == 'dev' - - validate-pyproject[all] ; extra == 'dev' - - psleak ; extra == 'dev' - - validate-pyproject[all] ; extra == 'dev' - - psleak ; extra == 'dev' - - validate-pyproject[all] ; extra == 'dev' - - colorama >=0.4 - - exceptiongroup >=1 - - python - - zstd >=1.5.7,<1.6.0a0 - - zstd >=1.5.7,<1.6.0a0 - - zstd >=1.5.7,<1.6.0a0 - - zstd >=1.5.7,<1.6.0a0 - - __osx >=10.13 - - __osx >=11.0 - - python >=3.10 - - python - - icu >=78.2,<79.0a0 - constrains: - - xorg-libx11 >=1.8.12,<2.0a0 - license_family: MIT From 5aad9cb7fafdb8434ba4077d212f1d56230d1f32 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 15 May 2026 11:35:39 +0200 Subject: [PATCH 15/94] Update pixi --- pixi.lock | 167 +++++++++++++++++++++--------------------------------- 1 file changed, 65 insertions(+), 102 deletions(-) diff --git a/pixi.lock b/pixi.lock index 11afdf14..fee018c9 100644 --- a/pixi.lock +++ b/pixi.lock @@ -32,7 +32,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.5-py314h7fe84b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -111,7 +110,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda @@ -143,6 +141,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/23/35/b1fae4c5245697837f6f63e407fa81e7ccc7948f6ef2b124cd38736f4d1d/duckdb-1.4.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/72/0b6035302e9c33f004240a50cb6e2e1fc7bb1f2b415b02d939c551bdd06b/inflate64-1.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -203,7 +202,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-46.0.5-py314h6a45124_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -274,7 +272,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda @@ -306,6 +303,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/ab/e04a8f97865251b544aee9501088d4f0cb8e8b37339bd465c0d33857d411/duckdb-1.4.2-cp314-cp314-macosx_10_15_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/80/24ba0d2ee14e07e275e9c5b058e59a8a58f8ef42dd51a78ebbfd7c857ac4/inflate64-1.0.4-cp314-cp314-macosx_10_15_x86_64.whl @@ -366,7 +364,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-46.0.5-py314h2cafa77_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -438,7 +435,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda @@ -470,6 +466,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/47/ec/b8229517c2f9fe88a38bb1a172a2da4d0ff34996d319d74554fda80b6358/duckdb-1.4.2-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/b8/073a79716e093db973b8823bdfb02e10fbdf65642dbe1fa3cda24832aeb2/inflate64-1.0.4-cp314-cp314-macosx_11_0_arm64.whl @@ -530,7 +527,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/cryptography-46.0.5-py314he884d78_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -595,7 +591,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda @@ -632,6 +627,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/5e/6f5ebaabc12c6db62f471f86b5c9c8debd57f11aa1b2acbbcc4c68683238/duckdb-1.4.2-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dd/51/f2972df8cceecc9bf3afa3353d517ffc7125285198c844588e9aaf98f5d0/inflate64-1.0.4-cp314-cp314-win_amd64.whl @@ -696,7 +692,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.5-py314h7fe84b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -764,7 +759,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda @@ -787,6 +781,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/23/35/b1fae4c5245697837f6f63e407fa81e7ccc7948f6ef2b124cd38736f4d1d/duckdb-1.4.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/72/0b6035302e9c33f004240a50cb6e2e1fc7bb1f2b415b02d939c551bdd06b/inflate64-1.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -842,7 +837,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-46.0.5-py314h6a45124_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -902,7 +896,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda @@ -925,6 +918,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/ab/e04a8f97865251b544aee9501088d4f0cb8e8b37339bd465c0d33857d411/duckdb-1.4.2-cp314-cp314-macosx_10_15_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/80/24ba0d2ee14e07e275e9c5b058e59a8a58f8ef42dd51a78ebbfd7c857ac4/inflate64-1.0.4-cp314-cp314-macosx_10_15_x86_64.whl @@ -980,7 +974,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-46.0.5-py314h2cafa77_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -1041,7 +1034,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda @@ -1064,6 +1056,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/47/ec/b8229517c2f9fe88a38bb1a172a2da4d0ff34996d319d74554fda80b6358/duckdb-1.4.2-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/b8/073a79716e093db973b8823bdfb02e10fbdf65642dbe1fa3cda24832aeb2/inflate64-1.0.4-cp314-cp314-macosx_11_0_arm64.whl @@ -1120,7 +1113,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cryptography-46.0.5-py314he884d78_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gcsfs-2025.7.0-pyhd8ed1ab_0.conda @@ -1174,7 +1166,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda @@ -1202,6 +1193,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/5e/6f5ebaabc12c6db62f471f86b5c9c8debd57f11aa1b2acbbcc4c68683238/duckdb-1.4.2-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dd/51/f2972df8cceecc9bf3afa3353d517ffc7125285198c844588e9aaf98f5d0/inflate64-1.0.4-cp314-cp314-win_amd64.whl @@ -1258,7 +1250,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1301,7 +1292,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda @@ -1318,6 +1308,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/23/35/b1fae4c5245697837f6f63e407fa81e7ccc7948f6ef2b124cd38736f4d1d/duckdb-1.4.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/72/0b6035302e9c33f004240a50cb6e2e1fc7bb1f2b415b02d939c551bdd06b/inflate64-1.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -1366,7 +1357,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1402,7 +1392,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda @@ -1419,6 +1408,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/ab/e04a8f97865251b544aee9501088d4f0cb8e8b37339bd465c0d33857d411/duckdb-1.4.2-cp314-cp314-macosx_10_15_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/80/24ba0d2ee14e07e275e9c5b058e59a8a58f8ef42dd51a78ebbfd7c857ac4/inflate64-1.0.4-cp314-cp314-macosx_10_15_x86_64.whl @@ -1467,7 +1457,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1504,7 +1493,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda @@ -1521,6 +1509,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/47/ec/b8229517c2f9fe88a38bb1a172a2da4d0ff34996d319d74554fda80b6358/duckdb-1.4.2-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/b8/073a79716e093db973b8823bdfb02e10fbdf65642dbe1fa3cda24832aeb2/inflate64-1.0.4-cp314-cp314-macosx_11_0_arm64.whl @@ -1569,7 +1558,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyha7b4d00_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -1599,7 +1587,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda @@ -1620,6 +1607,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/5e/6f5ebaabc12c6db62f471f86b5c9c8debd57f11aa1b2acbbcc4c68683238/duckdb-1.4.2-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dd/51/f2972df8cceecc9bf3afa3353d517ffc7125285198c844588e9aaf98f5d0/inflate64-1.0.4-cp314-cp314-win_amd64.whl @@ -1681,7 +1669,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.4-py314h67df5f8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -1736,7 +1723,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda @@ -1762,6 +1748,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/23/35/b1fae4c5245697837f6f63e407fa81e7ccc7948f6ef2b124cd38736f4d1d/duckdb-1.4.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/72/0b6035302e9c33f004240a50cb6e2e1fc7bb1f2b415b02d939c551bdd06b/inflate64-1.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -1816,7 +1803,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.4-py314h10d0514_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -1864,7 +1850,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py314h10d0514_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda @@ -1890,6 +1875,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/ab/e04a8f97865251b544aee9501088d4f0cb8e8b37339bd465c0d33857d411/duckdb-1.4.2-cp314-cp314-macosx_10_15_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/80/24ba0d2ee14e07e275e9c5b058e59a8a58f8ef42dd51a78ebbfd7c857ac4/inflate64-1.0.4-cp314-cp314-macosx_10_15_x86_64.whl @@ -1944,7 +1930,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.4-py314h6e9b3f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -1993,7 +1978,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda @@ -2019,6 +2003,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/47/ec/b8229517c2f9fe88a38bb1a172a2da4d0ff34996d319d74554fda80b6358/duckdb-1.4.2-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/b8/073a79716e093db973b8823bdfb02e10fbdf65642dbe1fa3cda24832aeb2/inflate64-1.0.4-cp314-cp314-macosx_11_0_arm64.whl @@ -2072,7 +2057,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.13.4-py314h2359020_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda @@ -2114,7 +2098,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda @@ -2144,6 +2127,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/5e/6f5ebaabc12c6db62f471f86b5c9c8debd57f11aa1b2acbbcc4c68683238/duckdb-1.4.2-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dd/51/f2972df8cceecc9bf3afa3353d517ffc7125285198c844588e9aaf98f5d0/inflate64-1.0.4-cp314-cp314-win_amd64.whl @@ -2821,16 +2805,54 @@ packages: - pkg:pypi/distlib?source=hash-mapping size: 275642 timestamp: 1752823081585 -- conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.4.2-h332efcf_0.conda - sha256: 2cc2cdf4a5a0ac975ed2f42b9a9f9cc5ee62213eb7eee11aabdde04127239d7c - md5: 4fff74996eacd532d60e2c544660a507 - depends: - - python-duckdb >=1.4.2,<1.4.3.0a0 - license: MIT - license_family: MIT - purls: [] - size: 7530 - timestamp: 1763377721341 +- pypi: https://files.pythonhosted.org/packages/23/35/b1fae4c5245697837f6f63e407fa81e7ccc7948f6ef2b124cd38736f4d1d/duckdb-1.4.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + name: duckdb + version: 1.4.2 + sha256: 128c97dab574a438d7c8d020670b21c68792267d88e65a7773667b556541fa9b + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/25/5e/6f5ebaabc12c6db62f471f86b5c9c8debd57f11aa1b2acbbcc4c68683238/duckdb-1.4.2-cp314-cp314-win_amd64.whl + name: duckdb + version: 1.4.2 + sha256: dfcc56a83420c0dec0b83e97a6b33addac1b7554b8828894f9d203955591218c + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/47/ec/b8229517c2f9fe88a38bb1a172a2da4d0ff34996d319d74554fda80b6358/duckdb-1.4.2-cp314-cp314-macosx_11_0_arm64.whl + name: duckdb + version: 1.4.2 + sha256: 20c45b4ead1ea4d23a1be1cd4f1dfc635e58b55f0dd11e38781369be6c549903 + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/82/ab/e04a8f97865251b544aee9501088d4f0cb8e8b37339bd465c0d33857d411/duckdb-1.4.2-cp314-cp314-macosx_10_15_x86_64.whl + name: duckdb + version: 1.4.2 + sha256: 459b1855bd06a226a2838da4f14c8863fd87a62e63d414a7f7f682a7c616511a + requires_dist: + - ipython ; extra == 'all' + - fsspec ; extra == 'all' + - numpy ; extra == 'all' + - pandas ; extra == 'all' + - pyarrow ; extra == 'all' + - adbc-driver-manager ; extra == 'all' + requires_python: '>=3.9.0' - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 md5: 8e662bd460bda79b1ea39194e3c4c9ab @@ -2845,7 +2867,7 @@ packages: - pypi: ./ name: fiboa-cli version: 0.21.0 - sha256: da5f2208753c0eb13ecc6e90d3d968fc4080f074550edbb6cb7bc88ab5c30d72 + sha256: 544a959421501ea1dcf28cede8c6b5c41b70ce5ddeb55f0c4ed1a54fc32c2d64 requires_dist: - vecorel-cli==0.2.15 - spdx-license-list==3.27.0 @@ -6340,65 +6362,6 @@ packages: - pkg:pypi/python-dateutil?source=hash-mapping size: 233310 timestamp: 1751104122689 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.4.2-py314ha160325_0.conda - sha256: e5d655846320be8b2cdb52302727b86748dbe25b28460640be2c5a234a9ab506 - md5: b1463a8e885b875d931dba7dc76cb250 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/duckdb?source=hash-mapping - size: 16377782 - timestamp: 1763377103187 -- conda: https://conda.anaconda.org/conda-forge/osx-64/python-duckdb-1.4.2-py314h21b9a27_0.conda - sha256: a0ee1056d10fb8d271b6ffc07030534b86ac5b7788d2907a9caf55a6e9565f08 - md5: ef180499a3ddff68c2b50f79733c9cf8 - depends: - - __osx >=10.13 - - libcxx >=19 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/duckdb?source=hash-mapping - size: 12398574 - timestamp: 1763377914269 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.4.2-py314h93ecee7_0.conda - sha256: 71f7644bd393f2508f077b1dd68b7354329457c8ea903354d4f2341f105ef4ac - md5: 7fd8e05bc4116c3cf05513d844d496d3 - depends: - - __osx >=11.0 - - libcxx >=19 - - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/duckdb?source=hash-mapping - size: 10777580 - timestamp: 1763377988015 -- conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.4.2-py314h13fbf68_0.conda - sha256: 7900099f892b9d80d4c4cce60a85dd410ac38ec73f47005250982d84c9e7eea7 - md5: 3f300010eff0b5880995e98ec5753930 - depends: - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: - - pkg:pypi/duckdb?source=hash-mapping - size: 9621168 - timestamp: 1763379001464 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda build_number: 8 sha256: ad6d2e9ac39751cc0529dd1566a26751a0bf2542adb0c232533d32e176e21db5 From 88020f807d6bf81e9461406609049cd3bd04a507 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 15 May 2026 21:48:43 +0200 Subject: [PATCH 16/94] Rename es to es_base, so we can introduce a generic Spain-wide converter --- fiboa_cli/datasets/es_an.py | 2 +- fiboa_cli/datasets/es_ar.py | 2 +- fiboa_cli/datasets/es_base.py | 50 +++++++++++++++++++++++++++++++++++ fiboa_cli/datasets/es_cb.py | 2 +- fiboa_cli/datasets/es_cl.py | 2 +- fiboa_cli/datasets/es_cm.py | 2 +- fiboa_cli/datasets/es_ex.py | 2 +- fiboa_cli/datasets/es_ga.py | 2 +- fiboa_cli/datasets/es_ib.py | 2 +- fiboa_cli/datasets/es_md.py | 2 +- fiboa_cli/datasets/es_nc.py | 2 +- fiboa_cli/datasets/es_pv.py | 2 +- fiboa_cli/datasets/es_vc.py | 2 +- fiboa_cli/registry.py | 2 +- 14 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 fiboa_cli/datasets/es_base.py diff --git a/fiboa_cli/datasets/es_an.py b/fiboa_cli/datasets/es_an.py index 44abd2fe..b484f3a0 100644 --- a/fiboa_cli/datasets/es_an.py +++ b/fiboa_cli/datasets/es_an.py @@ -1,7 +1,7 @@ from loguru import logger from .commons.data import read_data_csv -from .es import ESBaseConverter +from .es_base import ESBaseConverter class ANConverter(ESBaseConverter): diff --git a/fiboa_cli/datasets/es_ar.py b/fiboa_cli/datasets/es_ar.py index ac8d1f60..9e5b059b 100644 --- a/fiboa_cli/datasets/es_ar.py +++ b/fiboa_cli/datasets/es_ar.py @@ -1,6 +1,6 @@ import pandas as pd -from .es import ESBaseConverter +from .es_base import ESBaseConverter class ARConverter(ESBaseConverter): diff --git a/fiboa_cli/datasets/es_base.py b/fiboa_cli/datasets/es_base.py new file mode 100644 index 00000000..4d2fd9d4 --- /dev/null +++ b/fiboa_cli/datasets/es_base.py @@ -0,0 +1,50 @@ +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter +from fiboa_cli.datasets.commons.data import read_data_csv + + +class ESBaseConverter(FiboaBaseConverter): + """ + Base Converter for Spain + Asssumes a source column with the SIGPAC-Land Use code + The Land Use code is filtered for agricultural use and transformed into a high-level crop type + + "Cultivo Declarado" is what we would prefer, but the "Recinto" is the best to be found so far + + For Spanish Sources, see https://www.cartodruid.es/en/-/descargar-sigpac-comunidad-autonoma + There seems to be a National Layer; https://inspire-geoportal.ec.europa.eu/srv/api/records/87ce5171-d713-4eec-a1f3-2b9dd94cad91 + """ + + use_code_attribute = "uso_sigpac" + + extensions = { + "https://fiboa.org/crop-extension/v0.2.0/schema.yaml", + ADMIN_DIVISION, + } + column_additions = { + # https://www.euskadi.eus/contenidos/informacion/pac2015_pagosdirectos/es_def/adjuntos/Anexos_PAC_marzo2015.pdf + # https://www.fega.gob.es/sites/default/files/files/document/AD-CIRCULAR_2-2021_EE98293_SIGC2021.PDF + # Very generic list + "admin:country_code": "ES", + "crop:code_list": "https://fiboa.org/code/es/sigpac/land_use.csv", + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.id.startswith("es_"), "Assuming Spanish subclass" + + def code_filter(col): + return ~col.isin("AG/CA/ED/FO/IM/IS/IV/TH/ZC/ZU/ZV/MT".split("/") + [None]) + + self.column_filters = {self.use_code_attribute: code_filter} + self.column_additions["admin:subdivision_code"] = self.id[len("es_") :].upper() + + def migrate(self, gdf): + # This actually is a land use code. Not sure if we should put this in crop:code + rows = read_data_csv("es_coda_uso.csv") + mapping = {row["original_code"]: row["original_name"] for row in rows} + mapping_en = {row["original_code"]: row["name_en"] for row in rows} + gdf["crop:name"] = gdf[self.use_code_attribute].map(mapping) + gdf["crop:name_en"] = gdf[self.use_code_attribute].map(mapping_en) + return super().migrate(gdf) diff --git a/fiboa_cli/datasets/es_cb.py b/fiboa_cli/datasets/es_cb.py index 9d63ee5e..a2ca7096 100644 --- a/fiboa_cli/datasets/es_cb.py +++ b/fiboa_cli/datasets/es_cb.py @@ -1,7 +1,7 @@ import re from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin -from fiboa_cli.datasets.es import ESBaseConverter +from fiboa_cli.datasets.es_base import ESBaseConverter class ESCBConverter(EsriRESTConverterMixin, ESBaseConverter): diff --git a/fiboa_cli/datasets/es_cl.py b/fiboa_cli/datasets/es_cl.py index 573f0ceb..1d2cf4d9 100644 --- a/fiboa_cli/datasets/es_cl.py +++ b/fiboa_cli/datasets/es_cl.py @@ -4,7 +4,7 @@ import requests from loguru import logger -from .es import ESBaseConverter +from .es_base import ESBaseConverter regex = re.compile(r"\d+_(RECFE|BURGOS).*\.shp$") diff --git a/fiboa_cli/datasets/es_cm.py b/fiboa_cli/datasets/es_cm.py index c6e6f6ce..789ce436 100644 --- a/fiboa_cli/datasets/es_cm.py +++ b/fiboa_cli/datasets/es_cm.py @@ -3,7 +3,7 @@ import requests from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin -from fiboa_cli.datasets.es import ESBaseConverter +from fiboa_cli.datasets.es_base import ESBaseConverter class ESCMConverter(EsriRESTConverterMixin, ESBaseConverter): diff --git a/fiboa_cli/datasets/es_ex.py b/fiboa_cli/datasets/es_ex.py index 4ca7fe1f..867c08e3 100644 --- a/fiboa_cli/datasets/es_ex.py +++ b/fiboa_cli/datasets/es_ex.py @@ -3,7 +3,7 @@ import requests -from fiboa_cli.datasets.es import ESBaseConverter +from fiboa_cli.datasets.es_base import ESBaseConverter class EXConverter(ESBaseConverter): diff --git a/fiboa_cli/datasets/es_ga.py b/fiboa_cli/datasets/es_ga.py index 0e94d161..0e2889a6 100644 --- a/fiboa_cli/datasets/es_ga.py +++ b/fiboa_cli/datasets/es_ga.py @@ -1,5 +1,5 @@ from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin -from fiboa_cli.datasets.es import ESBaseConverter +from fiboa_cli.datasets.es_base import ESBaseConverter class ESGAConverter(EsriRESTConverterMixin, ESBaseConverter): diff --git a/fiboa_cli/datasets/es_ib.py b/fiboa_cli/datasets/es_ib.py index 2c2f9d7e..a7ff02cf 100644 --- a/fiboa_cli/datasets/es_ib.py +++ b/fiboa_cli/datasets/es_ib.py @@ -3,7 +3,7 @@ import pandas as pd from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin -from fiboa_cli.datasets.es import ESBaseConverter +from fiboa_cli.datasets.es_base import ESBaseConverter class ESIBConverter(EsriRESTConverterMixin, ESBaseConverter): diff --git a/fiboa_cli/datasets/es_md.py b/fiboa_cli/datasets/es_md.py index a69fd6f2..9f4f748d 100644 --- a/fiboa_cli/datasets/es_md.py +++ b/fiboa_cli/datasets/es_md.py @@ -1,4 +1,4 @@ -from .es import ESBaseConverter +from .es_base import ESBaseConverter class ESCLConverter(ESBaseConverter): diff --git a/fiboa_cli/datasets/es_nc.py b/fiboa_cli/datasets/es_nc.py index a06d3dd0..b84cf58f 100644 --- a/fiboa_cli/datasets/es_nc.py +++ b/fiboa_cli/datasets/es_nc.py @@ -6,7 +6,7 @@ from loguru import logger from vecorel_cli.vecorel.util import name_from_uri -from .es import ESBaseConverter +from .es_base import ESBaseConverter class NCConverter(ESBaseConverter): diff --git a/fiboa_cli/datasets/es_pv.py b/fiboa_cli/datasets/es_pv.py index e4fbe3b5..1ff293bd 100644 --- a/fiboa_cli/datasets/es_pv.py +++ b/fiboa_cli/datasets/es_pv.py @@ -2,7 +2,7 @@ import requests from loguru import logger -from .es import ESBaseConverter +from .es_base import ESBaseConverter class ESPVConverter(ESBaseConverter): diff --git a/fiboa_cli/datasets/es_vc.py b/fiboa_cli/datasets/es_vc.py index a03b9dae..9289babd 100644 --- a/fiboa_cli/datasets/es_vc.py +++ b/fiboa_cli/datasets/es_vc.py @@ -3,7 +3,7 @@ import requests -from .es import ESBaseConverter +from .es_base import ESBaseConverter class ESVCConverter(ESBaseConverter): diff --git a/fiboa_cli/registry.py b/fiboa_cli/registry.py index 56e0f04d..76562bc8 100644 --- a/fiboa_cli/registry.py +++ b/fiboa_cli/registry.py @@ -22,7 +22,7 @@ class FiboaRegistry(VecorelRegistry): "determination:details", ] required_extensions = [re.compile(spec_pattern)] - ignored_datasets = VecorelRegistry.ignored_datasets + ["es.py"] + ignored_datasets = VecorelRegistry.ignored_datasets + ["es_base.py"] def register_commands(self): from .convert import ConvertData From 42db7be45f6d18d2a26fcaf508f0ffc05de40e9c Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 15 May 2026 21:54:01 +0200 Subject: [PATCH 17/94] Converter for Spain (whole), based on the FEGA 2025+ data --- CHANGELOG.md | 2 + fiboa_cli/datasets/es.py | 125 ++++++++++++++---- .../es/1501_ALAVA_cd_2025_20250105.gpkg.zip | Bin 0 -> 30369 bytes tests/test_convert.py | 2 + 4 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 tests/data-files/convert/es/1501_ALAVA_cd_2025_20250105.gpkg.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 98daeccf..b0bbc5d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +- Converter for Spain (whole), based on the FEGA 2025+ data + ## [v0.21.0] - 2026-02-16 - Update vecorel-cli diff --git a/fiboa_cli/datasets/es.py b/fiboa_cli/datasets/es.py index 4d2fd9d4..2352d444 100644 --- a/fiboa_cli/datasets/es.py +++ b/fiboa_cli/datasets/es.py @@ -1,50 +1,121 @@ +import re + +import requests from vecorel_cli.vecorel.extensions import ADMIN_DIVISION -from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter -from fiboa_cli.datasets.commons.data import read_data_csv +from ..conversion.fiboa_converter import FiboaBaseConverter + +class Converter(FiboaBaseConverter): + id = "es" + short_name = "Spain" + title = "Spain Declared Crops (Cultivos Declarados SIGPAC)" + description = """ +National declared-crop dataset (Cultivos Declarados SIGPAC) published by the Spanish Agricultural Guarantee Fund +(FEGA) via the unified SIGPAC Hub Cloud portal (sigpac-hubcloud.es). Each record is a declaration line within a +farmer's Single Application (Solicitud Única) for Common Agricultural Policy (CAP) direct payments, mapped onto +SIGPAC cadastral divisions. Data is distributed as one GeoPackage per Spanish province, harmonised across the +country since the 2025 campaign year. -class ESBaseConverter(FiboaBaseConverter): +This is a high-value dataset (HVD) under EU Implementing Regulation 2023/138. """ - Base Converter for Spain - Asssumes a source column with the SIGPAC-Land Use code - The Land Use code is filtered for agricultural use and transformed into a high-level crop type + provider = "Fondo Español de Garantía Agraria (FEGA) " + attribution = "©FEGA / Ministerio de Agricultura, Pesca y Alimentación" + license = "CC-BY-4.0" - "Cultivo Declarado" is what we would prefer, but the "Recinto" is the best to be found so far + variants = {"2025": "2025"} - For Spanish Sources, see https://www.cartodruid.es/en/-/descargar-sigpac-comunidad-autonoma - There seems to be a National Layer; https://inspire-geoportal.ec.europa.eu/srv/api/records/87ce5171-d713-4eec-a1f3-2b9dd94cad91 - """ + columns = { + "geometry": "geometry", + "id": "id", + "provincia": "admin_province_code", + "municipio": "admin_municipality_code", + "dn_surface": "metrics:area", + "parc_producto": "crop:code", + "parc_sistexp": "irrigation_system", + "parc_supcult": "cultivation_surface", + } - use_code_attribute = "uso_sigpac" + area_is_in_ha = False extensions = { "https://fiboa.org/crop-extension/v0.2.0/schema.yaml", ADMIN_DIVISION, } + column_additions = { - # https://www.euskadi.eus/contenidos/informacion/pac2015_pagosdirectos/es_def/adjuntos/Anexos_PAC_marzo2015.pdf - # https://www.fega.gob.es/sites/default/files/files/document/AD-CIRCULAR_2-2021_EE98293_SIGC2021.PDF - # Very generic list "admin:country_code": "ES", - "crop:code_list": "https://fiboa.org/code/es/sigpac/land_use.csv", + # FEGA declared-crop codelist (PARC_PRODUCTO) — separate from the SIGPAC land-use list. + # Reference list shipped inside each provincial GPKG as the `cod_producto` layer. + "crop:code_list": "https://fiboa.org/code/es/cultivos_declarados/parc_producto.csv", + } + + column_migrations = { + # crop:code must be a string per the crop extension; parc_producto is an integer. + "parc_producto": lambda col: col.astype("Int64").astype(str), + # admin_*_code are strings; zero-pad province to 2 digits (INE convention). + "provincia": lambda col: col.astype("Int64").astype(str).str.zfill(2), + "municipio": lambda col: col.astype("Int64").astype(str), + } + + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + "irrigation_system": {"type": "string"}, + "cultivation_surface": {"type": "int32"}, + } } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - assert self.id.startswith("es_"), "Assuming Spanish subclass" - - def code_filter(col): - return ~col.isin("AG/CA/ED/FO/IM/IS/IV/TH/ZC/ZU/ZV/MT".split("/") + [None]) + if not self.variant: + self.variant = next(iter(self.variants)) + self.column_additions = { + **self.column_additions, + "determination:datetime": f"{self.variant}-01-01T00:00:00Z", + } - self.column_filters = {self.use_code_attribute: code_filter} - self.column_additions["admin:subdivision_code"] = self.id[len("es_") :].upper() + def layer_filter(self, layer: str, uri: str) -> bool: + # GPKG contains the data layer plus several codelist tables (cod_*) — only read the data. + return layer == "cultivo_declarado" def migrate(self, gdf): - # This actually is a land use code. Not sure if we should put this in crop:code - rows = read_data_csv("es_coda_uso.csv") - mapping = {row["original_code"]: row["original_name"] for row in rows} - mapping_en = {row["original_code"]: row["name_en"] for row in rows} - gdf["crop:name"] = gdf[self.use_code_attribute].map(mapping) - gdf["crop:name_en"] = gdf[self.use_code_attribute].map(mapping_en) + # The source has no globally unique row identifier. Build one from the SIGPAC cadastral key + # plus the declaration-line index, which is unique per record. + def part(col): + return gdf[col].astype("Int64").astype(str) + + gdf["id"] = ( + part("provincia").str.zfill(2) + + "-" + + part("municipio") + + "-" + + part("agregado") + + "-" + + part("zona") + + "-" + + part("poligono") + + "-" + + part("parcela") + + "-" + + part("recinto") + + "-" + + part("ld_recinto") + ) return super().migrate(gdf) + + def get_urls(self): + if self.variant not in self.variants: + opts = ", ".join(self.variants.keys()) + raise ValueError(f"Unknown variant '{self.variant}', choose from {opts}") + + year = self.variant + base = f"https://sigpac-hubcloud.es/geopackages/{year}/cultivo_declarado/" + response = requests.get(base, timeout=60) + response.raise_for_status() + # The directory listing is a classic Apache-style HTML index; parse out the .zip hrefs. + zip_paths = re.findall(r'HREF="(/geopackages/[^"]+\.zip)"', response.text) + if not zip_paths: + raise RuntimeError(f"No GeoPackage archives found at {base}") + return {f"https://sigpac-hubcloud.es{p}": ["*.gpkg"] for p in zip_paths} diff --git a/tests/data-files/convert/es/1501_ALAVA_cd_2025_20250105.gpkg.zip b/tests/data-files/convert/es/1501_ALAVA_cd_2025_20250105.gpkg.zip new file mode 100644 index 0000000000000000000000000000000000000000..a90e4560f22fdb8b5d2345f4211b859060d830cc GIT binary patch literal 30369 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 ZN5EJ;P5g)dx*q56{{C;y8St;#{{d}RTRZ>& literal 0 HcmV?d00001 diff --git a/tests/test_convert.py b/tests/test_convert.py index 2766e5b5..c614158a 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -43,6 +43,7 @@ "lv", "ie", "es_cat", + "es", "nz", "lt", "si", @@ -67,6 +68,7 @@ def _input_files(converter, *names): "br_ba_lem": _input_files("br_ba_lem", "LEM_dataset.zip"), "ch": _input_files("ch", "lwb_nutzungsflaechen_v2_0_lv95.gpkg"), "es_cat": _input_files("es_cat", "Cultius_DUN2023_GPKG.zip"), + "es": {"input_files": {f"{test_path}/es/1501_ALAVA_cd_2025_20250105.gpkg.zip": ["*.gpkg"]}}, "lv": _input_files("lv", "1_100.xml"), "nz": _input_files("nz", "irrigated-land-area-raw-2020-update.zip"), "jecam": _input_files("jecam", "BD_JECAM_CIRAD_2023_feb.shp"), From 892be964e0eb3afc92d3fcef483a41281bdc7e26 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 19 May 2026 23:36:27 +0200 Subject: [PATCH 18/94] PerFileBaseConverter: per-file migration and merging the result, decreasing memory requirements for large data sets --- CHANGELOG.md | 3 +- fiboa_cli/conversion/per_file.py | 295 +++++++++++++++++++++++++++++++ fiboa_cli/datasets/es.py | 4 +- pyproject.toml | 2 +- 4 files changed, 300 insertions(+), 4 deletions(-) create mode 100644 fiboa_cli/conversion/per_file.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 788d21aa..24139d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Converter for Spain (whole), based on the FEGA 2025+ data - Update fr-converter to support 2021/2022 files +- PerFileBaseConverter: per-file migration and merging the result, decreasing memory requirements for large data sets ## [v0.21.0] - 2026-02-16 @@ -20,7 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix the column additions of the determination fields in the AI4SF converter - Add HCAT to datasets where possible - Updated years & variants for at_crop, be_vlg, es_an, es_cl, es_pv, ie, pt, se -- Extend create_stac, include include fiboa data +- Extend create_stac, include fiboa data - Publish command; skip hidden files, generate better texts - Fix to vecorel: converter.license and provider should be string - Added a Dockerfile to simplify working with fiboa diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py new file mode 100644 index 00000000..349b7a55 --- /dev/null +++ b/fiboa_cli/conversion/per_file.py @@ -0,0 +1,295 @@ +import json +import os +from typing import Optional + +import numpy as np +import pyarrow as pa +import pyarrow.parquet as pq + +from .fiboa_converter import FiboaBaseConverter + +GEO_META_KEY = b"geo" +DEFAULT_BATCH_SIZE = 64_000 + + +# This converter is experimental, use with caution. +# Use this primarily for datasets that are too large to be processed by the default converter +class PerFileBaseConverter(FiboaBaseConverter): + def convert( + self, + output_file, + cache=None, + input_files=None, + variant=None, + compression=None, + compression_level: Optional[int] = None, + geoparquet_version=None, + original_geometries=False, + **kwargs, + ) -> str: + dirname, filename = os.path.split(output_file) + filename, ext = os.path.splitext(filename) + if input_files is not None and isinstance(input_files, dict) and len(input_files) > 0: + self.warning("Using user provided input file(s) instead of the pre-defined file(s)") + urls = input_files + else: + urls = self.get_urls() + if urls is None: + raise ValueError("No input files provided") + + # Single-source: the per-file pipeline degenerates to plain convert. + if len(urls) <= 1: + return super().convert( + output_file=output_file, + cache=cache, + input_files=urls, + variant=variant, + compression=compression, + compression_level=compression_level, + geoparquet_version=geoparquet_version, + original_geometries=original_geometries, + **kwargs, + ) + + # Multi-source: convert each URI to its own GeoParquet part, then merge. + part_files = [] + for index, (uri, target) in enumerate(urls.items()): + part = os.path.join(dirname, f"{filename}_{index}{ext}") + self.info(f"Converting source {index + 1}/{len(urls)}: {uri}") + super().convert( + output_file=part, + cache=cache, + input_files={uri: target}, + variant=variant, + compression=compression, + compression_level=compression_level, + geoparquet_version=geoparquet_version, + original_geometries=original_geometries, + **kwargs, + ) + part_files.append(part) + self.merge_files(output_file, part_files, compression=compression or "zstd") + return output_file + + def merge_files( + self, + output_file: str, + paths: list, + batch_size: int = DEFAULT_BATCH_SIZE, + compression: str = "zstd", + compression_level: Optional[int] = None, + cleanup_parts: bool = False, + ) -> str: + """ + Merge a list of GeoParquet files into a single GeoParquet, globally + sorted by Hilbert distance. Streams via pyarrow row groups so peak + memory is roughly O(batch_size * k). + + Assumes each input file was produced by the standard convert pipeline, + which already sorts rows by Hilbert distance against the CRS's total + bounds (see ``vecorel_cli.vecorel.hilbert.hilbert_sort_geodataframe``). + Because every input shares the same Hilbert reference grid (derived + from the CRS, not from per-file extents), no pre-sort is required — + we just merge the already-sorted runs. + """ + if not paths: + raise ValueError("No paths to merge") + paths = [str(p) for p in paths] + + base_pf = pq.ParquetFile(paths[0]) + base_schema = base_pf.schema_arrow + base_meta = base_schema.metadata or {} + if GEO_META_KEY not in base_meta: + raise ValueError(f"{paths[0]} has no 'geo' metadata; not a GeoParquet?") + base_geo = json.loads(base_meta[GEO_META_KEY]) + primary_col = base_geo["primary_column"] + primary_col_meta = base_geo["columns"][primary_col] + crs = primary_col_meta.get("crs") + + # Validate schemas + CRS, collect per-file bboxes / geometry_types for + # the merged geo metadata. + bboxes: list = [] + geom_types: set = set() + if primary_col_meta.get("bbox") is not None: + bboxes.append(primary_col_meta["bbox"]) + geom_types.update(primary_col_meta.get("geometry_types") or []) + for path in paths[1:]: + pf = pq.ParquetFile(path) + sch = pf.schema_arrow + if not sch.equals(base_schema, check_metadata=False): + raise ValueError( + f"Schema mismatch: {path} differs from {paths[0]}.\n" + f" Expected: {base_schema}\n" + f" Got: {sch}" + ) + geo = json.loads((sch.metadata or {})[GEO_META_KEY]) + col = geo["columns"][primary_col] + if col.get("crs") != crs: + raise ValueError( + f"CRS mismatch: {path} has crs={col.get('crs')!r}, expected {crs!r}" + ) + if col.get("bbox") is not None: + bboxes.append(col["bbox"]) + geom_types.update(col.get("geometry_types") or []) + + merged_bbox = None + if bboxes: + merged_bbox = ( + min(b[0] for b in bboxes), + min(b[1] for b in bboxes), + max(b[2] for b in bboxes), + max(b[3] for b in bboxes), + ) + + # Same Hilbert reference grid that the upstream sort used. + from vecorel_cli.vecorel.hilbert import crs_total_bounds + + total_bounds = crs_total_bounds(crs) + + self.info(f"Streaming merge -> {output_file} (Hilbert ref bounds = {total_bounds})") + _streaming_merge( + paths, + output_file, + primary_col, + total_bounds, + merged_bbox, + sorted(geom_types), + batch_size, + compression, + compression_level, + ) + + if cleanup_parts: + for path in paths: + try: + os.remove(path) + except OSError: + self.warning(f"Could not remove part file {path}") + + return output_file + + +# ---------- helpers ---------- + + +def _bounds_array_for_table(table: pa.Table, primary_col: str) -> np.ndarray: + """Return an (N, 4) float64 array of [xmin, ymin, xmax, ymax] per feature. + + Uses the GeoParquet 1.1.0 covering ``bbox`` struct column when present + (zero-decode); otherwise falls back to decoding WKB. + """ + if "bbox" in table.column_names and pa.types.is_struct(table.column("bbox").type): + arr = table.column("bbox").combine_chunks() + return np.column_stack( + [ + arr.field("xmin").to_numpy(zero_copy_only=False), + arr.field("ymin").to_numpy(zero_copy_only=False), + arr.field("xmax").to_numpy(zero_copy_only=False), + arr.field("ymax").to_numpy(zero_copy_only=False), + ] + ).astype(np.float64, copy=False) + import shapely + + wkb_list = table.column(primary_col).combine_chunks().to_pylist() + geoms = shapely.from_wkb(wkb_list) + return shapely.bounds(geoms) + + +def _hilbert_keys_for_table(table: pa.Table, primary_col: str, total_bounds) -> np.ndarray: + from vecorel_cli.vecorel.hilbert import hilbert_distances_from_bounds + + bounds = _bounds_array_for_table(table, primary_col) + return hilbert_distances_from_bounds(bounds, total_bounds) + + +def _build_output_schema(input_schema: pa.Schema, merged_bbox, geom_types) -> pa.Schema: + """Patch the geo metadata: merged bbox + union of geometry_types. Other + schema metadata and field metadata are preserved unchanged.""" + meta = dict(input_schema.metadata or {}) + geo = json.loads(meta[GEO_META_KEY]) + primary_col = geo["primary_column"] + if merged_bbox is not None: + geo["columns"][primary_col]["bbox"] = [float(v) for v in merged_bbox] + if geom_types: + geo["columns"][primary_col]["geometry_types"] = list(geom_types) + meta[GEO_META_KEY] = json.dumps(geo).encode("utf-8") + return input_schema.with_metadata(meta) + + +def _streaming_merge( + paths: list, + output_file: str, + primary_col: str, + total_bounds, + merged_bbox, + geom_types, + batch_size: int, + compression: str, + compression_level: Optional[int], +) -> None: + pq_files = [pq.ParquetFile(p) for p in paths] + in_schema = pq_files[0].schema_arrow + out_schema = _build_output_schema(in_schema, merged_bbox, geom_types) + + iters = [pf.iter_batches(batch_size=batch_size) for pf in pq_files] + heads: list = [None] * len(paths) + hilberts: list = [None] * len(paths) + + def refill(i): + # Skip any empty batches; mark the iterator exhausted only when next() raises. + while True: + try: + batch = next(iters[i]) + except StopIteration: + heads[i] = None + hilberts[i] = None + return + if batch.num_rows == 0: + continue + tbl = pa.Table.from_batches([batch]) + heads[i] = tbl + hilberts[i] = _hilbert_keys_for_table(tbl, primary_col, total_bounds) + return + + for i in range(len(paths)): + refill(i) + + write_kwargs = {"compression": compression} + if compression_level is not None: + write_kwargs["compression_level"] = compression_level + writer = pq.ParquetWriter(output_file, out_schema, **write_kwargs) + + try: + while any(h is not None for h in heads): + active = [i for i, h in enumerate(heads) if h is not None] + # The horizon is the smallest "current max" Hilbert across active heads. + # Every row with hilbert <= horizon is emit-safe in this round, because + # no still-pending row from any other file can possibly be less than it. + horizon = min(hilberts[i][-1] for i in active) + + chunks = [] + chunk_h = [] + for i in active: + h = hilberts[i] + cut = int(np.searchsorted(h, horizon, side="right")) + if cut == 0: + continue + chunks.append(heads[i].slice(0, cut)) + chunk_h.append(h[:cut]) + if cut == heads[i].num_rows: + refill(i) + else: + heads[i] = heads[i].slice(cut) + hilberts[i] = h[cut:] + + if not chunks: + # Defensive: shouldn't happen because at least the file defining the + # horizon will contribute its full current batch. + break + + combined = pa.concat_tables(chunks) + combined_h = np.concatenate(chunk_h) + order = np.argsort(combined_h, kind="stable") + writer.write_table(combined.take(pa.array(order))) + finally: + writer.close() diff --git a/fiboa_cli/datasets/es.py b/fiboa_cli/datasets/es.py index 2352d444..5ecd23ba 100644 --- a/fiboa_cli/datasets/es.py +++ b/fiboa_cli/datasets/es.py @@ -3,10 +3,10 @@ import requests from vecorel_cli.vecorel.extensions import ADMIN_DIVISION -from ..conversion.fiboa_converter import FiboaBaseConverter +from ..conversion.per_file import PerFileBaseConverter -class Converter(FiboaBaseConverter): +class Converter(PerFileBaseConverter): id = "es" short_name = "Spain" title = "Spain Declared Crops (Cultivos Declarados SIGPAC)" diff --git a/pyproject.toml b/pyproject.toml index 735559ce..22cbda67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] requires-python = ">=3.11" dependencies = [ - "vecorel-cli==0.2.15", + "vecorel-cli==0.2.16", "spdx-license-list==3.27.0", ] From 06e47ea1f3fdd616ca48889737510c1685a71c7a Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 20 May 2026 20:24:25 +0200 Subject: [PATCH 19/94] Update ES Provinces list --- fiboa_cli/datasets/data-files/es_cl_prv.csv | 47 +++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/fiboa_cli/datasets/data-files/es_cl_prv.csv b/fiboa_cli/datasets/data-files/es_cl_prv.csv index c05865db..4b94d7b9 100644 --- a/fiboa_cli/datasets/data-files/es_cl_prv.csv +++ b/fiboa_cli/datasets/data-files/es_cl_prv.csv @@ -1,10 +1,51 @@ code,province,filename +01,Álava,ALAVA +02,Albacete,ALBACETE +03,Alicante,ALICANTE +04,Almería,ALMERIA 05,Ávila,AVILA +06,Badajoz,BADAJOZ +07,Illes Balears,ILLES BALEARS +08,Barcelona,BARCELONA +09,Burgos,BURGOS +10,Cáceres,CACERES +11,Cádiz,CADIZ +12,Castellón,CASTELLON +13,Ciudad Real,CIUDAD REAL +14,Córdoba,CORDOBA +15,A Coruña,A CORUÑA +16,Cuenca,CUENCA +17,Girona,GIRONA +18,Granada,GRANADA +19,Guadalajara,GUADALAJARA +20,Guipúzcoa,GUIPUZCOA +21,Huelva,HUELVA +22,Huesca,HUESCA +23,Jaén,JAEN +24,León,LEON +25,Lleida,LLEIDA +26,La Rioja,LA RIOJA +27,Lugo,LUGO +28,Madrid,MADRID +29,Málaga,MALAGA +30,Murcia,MURCIA +31,Navarra,NAVARRA +32,Ourense,OURENSE +33,Asturias,ASTURIAS 34,Palencia,PALENCIA +35,Las Palmas,LAS PALMAS +36,Pontevedra,PONTEVEDRA 37,Salamanca,SALAMANCA +38,Santa Cruz de Tenerife,SANTA CRUZ DE TENERIFE +39,Cantabria,CANTABRIA 40,Segovia,SEGOVIA -42,Soria,SORIA, +41,Sevilla,SEVILLA +42,Soria,SORIA +43,Tarragona,TARRAGONA +44,Teruel,TERUEL +45,Toledo,TOLEDO +46,Valencia,VALENCIA 47,Valladolid,VALLADOLID +48,Vizcaya,VIZCAYA 49,Zamora,ZAMORA -24,León,LEON -09,Burgos,BURGOS +50,Zaragoza,ZARAGOZA From 13aa258367ab4f3b067160769f6272fb97577a1a Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 20 May 2026 20:24:45 +0200 Subject: [PATCH 20/94] Fix hilbert-sort bug --- fiboa_cli/conversion/per_file.py | 78 ++++++++++++++++++++++++++++---- fiboa_cli/datasets/es.py | 27 ++++------- 2 files changed, 80 insertions(+), 25 deletions(-) diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 349b7a55..bd29f0f6 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -54,7 +54,13 @@ def convert( # Multi-source: convert each URI to its own GeoParquet part, then merge. part_files = [] for index, (uri, target) in enumerate(urls.items()): - part = os.path.join(dirname, f"{filename}_{index}{ext}") + part = os.path.join(dirname, f"{filename}_{index}_part{ext}") + part_files.append(part) + if os.path.exists(part): + self.info( + f"Skipping existing file {part}: {uri} -> {output_file} (part {index + 1}/{len(urls)})" + ) + continue self.info(f"Converting source {index + 1}/{len(urls)}: {uri}") super().convert( output_file=part, @@ -67,7 +73,6 @@ def convert( original_geometries=original_geometries, **kwargs, ) - part_files.append(part) self.merge_files(output_file, part_files, compression=compression or "zstd") return output_file @@ -85,12 +90,13 @@ def merge_files( sorted by Hilbert distance. Streams via pyarrow row groups so peak memory is roughly O(batch_size * k). - Assumes each input file was produced by the standard convert pipeline, - which already sorts rows by Hilbert distance against the CRS's total - bounds (see ``vecorel_cli.vecorel.hilbert.hilbert_sort_geodataframe``). - Because every input shares the same Hilbert reference grid (derived - from the CRS, not from per-file extents), no pre-sort is required — - we just merge the already-sorted runs. + Each input file is expected to be sorted by Hilbert distance against + the CRS's total bounds (see ``vecorel_cli.vecorel.hilbert``). If a + part file is *not* in Hilbert order it is sorted in place before the + streaming merge — this guards against pre-existing part files that + were produced by an older vecorel-cli (which sorted by WKB lex order + instead of Hilbert) and would otherwise silently drop rows in the + streaming merge (``np.searchsorted`` requires a sorted input). """ if not paths: raise ValueError("No paths to merge") @@ -146,7 +152,24 @@ def merge_files( total_bounds = crs_total_bounds(crs) + # Verify each part is Hilbert-sorted; sort in place if not. With a + # vecorel-cli that already Hilbert-sorts, this is a fast no-op read. + self.info(f"Verifying Hilbert order of {len(paths)} part file(s)") + n_resorted = 0 + for path in paths: + if _ensure_hilbert_sorted( + path, primary_col, total_bounds, compression, compression_level + ): + n_resorted += 1 + self.warning( + f" {path}: was not Hilbert-sorted, re-sorted in place. " + "(Bump vecorel-cli to skip this rewrite next time.)" + ) + if n_resorted: + self.warning(f"Re-sorted {n_resorted}/{len(paths)} part file(s) before merging.") + self.info(f"Streaming merge -> {output_file} (Hilbert ref bounds = {total_bounds})") + expected_rows = sum(pq.ParquetFile(p).metadata.num_rows for p in paths) _streaming_merge( paths, output_file, @@ -158,6 +181,14 @@ def merge_files( compression, compression_level, ) + actual_rows = pq.ParquetFile(output_file).metadata.num_rows + if actual_rows != expected_rows: + raise RuntimeError( + f"Streaming merge dropped rows: expected {expected_rows:,} " + f"(sum of inputs), wrote {actual_rows:,} to {output_file}. " + "This is a bug — inputs were verified Hilbert-sorted before merge." + ) + self.info(f"Merged {actual_rows:,} rows into {output_file}") if cleanup_parts: for path in paths: @@ -202,6 +233,37 @@ def _hilbert_keys_for_table(table: pa.Table, primary_col: str, total_bounds) -> return hilbert_distances_from_bounds(bounds, total_bounds) +def _ensure_hilbert_sorted( + path: str, + primary_col: str, + total_bounds, + compression: str, + compression_level: Optional[int], +) -> bool: + """If ``path`` is already Hilbert-sorted against ``total_bounds``, leave it + untouched and return False. Otherwise sort it in place and return True. + + The whole file is loaded into memory once; for the per-file converter this + is bounded by a single source partition (much smaller than the merged + dataset). Schema metadata (``geo``, collection JSON, etc.) is preserved. + """ + pf = pq.ParquetFile(path) + table = pf.read() + hilberts = _hilbert_keys_for_table(table, primary_col, total_bounds) + # NB: hilberts is uint64; never use np.diff for monotonicity here — uint + # underflow makes any descent wrap to a huge positive and fool the check. + if hilberts.size <= 1 or bool(np.all(hilberts[1:] >= hilberts[:-1])): + return False + order = np.argsort(hilberts, kind="stable") + sorted_table = table.take(pa.array(order)) + sorted_table = sorted_table.replace_schema_metadata(pf.schema_arrow.metadata) + write_kwargs = {"compression": compression} + if compression_level is not None: + write_kwargs["compression_level"] = compression_level + pq.write_table(sorted_table, path, **write_kwargs) + return True + + def _build_output_schema(input_schema: pa.Schema, merged_bbox, geom_types) -> pa.Schema: """Patch the geo metadata: merged bbox + union of geometry_types. Other schema metadata and field metadata are preserved unchanged.""" diff --git a/fiboa_cli/datasets/es.py b/fiboa_cli/datasets/es.py index 5ecd23ba..4870d93d 100644 --- a/fiboa_cli/datasets/es.py +++ b/fiboa_cli/datasets/es.py @@ -1,12 +1,15 @@ import re import requests +from vecorel_cli.conversion.admin import AdminConverterMixin from vecorel_cli.vecorel.extensions import ADMIN_DIVISION +from fiboa_cli.datasets.commons.hcat import AddHCATMixin + from ..conversion.per_file import PerFileBaseConverter -class Converter(PerFileBaseConverter): +class Converter(AdminConverterMixin, AddHCATMixin, PerFileBaseConverter): id = "es" short_name = "Spain" title = "Spain Declared Crops (Cultivos Declarados SIGPAC)" @@ -25,15 +28,17 @@ class Converter(PerFileBaseConverter): variants = {"2025": "2025"} + # FEGA declared-crop codelist (PARC_PRODUCTO) — separate from the SIGPAC land-use list. + # Reference list shipped inside each provincial GPKG as the `cod_producto` layer. + ec_mapping_csv = "https://fiboa.org/code/es/es.csv" + columns = { "geometry": "geometry", "id": "id", - "provincia": "admin_province_code", - "municipio": "admin_municipality_code", + "provincia": "admin:subdivision_code", "dn_surface": "metrics:area", "parc_producto": "crop:code", "parc_sistexp": "irrigation_system", - "parc_supcult": "cultivation_surface", } area_is_in_ha = False @@ -43,27 +48,15 @@ class Converter(PerFileBaseConverter): ADMIN_DIVISION, } - column_additions = { - "admin:country_code": "ES", - # FEGA declared-crop codelist (PARC_PRODUCTO) — separate from the SIGPAC land-use list. - # Reference list shipped inside each provincial GPKG as the `cod_producto` layer. - "crop:code_list": "https://fiboa.org/code/es/cultivos_declarados/parc_producto.csv", - } - column_migrations = { - # crop:code must be a string per the crop extension; parc_producto is an integer. - "parc_producto": lambda col: col.astype("Int64").astype(str), - # admin_*_code are strings; zero-pad province to 2 digits (INE convention). + "parc_producto": lambda col: col.astype("Int64").fillna(0).astype(str), "provincia": lambda col: col.astype("Int64").astype(str).str.zfill(2), - "municipio": lambda col: col.astype("Int64").astype(str), } missing_schemas = { "properties": { - "admin_province_code": {"type": "string"}, "admin_municipality_code": {"type": "string"}, "irrigation_system": {"type": "string"}, - "cultivation_surface": {"type": "int32"}, } } From cf85e8143af3110c06593f407ae2be0571e0b13c Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 20 May 2026 20:25:06 +0200 Subject: [PATCH 21/94] Fail early for debugging --- fiboa_cli/datasets/commons/hcat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/datasets/commons/hcat.py b/fiboa_cli/datasets/commons/hcat.py index 5a94d219..dad4e448 100644 --- a/fiboa_cli/datasets/commons/hcat.py +++ b/fiboa_cli/datasets/commons/hcat.py @@ -35,8 +35,8 @@ def __init__(self, *args, **kwargs): def convert(self, *args, **kwargs): self.mapping_file = kwargs.get("mapping_file") if not self.mapping_file: - assert self.ec_mapping_csv is not None, ( - "Specify ec_mapping_csv in Converter, e.g. find them at https://github.com/maja601/EuroCrops/tree/main/csvs/country_mappings" + assert isinstance(self.ec_mapping_csv, str), ( + "Specify proper ec_mapping_csv in Converter, e.g. find them at https://github.com/maja601/EuroCrops/tree/main/csvs/country_mappings" ) return super().convert(*args, **kwargs) From cea4333a6f155770df4e99f599936e60216ad2ce Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 20 May 2026 20:25:28 +0200 Subject: [PATCH 22/94] Don't upload part files --- fiboa_cli/publish.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/publish.py b/fiboa_cli/publish.py index 122cc4da..81d8fa29 100644 --- a/fiboa_cli/publish.py +++ b/fiboa_cli/publish.py @@ -441,4 +441,6 @@ def upload_to_aws(self, target): self.info("Uploading to aws") self.check_command("aws") - self.exc(f"aws s3 sync --exclude '.*' {target} {self.s3_upload_path}") + self.exc( + f"aws s3 sync --exclude '.*' --exclude '*_part.parquet' {target} {self.s3_upload_path}" + ) From 4ba30cac8ee87dfeb92b407d128eadcd9332c9fb Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 20 May 2026 20:52:38 +0200 Subject: [PATCH 23/94] Keep geo_parquet version in PerFileConverter --- fiboa_cli/conversion/per_file.py | 39 ++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index bd29f0f6..9b9e86b0 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -73,7 +73,13 @@ def convert( original_geometries=original_geometries, **kwargs, ) - self.merge_files(output_file, part_files, compression=compression or "zstd") + self.merge_files( + output_file, + part_files, + compression=compression or "zstd", + compression_level=compression_level, + geoparquet_version=geoparquet_version, + ) return output_file def merge_files( @@ -83,6 +89,7 @@ def merge_files( batch_size: int = DEFAULT_BATCH_SIZE, compression: str = "zstd", compression_level: Optional[int] = None, + geoparquet_version: Optional[str] = None, cleanup_parts: bool = False, ) -> str: """ @@ -97,7 +104,19 @@ def merge_files( were produced by an older vecorel-cli (which sorted by WKB lex order instead of Hilbert) and would otherwise silently drop rows in the streaming merge (``np.searchsorted`` requires a sorted input). + + ``geoparquet_version`` (``"1.0.0"`` / ``"1.1.0"`` / ``None``) sets the + ``version`` field of the merged file's ``geo`` metadata. When ``None`` + (default), the value declared by the input files is preserved unchanged. """ + if geoparquet_version is not None: + from vecorel_cli.const import GEOPARQUET_VERSIONS + + if geoparquet_version not in GEOPARQUET_VERSIONS: + raise ValueError( + f"Invalid geoparquet_version {geoparquet_version!r}; " + f"expected one of {GEOPARQUET_VERSIONS}" + ) if not paths: raise ValueError("No paths to merge") paths = [str(p) for p in paths] @@ -180,6 +199,7 @@ def merge_files( batch_size, compression, compression_level, + geoparquet_version, ) actual_rows = pq.ParquetFile(output_file).metadata.num_rows if actual_rows != expected_rows: @@ -264,9 +284,15 @@ def _ensure_hilbert_sorted( return True -def _build_output_schema(input_schema: pa.Schema, merged_bbox, geom_types) -> pa.Schema: - """Patch the geo metadata: merged bbox + union of geometry_types. Other - schema metadata and field metadata are preserved unchanged.""" +def _build_output_schema( + input_schema: pa.Schema, + merged_bbox, + geom_types, + geoparquet_version: Optional[str] = None, +) -> pa.Schema: + """Patch the geo metadata: merged bbox + union of geometry_types, and + optionally overwrite the GeoParquet ``version`` field. Other schema + metadata and field metadata are preserved unchanged.""" meta = dict(input_schema.metadata or {}) geo = json.loads(meta[GEO_META_KEY]) primary_col = geo["primary_column"] @@ -274,6 +300,8 @@ def _build_output_schema(input_schema: pa.Schema, merged_bbox, geom_types) -> pa geo["columns"][primary_col]["bbox"] = [float(v) for v in merged_bbox] if geom_types: geo["columns"][primary_col]["geometry_types"] = list(geom_types) + if geoparquet_version is not None: + geo["version"] = geoparquet_version meta[GEO_META_KEY] = json.dumps(geo).encode("utf-8") return input_schema.with_metadata(meta) @@ -288,10 +316,11 @@ def _streaming_merge( batch_size: int, compression: str, compression_level: Optional[int], + geoparquet_version: Optional[str] = None, ) -> None: pq_files = [pq.ParquetFile(p) for p in paths] in_schema = pq_files[0].schema_arrow - out_schema = _build_output_schema(in_schema, merged_bbox, geom_types) + out_schema = _build_output_schema(in_schema, merged_bbox, geom_types, geoparquet_version) iters = [pf.iter_batches(batch_size=batch_size) for pf in pq_files] heads: list = [None] * len(paths) From e69174d261d299fa7bed05516981418b5b8bc08e Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 21 Aug 2026 13:58:33 +0200 Subject: [PATCH 24/94] Simplify `fiboa publish` for catalog-driven publication `fiboa publish` now only converts, validates, builds PMTiles and writes a collection.json with relative links, file:size/file:checksum (multihash), a web-map-links v1.3.0 `pmtiles` link with `pmtiles:layers` and a `visual` asset. README/LICENSE generation, the data-survey lookup and the S3 upload are gone; catalogs such as fieldsoftheworld/harmonized-field-data-catalog own those. spdx-license-list is only needed by tests and moves to the dev feature. Also: - FiboaBaseConverter: keep the determination:datetime column that `use_variant_as_determination` adds; it was removed again as unlisted (affected dk, hr). - be_vlg: drop plots without a crop code (one in 2023 failed validation), take the determination date from the variant year, add the 2026 edition. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 3 + README.md | 47 +-- fiboa_cli/conversion/fiboa_converter.py | 4 + fiboa_cli/datasets/be_vlg.py | 10 +- fiboa_cli/publish.py | 490 +++++++--------------- pixi.lock | 11 +- pyproject.toml | 2 +- tests/data-files/publish/BE-VLG-survey.md | 78 ---- tests/test_publish.py | 49 ++- 9 files changed, 215 insertions(+), 479 deletions(-) delete mode 100644 tests/data-files/publish/BE-VLG-survey.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3747e4..7a80ad1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +- Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) +- BE-VLG: drop plots without a crop code (one such plot in the 2023 edition made validation fail); derive determination:datetime from the variant year instead of a constant date +- `fiboa publish` no longer uploads to S3 or generates README/LICENSE files. It creates GeoParquet, PMTiles and a STAC Collection with relative links, `file:size`/`file:checksum` and a web-map-links v1.3.0 `pmtiles` link. Publishing is done by catalogs such as the [harmonized field data catalog](https://github.com/fieldsoftheworld/harmonized-field-data-catalog). - Add Italy Tuscany (IT-1) basd on EuroCrops v2 - Suuport multiple years for CZ - Multiple years for DE_sh diff --git a/README.md b/README.md index 57982654..9a5f5252 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ fiboa CLI supports various commands to work with the files: - [Improve a fiboa Parquet file](#improve-a-fiboa-parquet-file) - [Update an extension template with new names](#update-an-extension-template-with-new-names) - [Converter for existing datasets](#converter-for-existing-datasets) - - [Publish datasets to source coop or your own s3 repository](#publish-datasets-to-source-coop-or-your-own-s3-repository) + - [Publish datasets](#publish-datasets) - [Development](#development) - [Implement a converter](#implement-a-converter) - [Run in Docker](#run-in-docker) @@ -193,46 +193,43 @@ Use any of the IDs from the list to convert an existing dataset to fiboa: See [Implement a converter](#implement-a-converter) for details about how to -### Publish datasets to source coop or your own s3 repository +### Publish datasets `fiboa publish -o ` -The publish converts and publishes a fiboa dataset to source coop or your own s3 repository. The target directory -will be filled with the following files: +Converts and validates a fiboa dataset and prepares everything that is needed to publish it +in a (STAC-based) catalog. The target directory will be filled with the following files: ``` / - .parquet - .pmtiles # requires working ogr2ogr and tippecanoe - stac/collection.json - README.md # generated if --generate-meta/-gm flag is present - LICENSE.txt # generated if --generate-meta/-gm flag is present + [-].parquet + [-].pmtiles # requires working ogr2ogr and tippecanoe + collection.json # STAC Collection with relative links to the files above ``` -This directory is synchronized to the s3 repository (default source.coop/fiboa/data). +The STAC Collection carries `file:size` and `file:checksum` for the files and a +`pmtiles` link (web-map-links extension). Existing files in the target directory are reused, +delete them to regenerate. Uploading to a bucket and catalog-specific metadata +(README, styles, thumbnails, ...) are the job of the catalog that publishes the data, e.g. the +[harmonized field data catalog](https://github.com/fieldsoftheworld/harmonized-field-data-catalog). -**Requirements**: Requires the [aws CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) to be installed, -and `AWS_ACCESS_KEY_ID` with `AWS_SECRET_ACCESS_KEY` environment variables. Also, for generating the pmtiles file, -it requires [ogr2ogr](https://gdal.org/programs/ogr2ogr.html) and [tippecanoe](https://github.com/mapbox/tippecanoe). +The command runs: -The command executes the following steps: - -- `fiboa convert` to generate a fiboa parquet dataset. All convert parameters are passed to the converter. -- `fiboa validate` to validate the fiboa dataset -- creates a .pmtiles from the parquet file. Uses ogr2ogr and tippecanoe -- `fiboa create-stac-collection` to create a STAC collection -- `fiboa publish` to publish the fiboa dataset to a source coop or your own s3 repository +- `fiboa convert` to create a `[-].parquet` file +- `fiboa validate` to validate the GeoParquet file +- `ogr2ogr | tippecanoe` to create the PMTiles file +- `fiboa create-stac-collection` to create the STAC Collection Examples: -- `fiboa publish at_crop -o data/at_crop` -- `fiboa publish -c /tmp/cache -gm br_conab -o data/br_conab` +- `fiboa publish at -o data/at` +- `fiboa publish -c /tmp/cache nl --variant 2025 -o data/nl/2025` Relevant parameters: -- `--generate-meta/-gm` Generatse the README.md and LICENSE.txt files if absent, based on data-survey and converter properties. -- `--data-url` The URL to the data repository, used when generating the README -- `--s3-upload-path` The `aws s3 sync` target. Defaults to `s3://source.coop/fiboa/data` . Uploading requires the `aws` CLI, and `AWS_ACCESS_KEY_ID` with `AWS_SECRET_ACCESS_KEY` environment variables. +- `--variant` Choose the variant (e.g. year) of a dataset, defaults to the first variant. +- `--no-pmtiles` Skip PMTiles generation. +- `--tippecanoe-opts` Options passed to tippecanoe, defaults to `-zg --drop-densest-as-needed --extend-zooms-if-still-dropping`. Check `fiboa publish --help` for more details. diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index c5ac7227..a073b0e5 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -14,6 +14,10 @@ class FiboaBaseConverter(BaseConverter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.extensions.add(get_fiboa_uri()) + if self.use_variant_as_determination: + # The column is added in post_migrate; list it so it survives the + # "remove unlisted columns" step of the base converter. + self.columns = {**self.columns, "determination:datetime": "determination:datetime"} def post_migrate(self, gdf): gdf = super().post_migrate(gdf) diff --git a/fiboa_cli/datasets/be_vlg.py b/fiboa_cli/datasets/be_vlg.py index 4d4c253a..b8dc6d9a 100644 --- a/fiboa_cli/datasets/be_vlg.py +++ b/fiboa_cli/datasets/be_vlg.py @@ -10,6 +10,7 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): variants = { str(k): {PREFIX + v: [v.replace("_GPKG.zip", ".gpkg")]} for k, v in ( + (2026, "agpa_2026_2026-06-02_public.zip"), (2025, "Landbouwgebruikspercelen_2025_-_Voorlopig_(extractie_02-06-2025)_GPKG.zip"), (2024, "Landbouwgebruikspercelen_2024_-_Definitief_(extractie_27-03-2025)_GPKG.zip"), (2023, "Landbouwgebruikspercelen_2023_-_Definitief_(extractie_28-03-2024)_GPKG.zip"), @@ -42,8 +43,13 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): "GWSCOD_H": "crop:code", "GWSNAM_H": "crop:name", } - column_additions = { - "determination:datetime": "2024-03-28T00:00:00Z", + # Each edition is the campaign year of its variant; the old constant + # "2024-03-28" was the extraction date of one edition applied to all of them. + use_variant_as_determination = True + column_filters = { + # A handful of plots (e.g. one in 2023, typology "Niet-geclassificeerd") carry no + # crop code; crop:code is required by the crop extension, so drop them. + "GWSCOD_H": lambda col: col.notna(), } ec_mapping_csv = "be_vlg_2021.csv" diff --git a/fiboa_cli/publish.py b/fiboa_cli/publish.py index 122cc4da..20eddeab 100644 --- a/fiboa_cli/publish.py +++ b/fiboa_cli/publish.py @@ -1,17 +1,14 @@ +import hashlib import json import os -import re +import shutil +import subprocess import sys -from datetime import date -from functools import cache from pathlib import Path import click -import requests -import spdx_license_list from vecorel_cli.basecommand import BaseCommand, runnable from vecorel_cli.cli.options import VECOREL_TARGET -from vecorel_cli.encoding.auto import create_encoding from .convert import ConvertData from .converters import Converters @@ -19,84 +16,50 @@ from .registry import Registry from .validate import ValidateData -STAC_EXTENSION = "https://stac-extensions.github.io/web-map-links/v1.2.0/schema.json" -DESCRIPTIONS = { - "id": "Unique identifier", - "collection": "The collection identifier", - "inspire:id": "The INSPIRE identifier", - "determination:datetime": "Timestamp of the determination of the field boundary", - "metrics:area": "Field area in square meters", - "metrics:perimeter": "Field perimeter in square meters", - "crop:code_list": "A link to the code list", - "crop:code": "The crop code", - "crop:name": "Crop name in the original language", - "crop:name_en": "Crop name in English", - "hcat:name": "The machine-readable HCAT name of the crop", - "hcat:code": "The 10-digit HCAT code indicating the hierarchy of the crop", - "hcat:name_en": "The HCAT crop name translated into English", - "admin:country_code": "ISO 3166-1 alpha-2 country code.", - "admin:subdivision_code": "ISO 3166-2 principal subdivision code (e.g. province or state)", -} +FILE_EXTENSION = "https://stac-extensions.github.io/file/v2.1.0/schema.json" +WEB_MAP_LINKS_EXTENSION = "https://stac-extensions.github.io/web-map-links/v1.3.0/schema.json" +PMTILES_MEDIA_TYPE = "application/vnd.pmtiles" +TIPPECANOE_DEFAULT_OPTS = "-zg --drop-densest-as-needed --extend-zooms-if-still-dropping" is_windows = os.name == "nt" +def multihash_sha256(path: Path, chunk_size: int = 1024 * 1024) -> str: + """ + sha2-256 multihash of a file, hex encoded: 0x12 (sha2-256), 0x20 (32 bytes), digest. + This is the encoding the STAC file extension expects for ``file:checksum``. + """ + digest = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(chunk_size), b""): + digest.update(chunk) + return "1220" + digest.hexdigest() + + class Publish(BaseCommand): cmd_name = "publish" - cmd_help = f"Convert and publish a {Registry.project} dataset to source coop." - url_base = "https://data.source.coop/fiboa/data" + cmd_help = ( + f"Convert a {Registry.project} dataset and prepare it for publication: " + "GeoParquet, PMTiles and a STAC Collection with relative links." + ) @staticmethod def get_cli_args(): return { **ConvertData.get_cli_args(), "target": VECOREL_TARGET(folder=True), - "generate_meta": click.option( - "--generate-meta", - "-gm", - is_flag=True, - type=click.BOOL, - help="Generate README.txt and LICENSE.txt for the dataset if not present.", - default=False, - ), - "data_url": click.option( - "--data-url", - type=click.STRING, - help="When generating documentation, this is the link to the data.", - ), - "s3_upload_path": click.option( - "--s3-upload-path", - type=click.STRING, - help="Upload to this path on S3. By default it's the source coop fiboa data repository.", - ), - "yes": click.option( - "--yes", - "-y", + "pmtiles": click.option( + "--pmtiles/--no-pmtiles", is_flag=True, - type=click.BOOL, - help="Answer yes to all questions.", - default=False, - show_default=True, - ), - "data_survey_url": click.option( - "--data-survey-url", - type=click.STRING, - help="URL to the data survey markdown file.", - default=os.getenv("FIBOA_DATA_SURVEY"), + help="Generate PMTiles with ogr2ogr and tippecanoe.", + default=True, show_default=True, ), - "editor": click.option( - "--editor", + "tippecanoe_opts": click.option( + "--tippecanoe-opts", type=click.STRING, - help="Editor to use when editing generated files.", - default=os.getenv("EDITOR", "edit" if is_windows else "nano"), - show_default=True, - ), - "converted_by": click.option( - "--converted-by", - type=click.STRING, - help="Name of the person or organization that converted the data.", - default=os.getenv("FIBOA_CONVERTED_BY"), + help="Additional options passed to tippecanoe.", + default=TIPPECANOE_DEFAULT_OPTS, show_default=True, ), } @@ -108,337 +71,174 @@ def callback(dataset, *args, **kwargs): return callback - def __init__(self, dataset: str, data_url=None, s3_upload_path=None): + def __init__(self, dataset: str): super().__init__() self.cmd_title = f"Publish {dataset}" self.dataset = dataset - self.data_url = data_url or f"{self.url_base}/{self.dataset}" - self.s3_upload_path = ( - s3_upload_path or f"s3://us-west-2.opendata.source.coop/fiboa/data/{self.dataset}/" - ) try: self.converter = Converters().load(self.dataset) except (ImportError, NameError, OSError, RuntimeError, SyntaxError) as e: raise Exception(f"Converter for '{self.dataset}' not available or faulty: {e}") from e - def exc(self, cmd): - assert os.system(cmd) == 0 - def check_command(self, cmd, name=None): - if os.system(f"{cmd} --version") != 0: + if shutil.which(cmd) is None: self.error(f"Missing command {cmd}. Please install {name or cmd}") sys.exit(1) - def download_data_survey(self, base, **kwargs): - data_survey = ( - kwargs.get("data_survey_url") - or f"https://raw.githubusercontent.com/fiboa/data-survey/refs/heads/main/data/{base}.md" - ) - response = requests.get(data_survey) - if not response.ok: - self.warning( - f"Missing data survey {base}.md at {data_survey}. Falling back to converter declared properties." - ) - else: - return response.text - - @cache - def collect_meta_data(self, parquet_file, **kwargs): - base = self.dataset.replace("_", "-").upper() - data = { - "provider": self.converter.provider, - "license": self.converter.license, - "projection": "", - "homepage": "", - "submitter": "Fiboa project", - "header": "", - } - text = self.download_data_survey(base, **kwargs) - mapping = { - "data provider (legal entity)": "provider", - "submitter (affiliation)": "submitter", - } - properties = {} - if text: - data["header"] = ( - f"\n- **Data Survey:** https://github.com/fiboa/data-survey/blob/main/data/{base}.md" - ) - data.update( - { - mapping.get(a.lower(), a.lower()): b - for a, b in re.findall(r"- \*\*(.+?):\*\* (.+?)\n", text) - } - ) - properties = { - a.lower(): b.strip() - for a, b in re.findall(r"\n\|\s*(\w+)[^|]*\|[^|]*\|[^|]*\|([^|]*)\|", text) - } - try: - # Try read projection from parquet metadata - meta = create_encoding(parquet_file).get_geoparquet_metadata() - crs = meta["columns"]["geometry"]["crs"] - data["projection"] = f"{crs['id']['authority']}:{crs['id']['code']} ({crs['name']})" - except Exception: - pass - converted_by = kwargs.get("converted_by") - if converted_by: - data["submitter"] = converted_by - - assert data["provider"], "Cannot determine data provider from converter or data survey." - return data, properties - - def readme_attribute_table(self, stac_data, properties): - def description(name): - m = self.converter.columns - reverse = dict(zip(m.values(), m.keys())) - return ( - properties.get(reverse.get(name)) - or properties.get(name) - or DESCRIPTIONS.get(name, "") - ) - - cols = [["Property", "**Data Type**", "Description"]] + [ - [ - s["name"], - re.search(r"\w+", s["type"])[0], - description(s["name"]), - ] - for s in stac_data["assets"]["data"]["table:columns"] - if s["name"] not in ("geometry", "bbox", "collection") - ] - widths = [max(len(c[i]) for c in cols) for i in range(3)] - aligned_cols = [[f" {c:<{w}} " for c, w in zip(row, widths)] for row in cols] - aligned_cols.insert(1, ["-" * (w + 2) for w in widths]) - return "\n".join(["|" + "|".join(cols) + "|" for cols in aligned_cols]) - - def make_license(self, parquet_file, **kwargs): - text = "" - try: - data, properties = self.collect_meta_data(parquet_file, **kwargs) - text = data["license"] - if getattr(self.converter, "license") not in (None, "", data["license"]): - text += "\n" + self.converter.license + "\n" - - found = False - for _license in (data["license"], self.converter.license): - if not _license or "<(https://" in _license: - continue - - # Include full-license text - _license = _license.upper() - if _license in spdx_license_list.LICENSES: - response = requests.get( - f"https://raw.githubusercontent.com/spdx/license-list-data/refs/heads/main/text/{_license}.txt" - ) - if response.ok: - found = True - text += f"\n\n{response.text}\n" - break - if not found: - self.warning(f"License {text} could not be found in SPDX license list") - - except Exception as e: - self.exception(e) - return text - - def make_readme(self, parquet_file, file_name, stac, **kwargs): - version = Registry.get_version() - converter = self.converter - with open(stac) as f: - stac_data = json.load(f) - count = stac_data["assets"]["data"]["table:row_count"] - data, properties = self.collect_meta_data(parquet_file, **kwargs) - columns = self.readme_attribute_table(stac_data, properties) - urls = converter.get_urls() or "manually downloaded file" - urls = urls.keys() if isinstance(urls, dict) else [urls] - downloaded_urls = "\n".join([(" - " + url) for url in urls]) - - return f"""# Field boundaries for {converter.short_name} - -Provides {count} official field boundaries from {converter.short_name}. -It has been converted to a fiboa GeoParquet file from data obtained from {data["provider"]}. - -- **Source Data Provider:** [{data["provider"]}]({data["homepage"]}) -- **Converted by:** {data["submitter"]} -- **License:** {data["license"]} -- **Projection:** {data["projection"]}{data["header"]} - ---- - -- [Download the data as fiboa GeoParquet]({self.data_url}/{file_name}.parquet) -- [STAC Browser](https://radiantearth.github.io/stac-browser/#/external/data.source.coop/fiboa/data/{self.dataset}/stac/collection.json) -- [STAC Collection]({self.data_url}/stac/collection.json) -- [PMTiles]({self.data_url}/{file_name}.pmtiles) - -## Columns - -{columns} - -## Lineage - -- Data downloaded on {date.today()} from: -{downloaded_urls} -- Converted to GeoParquet using [fiboa-cli](https://github.com/fiboa/cli), version {version} -""" - @runnable def publish( self, target, - generate_meta=False, - yes=False, - data_survey_url=None, - editor=None, - converted_by=None, + pmtiles=True, + tippecanoe_opts=TIPPECANOE_DEFAULT_OPTS, **kwargs, ): """ - You need GDAL 3.8 or later (for ogr2ogr) with libgdal-arrow-parquet, tippecanoe, and AWS CLI + Creates the following files in the target folder: + + - [-].parquet: the converted and validated fiboa GeoParquet file + - [-].pmtiles: vector tiles for visualization (ogr2ogr + tippecanoe) + - collection.json: a STAC Collection with relative links to the files above + + Existing files are reused, delete them to regenerate. + PMTiles generation needs GDAL 3.8 or later (for ogr2ogr) and tippecanoe: - https://gdal.org/ - https://github.com/felt/tippecanoe - - https://aws.amazon.com/cli/ """ - Path(target).mkdir(parents=True, exist_ok=True) + target = Path(target) + target.mkdir(parents=True, exist_ok=True) file_name = self.dataset - if not kwargs["variant"] and self.converter.variants: + if not kwargs.get("variant") and self.converter.variants: kwargs["variant"] = next(iter(self.converter.variants)) - if kwargs["variant"]: + if kwargs.get("variant"): file_name += f"-{kwargs['variant']}" - parquet_file = Path(target) / f"{file_name}.parquet" - - has_write_access = bool( - os.getenv("AWS_ACCESS_KEY_ID") and os.getenv("AWS_SECRET_ACCESS_KEY") - ) + parquet_file = target / f"{file_name}.parquet" + pmtiles_file = target / f"{file_name}.pmtiles" + stac_file = target / "collection.json" - stac_file = Path(target) / "stac" / "collection.json" - - ## Create parquet file + # Create parquet file if not parquet_file.exists(): - self.info(f"Converting file for {self.dataset} to {parquet_file}") + self.info(f"Converting {self.dataset} to {parquet_file}") ConvertData(self.dataset).run(parquet_file, **kwargs) - self.success(f"Converted file for {self.dataset} to {parquet_file}") + self.success(f"Converted {self.dataset} to {parquet_file}") else: - self.success(f"Using existing file {parquet_file} for {self.dataset}") + self.success(f"Using existing file {parquet_file}") - ## Validate parquet file, we only want to publish valid files + # Validate parquet file, we only want to publish valid files self.info(f"Validating {parquet_file}") ValidateData().validate(parquet_file, num=-1) self.log("\n => VALID\n", "success") - ## Create STAC collection.json - self.create_stac_collection(target, file_name, parquet_file, stac_file) - - if generate_meta: - self.generate_meta( - target, - file_name, - stac_file, - data_survey_url=data_survey_url, - converted_by=converted_by, - yes=yes, - editor=editor, - ) - - self.generate_pmtiles(target, file_name, parquet_file) - if not has_write_access: - self.info("Get your credentials through the source coop organization.") - self.info("Login to AWS Console and generate an access key:") - self.info( - " - In AWS console, click on account (right top) press 'Security credentials'," - ) - self.info(" - Go to 'Access keys' and press 'Create access key'") - self.info( - " - Run `export AWS_ACCESS_KEY_ID=<> AWS_SECRET_ACCESS_KEY=<>`\n" - " (Linux/Mac only) where you copy-paste the access key and secret to <>.", - ) - self.error("Please set AWS_ environment variables for uploading") - return - self.upload_to_aws(target) - - def create_stac_collection(self, target, file_name, parquet_file, stac_file): - p_stac = Path(stac_file) - if p_stac.exists() and p_stac.stat().st_mtime >= Path(parquet_file).stat().st_mtime: + # Create PMTiles + if pmtiles: + self.generate_pmtiles(parquet_file, pmtiles_file, tippecanoe_opts) + has_pmtiles = pmtiles_file.exists() + + # Create STAC collection.json + self.create_stac_collection(parquet_file, pmtiles_file if has_pmtiles else None, stac_file) + self.success(f"Created {stac_file}") + return stac_file + + def create_stac_collection(self, parquet_file: Path, pmtiles_file, stac_file: Path): + is_current = ( + stac_file.exists() + and stac_file.stat().st_mtime >= parquet_file.stat().st_mtime + and (pmtiles_file is None or stac_file.stat().st_mtime >= pmtiles_file.stat().st_mtime) + ) + if is_current: + self.info(f"Reusing existing {stac_file}") return - self.success(f"Creating STAC collection.json for {parquet_file}") - p_stac.parent.mkdir(exist_ok=True) - CreateStacCollection().create_cli(parquet_file, stac_file) - - Path(target, "stac").mkdir(parents=True, exist_ok=True) - data = json.load(open(stac_file, "r")) - assert data["id"] == self.dataset, ( - f"Wrong collection dataset id: {data['id']} != {self.dataset}, for {stac_file}" + self.info(f"Creating STAC collection for {parquet_file}") + data = CreateStacCollection().create_from_file( + parquet_file, data_url=f"./{parquet_file.name}" ) + if data["id"] != self.dataset: + raise Exception( + f"Wrong collection id: {data['id']} != {self.dataset}, for {parquet_file}" + ) - data["assets"]["data"]["href"] = f"{self.data_url}/{file_name}.parquet" + extensions = data.setdefault("stac_extensions", []) + if FILE_EXTENSION not in extensions: + extensions.append(FILE_EXTENSION) - if STAC_EXTENSION not in data["stac_extensions"]: - data["stac_extensions"].append(STAC_EXTENSION) + asset = data["assets"]["data"] + asset["title"] = f"{data.get('title') or self.dataset} (GeoParquet)" + asset.update(self.file_metadata(parquet_file)) - if not any(d.get("rel") == "pmtiles" for d in data["links"]): + if pmtiles_file is not None: + if WEB_MAP_LINKS_EXTENSION not in extensions: + extensions.append(WEB_MAP_LINKS_EXTENSION) + data["links"] = [link for link in data.get("links", []) if link.get("rel") != "pmtiles"] data["links"].append( { - "href": f"{self.data_url}/{file_name}.pmtiles", - "type": "application/vnd.pmtiles", "rel": "pmtiles", + "href": f"./{pmtiles_file.name}", + "type": PMTILES_MEDIA_TYPE, + "title": "Web map tiles", + "pmtiles:layers": [self.dataset], } ) + data["assets"]["visual"] = { + "href": f"./{pmtiles_file.name}", + "type": PMTILES_MEDIA_TYPE, + "title": f"{data.get('title') or self.dataset} (PMTiles)", + "roles": ["visual"], + **self.file_metadata(pmtiles_file), + } - with open(stac_file, "w", encoding="utf-8") as f: + with stac_file.open("w", encoding="utf-8") as f: json.dump(data, f, indent=2) - def generate_meta(self, target, file_name, stac_file, **kwargs): - parquet_file = Path(target) / f"{file_name}.parquet" - for required in ("README.md", "LICENSE.txt"): - path = Path(target) / required - if not path.exists(): - self.warning(f"Missing {required}. Generating at {path}") - if required == "README.md": - text = self.make_readme( - parquet_file, - file_name=file_name, - stac=stac_file, - **kwargs, - ) - else: - text = self.make_license(parquet_file, **kwargs) - self.info( - f"\nGenerated the following file {required}:\n{'-' * 80}\n\n{text}\n{'-' * 80}\n" - ) - action = ( - "C" - if kwargs.get("yes") - else input("Do you want to Continue (C), Edit (E) or Abort (A)?") - ) - if action.lower() not in "ce": - self.warning("Bailing out") - sys.exit(1) - with open(path, "w") as f: - f.write(text) - editor = kwargs.get("editor") - if action.lower() == "e" and editor: - os.system(f"{editor} {path}") + @staticmethod + def file_metadata(path: Path) -> dict: + return { + "file:size": path.stat().st_size, + "file:checksum": multihash_sha256(path), + } - def generate_pmtiles(self, target, file_name, parquet_file): + def generate_pmtiles(self, parquet_file: Path, pmtiles_file: Path, tippecanoe_opts: str): if is_windows: self.warning( "PMTiles generation through tippecanoe is not supported on Windows, skipping." ) return + if pmtiles_file.exists(): + self.success(f"Using existing file {pmtiles_file}") + return - pm_file = Path(target) / f"{file_name}.pmtiles" - if not pm_file.exists(): - self.info("Running ogr2ogr | tippecanoe") - self.check_command("tippecanoe") - self.check_command("ogr2ogr", name="GDAL") - self.exc( - f"ogr2ogr -t_srs EPSG:4326 -f geojson /vsistdout/ {str(parquet_file)} | tippecanoe -zg --projection=EPSG:4326 -o {str(pm_file)} -l {self.dataset} --drop-densest-as-needed" - ) - - def upload_to_aws(self, target): - self.info("Uploading to aws") - - self.check_command("aws") - self.exc(f"aws s3 sync --exclude '.*' {target} {self.s3_upload_path}") + self.check_command("tippecanoe") + self.check_command("ogr2ogr", name="GDAL") + self.info("Running ogr2ogr | tippecanoe") + ogr = subprocess.Popen( + [ + "ogr2ogr", + "-t_srs", + "EPSG:4326", + "-f", + "GeoJSONSeq", + "/vsistdout/", + str(parquet_file), + ], + stdout=subprocess.PIPE, + ) + tippecanoe = subprocess.run( + [ + "tippecanoe", + *tippecanoe_opts.split(), + "--projection=EPSG:4326", + "-o", + str(pmtiles_file), + "-l", + self.dataset, + ], + stdin=ogr.stdout, + ) + ogr.stdout.close() + ogr.wait() + if ogr.returncode != 0 or tippecanoe.returncode != 0: + pmtiles_file.unlink(missing_ok=True) + raise Exception("PMTiles generation failed, see output above.") + self.success(f"Created {pmtiles_file}") diff --git a/pixi.lock b/pixi.lock index 1f6b2a69..9689ee5e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -804,7 +804,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -940,7 +939,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1077,7 +1075,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1213,7 +1210,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1327,7 +1323,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1426,7 +1421,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1526,7 +1520,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1623,7 +1616,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -2803,10 +2795,9 @@ packages: - pypi: ./ name: fiboa-cli version: 0.21.0 - sha256: 5a8a4c3d9234870ac9a54c0cc925c41f59ca033ee111dd241c1af9633591f47b + sha256: 5b0a002f53b5ef4abfb8c3f5f429dc374fa4a4c9e0ef0fe34f6e13e3cce989da requires_dist: - vecorel-cli==0.2.15 - - spdx-license-list==3.27.0 requires_python: '>=3.11' editable: true - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda diff --git a/pyproject.toml b/pyproject.toml index 735559ce..66099f37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ classifiers = [ requires-python = ">=3.11" dependencies = [ "vecorel-cli==0.2.15", - "spdx-license-list==3.27.0", ] [project.scripts] @@ -62,6 +61,7 @@ ruff = "==0.12.8" [tool.pixi.feature.dev.pypi-dependencies] build = ">=1.0.0,<2.0.0" +spdx-license-list = "==3.27.0" [tool.pixi.feature.cloud.dependencies] s3fs = "==2025.7.0" diff --git a/tests/data-files/publish/BE-VLG-survey.md b/tests/data-files/publish/BE-VLG-survey.md deleted file mode 100644 index 9ac80ea3..00000000 --- a/tests/data-files/publish/BE-VLG-survey.md +++ /dev/null @@ -1,78 +0,0 @@ -# Vlaanderen, Belgium - -## Submission Details - -- **Submitter (Affiliation):** Matthias Mohr -- **Data Provider (Legal Entity):** Agriculture and Marine Fisheries Agency of the Flemish government (Government) -- **Homepage:** https://landbouwcijfers.vlaanderen.be/open-geodata-landbouwgebruikspercelen -- **Alternative URL:** https://www.vlaanderen.be/datavindplaats/catalogus/landbouwgebruikspercelen-lv-2022 - -## Overview - -Since 2020, the Department of Agriculture and Fisheries has been publishing a more extensive set of data related to agricultural use plots (from the 2008 campaign). - -From 2023, the downloadable dataset of agricultural use plots will also include the specialization given by the company (= company typology) and that is given to the plots of the company. Based on the typology, the companies are divided into 4 major specializations: arable farming, horticulture, livestock farming and mixed farms. The specialization of each company is calculated annually according to a European method and is based on the standard output of the various agricultural productions on the company. It is therefore an economic specialization and not a reflection of all agricultural production on the company. - -## Data & Metadata - -- **URL:** https://landbouwcijfers.vlaanderen.be/open-geodata-landbouwgebruikspercelen -- **Documentation:** contained in the ZIP packages -- **File Format:** GeoPackage / Shapefile -- **Projection:** EPSG:31370 (Belgian Lambert 72) -- **License:** CC-0 (described as "Publiek" and "Toegang zonder voorwaarden") - -### Properties - -Some of the documented fields are missing in the GeoPackage. These are marked with "(missing)". - -| Property | **Data Type** | Constraints | Description | -|-----------------------|---------------|----------------------------|----------------------------------------------------------------------------------------------| -| fid | integer | | Identifier | -| BT_OMSCH | string | 200 chars | Business type (economic specialization) | -| BT_BRON | string | 50 chars | Source of the business type (year of calculation or specialization indicated) | -| GRAF_OPP | number | | Area (ha, accurate to 1m²) | -| REF_ID | integer | | Unique identification number for the field. | -| GWSCOD_V | string | 5 chars (digits), nullable | Pre-cultivation code | -| GWSNAM_V | string | 90 chars, nullable | Pre-cultivation name | -| GWSCOD_H | string | 5 chars (digits), nullable | Main cultivation/crop code | -| GWSNAM_H | string | 90 chars, nullable | Main cultivation/crop name | -| GWSGRPH_LB | string | 150 chars, nullable | Main cultivation/crop group name | -| GWSCOD_N | string | 5 chars (digits), nullable | First cultivation/crop code | -| CWSNAM_N | string | 90 chars, nullable | First cultivation/crop name | -| GWSCOD_N2 | string | 5 chars (digits), nullable | Second cultivation/crop code | -| GWSNAM_N2 | string | 90 chars, nullable | Second cultivation/crop name | -| AMKM (missing) | string | | Agri-environment code | -| AMKM_LB (missing) | string | | Agri-environment name | -| ECOREGELING (missing) | string | | Eco-regulation code | -| ECOR_LB (missing) | string | | Eco-regulation name | -| BLS (missing) | string | | Planting subsidy code (forest farming systems) | -| BLS_LB (missing) | string | | Planting subsidy name (forest farming systems) | -| GESP_PM | string | 11 chars, nullable | Specialized production method | -| GESP_PM_LB | string | 150 chars, nullable | Description of specialized production method | -| BIOCERT (missing) | string | `J` or `N` | Plot under bio-control with a bio-control body. | -| ERO_NAM | string | 20 chars, | Erosion color code for the field | -| STAT_BGV | string | 2 chars, nullable | Status Permanent Grassland under greening (BG) | -| MEERJARIG_GRASLAND | string | | Status Perennial Grassland (MG6 or higher). Example: MG16 = 16th year grassland | -| LANDBSTR | string | 2 chars, nullable | Agricultural region in which the center of the field is located | -| STAT_AAR | string | 10 chars, nullable | Status Potatoes, follow up rotation duty | -| PCT_EKBG | string | 10 chars, nullable | Percentage range of field that is ecologically sensitive permanent pasture. Example: `0-10%` | -| PCT_WETVEEN | string | 10 chars, nullable | Percentage range of field that is wetland and/or peatland. Example: `0-10%` | -| PRC_GEM | string | 30 chars | Municipality in which the center of the field is located | -| PRC_NIS | string | 5 chars (digits) | NIS code of the municipality in which the center of the field is located | -| X_REF | number | | X coordinate of the center of the field (Lambert) | -| Y_REF | number | | Y coordinate of the center of the field (Lambert) | -| WGS84_LG | string | 11 chars | Longitude of the center of the field (WGS84). Example: `3°21'44"` | -| WGS84_BG | string | 11 chars | Latitude of the center of the field (WGS84). Example: `51°11'39"` | - -Note: Many integer-like numbers are encoded as strings. - -## API - -The open data viewer https://geopunt.be/ shows the data in a viewer (search term: landbouwgebruikspercelen) -See https://www.vlaanderen.be/datavindplaats/catalogus/landbouwgebruikspercelen-lv-2022 for more info - -| Standard | URL | Documentation | -|--------------|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------| -| OGC WFS | https://geo.api.vlaanderen.be/Landbgebrperc/wfs | https://www.vlaanderen.be/datavindplaats/catalogus/wfs-landbouwgebruikspercelen | -| OGC Features | https://geo.api.vlaanderen.be/Landbgebrperc/ogc/features/v1 | https://metadata.vlaanderen.be/srv/dut/catalog.search#/metadata/01f408db-df8a-49a2-8ce4-0f66b8efe17b | -| OGC WMS | https://geo.api.vlaanderen.be/ALV/wms | https://www.vlaanderen.be/datavindplaats/catalogus/wms-departement-landbouw-en-visserij | diff --git a/tests/test_publish.py b/tests/test_publish.py index cf960760..021cb5ef 100644 --- a/tests/test_publish.py +++ b/tests/test_publish.py @@ -1,30 +1,43 @@ -import responses +import json from fiboa_cli.publish import Publish class PublishTest(Publish): - def generate_pmtiles(self, target, file_name, parquet_file): - pass + def generate_pmtiles(self, parquet_file, pmtiles_file, tippecanoe_opts): + # tippecanoe is not available everywhere, fake the tiles + pmtiles_file.write_bytes(b"PMTiles") - def upload_to_aws(self, target): - pass - -@responses.activate def test_publish(tmp_folder): converter = "be_vlg" - base = "BE-VLG" path = f"tests/data-files/convert/{converter}" - rsp1 = responses.Response( - method="GET", - url=f"https://raw.githubusercontent.com/fiboa/data-survey/refs/heads/main/data/{base}.md", - body=open(f"tests/data-files/publish/{base}-survey.md").read(), - ) - responses.add(rsp1) - PublishTest(converter).run( - variant="2023", target=tmp_folder, cache=path, generate_meta=True, yes=True - ) + PublishTest(converter).run(variant="2023", target=tmp_folder, cache=path) + files = [f.name for f in tmp_folder.iterdir() if f.is_file()] - for f in ("README.md", "LICENSE.txt", "be_vlg-2023.parquet"): + for f in ("collection.json", "be_vlg-2023.parquet", "be_vlg-2023.pmtiles"): assert f in files, f"Missing file {f}" + + with open(tmp_folder / "collection.json") as f: + stac = json.load(f) + + assert stac["id"] == converter + assert "https://stac-extensions.github.io/file/v2.1.0/schema.json" in stac["stac_extensions"] + assert ( + "https://stac-extensions.github.io/web-map-links/v1.3.0/schema.json" + in stac["stac_extensions"] + ) + + data = stac["assets"]["data"] + assert data["href"] == "./be_vlg-2023.parquet" + assert data["file:size"] == (tmp_folder / "be_vlg-2023.parquet").stat().st_size + assert data["file:checksum"].startswith("1220") and len(data["file:checksum"]) == 68 + + visual = stac["assets"]["visual"] + assert visual["href"] == "./be_vlg-2023.pmtiles" + assert visual["roles"] == ["visual"] + assert visual["file:size"] == 7 + + pmtiles = next(link for link in stac["links"] if link["rel"] == "pmtiles") + assert pmtiles["href"] == "./be_vlg-2023.pmtiles" + assert pmtiles["pmtiles:layers"] == [converter] From f8944b431f9534bc76e0626d32fb4603de6f9d71 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 22:33:25 +0200 Subject: [PATCH 25/94] NL: new PDOK download location, 2026 concept edition; DE-TH: INSPIRE note Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 2 ++ fiboa_cli/datasets/de_th.py | 2 ++ fiboa_cli/datasets/nl.py | 10 ++++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a80ad1e..986925f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) +- NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition +- DE-TH: note the INSPIRE download service - BE-VLG: drop plots without a crop code (one such plot in the 2023 edition made validation fail); derive determination:datetime from the variant year instead of a constant date - `fiboa publish` no longer uploads to S3 or generates README/LICENSE files. It creates GeoParquet, PMTiles and a STAC Collection with relative links, `file:size`/`file:checksum` and a web-map-links v1.3.0 `pmtiles` link. Publishing is done by catalogs such as the [harmonized field data catalog](https://github.com/fieldsoftheworld/harmonized-field-data-catalog). - Add Italy Tuscany (IT-1) basd on EuroCrops v2 diff --git a/fiboa_cli/datasets/de_th.py b/fiboa_cli/datasets/de_th.py index d272b1d9..215bda13 100644 --- a/fiboa_cli/datasets/de_th.py +++ b/fiboa_cli/datasets/de_th.py @@ -8,6 +8,8 @@ class Converter(AdminConverterMixin, FiboaBaseConverter): sources = "https://www.geoproxy.geoportal-th.de/download-service/opendata/agrar/DGK_Thue.zip" + # https://www.geoproxy.geoportal-th.de/inspire-dl/ + # http://www.geoproxy.geoportal-th.de/inspire-dl/atom/DataSet/DataSet_06cd3e2f-ed4a-4507-b5e7-14973d4d6968.xml id = "de_th" admin_subdivision_code = "TH" diff --git a/fiboa_cli/datasets/nl.py b/fiboa_cli/datasets/nl.py index 625106ba..5388b7d0 100644 --- a/fiboa_cli/datasets/nl.py +++ b/fiboa_cli/datasets/nl.py @@ -4,13 +4,15 @@ from ..conversion.fiboa_converter import FiboaBaseConverter from .commons.hcat import AddHCATMixin -# see https://service.pdok.nl/rvo/brpgewaspercelen/atom/v1_0/basisregistratie_gewaspercelen_brp.xml -base = "https://service.pdok.nl/rvo/brpgewaspercelen/atom/v1_0/downloads" +# see https://service.pdok.nl/rvo/gewaspercelen/atom/basisregistratie_gewaspercelen_brp.xml +# (the old feed rvo/brpgewaspercelen/atom/v1_0/ redirects here since 2026) +base = "https://service.pdok.nl/rvo/gewaspercelen/atom/downloads" class NLCropConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): area_calculate_missing = True variants = { + "2026": f"{base}/gewaspercelen_concept_2026.gpkg", **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.gpkg" for y in range(2025, 2020, -1)}, **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.zip" for y in range(2020, 2009, -1)}, } @@ -27,9 +29,9 @@ class NLCropConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): A dataset is generated for each year with reference date May 15. A view service and a download service are available for the most recent BRP crop plots. - + -Data is currently available for the years 2009 to 2024. +Data is currently available for the years 2009 to 2025 (final) and 2026 (concept). """ provider = ( From 3b8d8f1cff7552b8f43cfffe8d66976cb4238b74 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 22:38:28 +0200 Subject: [PATCH 26/94] Declare beautifulsoup4 (used by es_pv, es_vc) Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + pixi.lock | 53 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 986925f5..54f16710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) +- Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition - DE-TH: note the INSPIRE download service - BE-VLG: drop plots without a crop code (one such plot in the 2023 edition made validation fail); derive determination:datetime from the variant year instead of a constant date diff --git a/pixi.lock b/pixi.lock index 9689ee5e..e870f765 100644 --- a/pixi.lock +++ b/pixi.lock @@ -140,6 +140,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -169,6 +170,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -301,6 +303,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -330,6 +333,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -463,6 +467,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -492,6 +497,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -623,6 +629,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -652,6 +659,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -777,6 +785,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/72/0b6035302e9c33f004240a50cb6e2e1fc7bb1f2b415b02d939c551bdd06b/inflate64-1.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -804,6 +813,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -912,6 +922,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/80/24ba0d2ee14e07e275e9c5b058e59a8a58f8ef42dd51a78ebbfd7c857ac4/inflate64-1.0.4-cp314-cp314-macosx_10_15_x86_64.whl @@ -939,6 +950,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1048,6 +1060,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/b8/073a79716e093db973b8823bdfb02e10fbdf65642dbe1fa3cda24832aeb2/inflate64-1.0.4-cp314-cp314-macosx_11_0_arm64.whl @@ -1075,6 +1088,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1183,6 +1197,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dd/51/f2972df8cceecc9bf3afa3353d517ffc7125285198c844588e9aaf98f5d0/inflate64-1.0.4-cp314-cp314-win_amd64.whl @@ -1210,6 +1225,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1296,6 +1312,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/72/0b6035302e9c33f004240a50cb6e2e1fc7bb1f2b415b02d939c551bdd06b/inflate64-1.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -1323,6 +1340,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1394,6 +1412,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/80/24ba0d2ee14e07e275e9c5b058e59a8a58f8ef42dd51a78ebbfd7c857ac4/inflate64-1.0.4-cp314-cp314-macosx_10_15_x86_64.whl @@ -1421,6 +1440,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1493,6 +1513,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/b8/073a79716e093db973b8823bdfb02e10fbdf65642dbe1fa3cda24832aeb2/inflate64-1.0.4-cp314-cp314-macosx_11_0_arm64.whl @@ -1520,6 +1541,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1589,6 +1611,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dd/51/f2972df8cceecc9bf3afa3353d517ffc7125285198c844588e9aaf98f5d0/inflate64-1.0.4-cp314-cp314-win_amd64.whl @@ -1616,6 +1639,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl @@ -1727,6 +1751,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -1756,6 +1781,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -1853,6 +1879,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -1882,6 +1909,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -1980,6 +2008,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -2009,6 +2038,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -2103,6 +2133,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/e4/fac19dc34cb686c96011388b813ff7b858a70681e5ce6ce7698e5021b0f4/geopandas-1.1.2-py3-none-any.whl @@ -2132,6 +2163,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl @@ -2285,6 +2317,19 @@ packages: purls: [] size: 7514 timestamp: 1767044983590 +- pypi: https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl + name: beautifulsoup4 + version: 4.15.0 + sha256: d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9 + requires_dist: + - soupsieve>=1.6.1 + - typing-extensions>=4.0.0 + - cchardet ; extra == 'cchardet' + - chardet ; extra == 'chardet' + - charset-normalizer ; extra == 'charset-normalizer' + - html5lib ; extra == 'html5lib' + - lxml ; extra == 'lxml' + requires_python: '>=3.7.0' - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda sha256: f7efd22b5c15b400ed84a996d777b6327e5c402e79e3c534a7e086236f1eb2dc md5: 42834439227a4551b939beeeb8a4b085 @@ -2795,9 +2840,10 @@ packages: - pypi: ./ name: fiboa-cli version: 0.21.0 - sha256: 5b0a002f53b5ef4abfb8c3f5f429dc374fa4a4c9e0ef0fe34f6e13e3cce989da + sha256: 15c4c23b3c81265e8c7ee326e01ae3f44762998d0b463a66752ce2f5683b10d2 requires_dist: - vecorel-cli==0.2.15 + - beautifulsoup4>=4.12 requires_python: '>=3.11' editable: true - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.21.2-pyhd8ed1ab_0.conda @@ -6724,6 +6770,11 @@ packages: - pkg:pypi/six?source=hash-mapping size: 18455 timestamp: 1753199211006 +- pypi: https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl + name: soupsieve + version: 2.9.2 + sha256: 8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/6e/d5/6fbc5770fc55e027dbd24571c4fd0b4ad6f2e310adbbda95ec39993f344c/spdx_license_list-3.27.0-py3-none-any.whl name: spdx-license-list version: 3.27.0 diff --git a/pyproject.toml b/pyproject.toml index 66099f37..19c52c92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ classifiers = [ requires-python = ">=3.11" dependencies = [ "vecorel-cli==0.2.15", + "beautifulsoup4>=4.12", ] [project.scripts] From d22b6030530d6382faac4ccce520e74a539a83a8 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 22:44:57 +0200 Subject: [PATCH 27/94] Drop rows without a required crop:code instead of failing the conversion Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 3 ++- fiboa_cli/conversion/fiboa_converter.py | 10 ++++++++++ fiboa_cli/datasets/be_vlg.py | 5 ----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54f16710..39ddecfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition - DE-TH: note the INSPIRE download service -- BE-VLG: drop plots without a crop code (one such plot in the 2023 edition made validation fail); derive determination:datetime from the variant year instead of a constant date +- Drop rows without a crop:code (required by the crop extension) with a warning instead of failing the conversion (BE-VLG 2023, ES-CN had one such row each) +- BE-VLG: derive determination:datetime from the variant year instead of a constant date - `fiboa publish` no longer uploads to S3 or generates README/LICENSE files. It creates GeoParquet, PMTiles and a STAC Collection with relative links, `file:size`/`file:checksum` and a web-map-links v1.3.0 `pmtiles` link. Publishing is done by catalogs such as the [harmonized field data catalog](https://github.com/fieldsoftheworld/harmonized-field-data-catalog). - Add Italy Tuscany (IT-1) basd on EuroCrops v2 - Suuport multiple years for CZ diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index a073b0e5..53aa2e54 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -4,6 +4,9 @@ from ..fiboa.version import get_fiboa_uri AREA_KEY = "metrics:area" +# Properties that a schema requires to be non-null; rows lacking them cannot +# validate, so they are dropped (with a warning) rather than failing the run. +REQUIRED_NON_NULL = ("crop:code",) class FiboaBaseConverter(BaseConverter): @@ -22,6 +25,13 @@ def __init__(self, *args, **kwargs): def post_migrate(self, gdf): gdf = super().post_migrate(gdf) + for key in REQUIRED_NON_NULL: + if key in gdf.columns: + nulls = gdf[key].isna() + if nulls.any(): + self.warning(f"Dropping {int(nulls.sum())} rows without a value for {key}") + gdf = gdf[~nulls] + gdf_area_key = next((k for k, v in self.columns.items() if v == AREA_KEY), None) if self.area_calculate_missing: # If CRS is not in meters, reproject to an equal-area projection for area calculation diff --git a/fiboa_cli/datasets/be_vlg.py b/fiboa_cli/datasets/be_vlg.py index b8dc6d9a..c33e2282 100644 --- a/fiboa_cli/datasets/be_vlg.py +++ b/fiboa_cli/datasets/be_vlg.py @@ -46,11 +46,6 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): # Each edition is the campaign year of its variant; the old constant # "2024-03-28" was the extraction date of one edition applied to all of them. use_variant_as_determination = True - column_filters = { - # A handful of plots (e.g. one in 2023, typology "Niet-geclassificeerd") carry no - # crop code; crop:code is required by the crop extension, so drop them. - "GWSCOD_H": lambda col: col.notna(), - } ec_mapping_csv = "be_vlg_2021.csv" missing_schemas = {"properties": {"typology": {"type": "string"}}} From b92251a8cbb7d6e5ec435de917936b00f8ffbaca Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 22:46:25 +0200 Subject: [PATCH 28/94] Look up the source column when dropping rows without a crop:code Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/fiboa_converter.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index 53aa2e54..0ff5cfd5 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -25,12 +25,17 @@ def __init__(self, *args, **kwargs): def post_migrate(self, gdf): gdf = super().post_migrate(gdf) + # post_migrate runs before columns are renamed, so look up the source column for key in REQUIRED_NON_NULL: - if key in gdf.columns: - nulls = gdf[key].isna() - if nulls.any(): - self.warning(f"Dropping {int(nulls.sum())} rows without a value for {key}") - gdf = gdf[~nulls] + for src, dst in self.columns.items(): + targets = dst if isinstance(dst, (list, tuple)) else [dst] + if key in targets and src in gdf.columns: + nulls = gdf[src].isna() + if nulls.any(): + self.warning( + f"Dropping {int(nulls.sum())} rows without a value for {key} ({src})" + ) + gdf = gdf[~nulls] gdf_area_key = next((k for k, v in self.columns.items() if v == AREA_KEY), None) if self.area_calculate_missing: From a4afe8801f28757c811a5ef9ed15b1b79ae4fb8c Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 22:47:48 +0200 Subject: [PATCH 29/94] DE-BB: read the shapefile as cp1252 Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/de_bb.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39ddecfa..3ce2f587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import +- DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition - DE-TH: note the INSPIRE download service - Drop rows without a crop:code (required by the crop extension) with a warning instead of failing the conversion (BE-VLG 2023, ES-CN had one such row each) diff --git a/fiboa_cli/datasets/de_bb.py b/fiboa_cli/datasets/de_bb.py index 2294bcc0..c25d734d 100644 --- a/fiboa_cli/datasets/de_bb.py +++ b/fiboa_cli/datasets/de_bb.py @@ -14,6 +14,8 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): license = "DL-DE-BY-2.0" provider = "Land Brandenburg " ec_mapping_csv = "de.csv" + # The .cpg claims UTF-8 but the DBF is cp1252 (June 2026 download) + open_options = dict(encoding="cp1252") columns = { "geometry": "geometry", From 53204af984a6960d5714110cf3409c2cc957d397 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:05:57 +0200 Subject: [PATCH 30/94] CZ: find the shapefile in nested archive folders Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/cz.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ce2f587..6817e622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import +- CZ: find the shapefile in nested archive folders (2026) - DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition - DE-TH: note the INSPIRE download service diff --git a/fiboa_cli/datasets/cz.py b/fiboa_cli/datasets/cz.py index e02f7f69..ad04aafb 100644 --- a/fiboa_cli/datasets/cz.py +++ b/fiboa_cli/datasets/cz.py @@ -20,7 +20,8 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): # see https://mze.gov.cz/public/app/eagriapp/lpisdata/ - variants = {str(k): {BASE.format(v): ["*.shp"]} for k, v in ITEMS.items()} + # the 2026 archive nests the shapefile in a folder, older ones are flat + variants = {str(k): {BASE.format(v): ["**/*.shp"]} for k, v in ITEMS.items()} id = "cz" short_name = "Czech" title = "Field boundaries for Czech" From 33d5ffd6b03b453eaa682c1d796507dc9b56ca85 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:11:58 +0200 Subject: [PATCH 31/94] ES-CAT: the 2024 download is a shapefile package Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/es_cat.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6817e622..ca263053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import +- ES-CAT: the 2024 download is a shapefile package, not a GeoPackage - CZ: find the shapefile in nested archive folders (2026) - DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition diff --git a/fiboa_cli/datasets/es_cat.py b/fiboa_cli/datasets/es_cat.py index 1987bcfe..11fa36c5 100644 --- a/fiboa_cli/datasets/es_cat.py +++ b/fiboa_cli/datasets/es_cat.py @@ -9,7 +9,7 @@ class ESCatConverter(FiboaBaseConverter): variants = { "2024": { "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/d90f5fca-ddd8-405d-a0d5-90609985e98e?download=true&filename=Cultius_DUN2024_SHP.zip": [ - "Cultius_DUN2024_GPKG/CULTIUS_DUN2024.gpkg" + "Cultius_DUN2024_SHP/Cultius_DUN2024_SHP.shp" ] }, "2023": { @@ -66,7 +66,7 @@ def layer_filter(self, layer, uri): def migrate(self, gdf): # In 2023 gpkg, names are lowercase. But in 2022 shapefile, case is mixed - to_lower = {k: k.lower() for k in gdf.columns if k != k.lower} + to_lower = {k: k.lower() for k in gdf.columns if k != k.lower()} if to_lower: gdf.rename(columns=to_lower, inplace=True) From 4eeff3033c416d82d4af4f9b9d854e660c07eef6 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:13:39 +0200 Subject: [PATCH 32/94] SK: row index as id, KODKD block code as block_id Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/sk.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca263053..b9a9991f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import +- SK: KODKD is the (non-unique, sometimes empty) LPIS block code, keep it as block_id and use the row index as id - ES-CAT: the 2024 download is a shapefile package, not a GeoPackage - CZ: find the shapefile in nested archive folders (2026) - DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) diff --git a/fiboa_cli/datasets/sk.py b/fiboa_cli/datasets/sk.py index 71b78891..6fa84b3e 100644 --- a/fiboa_cli/datasets/sk.py +++ b/fiboa_cli/datasets/sk.py @@ -27,9 +27,12 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): provider = "Pôdohospodárska platobná agentúra " license = "CC0-1.0" # "Open Data" ec_mapping_csv = "https://fiboa.org/code/sk/sk.csv" + # KODKD is the LPIS block code, shared by several fields and sometimes empty; + # the row index is the field id and the code is kept as block_id. + index_as_id = True columns = { "geometry": "geometry", - "KODKD": "id", + "KODKD": "block_id", "PLODINA": "crop:name", "KULTURA_NA": "crop_group", "LOKALITA_N": "municipality", @@ -37,6 +40,7 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): } missing_schemas = { "properties": { + "block_id": {"type": "string"}, "crop_group": {"type": "string"}, "municipality": {"type": "string"}, } From 077238eb40cbb42d3d076ec77bd0ef8ae988756d Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:14:15 +0200 Subject: [PATCH 33/94] ES-CAT: map the 34 crop names new in the 2024 edition Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 2 +- fiboa_cli/datasets/data-files/es_cat.csv | 34 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a9991f..c89da358 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import - SK: KODKD is the (non-unique, sometimes empty) LPIS block code, keep it as block_id and use the row index as id -- ES-CAT: the 2024 download is a shapefile package, not a GeoPackage +- ES-CAT: the 2024 download is a shapefile package, not a GeoPackage; 34 crop names new in 2024 added to the mapping - CZ: find the shapefile in nested archive folders (2026) - DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition diff --git a/fiboa_cli/datasets/data-files/es_cat.csv b/fiboa_cli/datasets/data-files/es_cat.csv index c2b8225c..435cc08b 100644 --- a/fiboa_cli/datasets/data-files/es_cat.csv +++ b/fiboa_cli/datasets/data-files/es_cat.csv @@ -297,3 +297,37 @@ original_code,original_name,translated_name 310,MANDARINER,MANDARIN 311,'ALGARROBA' HERBÀCIA,ALGARROBA 312,ESPÍGOL O LAVANDA,LAVENDER +313,ALOE VERA,ALOE VERA +314,ANTARA VINYA,VINEYARD INTER-ROW +315,"ARANYONER, PRUNYONER",BLACKTHORN (SLOE) +316,"BITXO, VITXO",CHILI PEPPER +317,CIBULET,CHIVES +318,CIVADA I BLAT,OATS AND WHEAT +319,CIVADA I ORDI,OATS AND BARLEY +320,CIVADA I TRITICALE,OATS AND TRITICALE +321,COL KALE,KALE +322,CROTALÀRIA,CROTALARIA (SUNN HEMP) +323,ERBS I CIVADA,BITTER VETCH AND OATS +324,ESPELTA PETITA,EINKORN +325,GINJOLER,JUJUBE +326,LAVANDA X ESPIGOL,LAVANDIN +327,LOT CORNICULAT,BIRD'S-FOOT TREFOIL +328,MALVA,MALLOW +329,MARIALLUÏSA,LEMON VERBENA +330,"MARXANT, BLET",AMARANTH +331,"MENTA VERDA, HERBA DE SANTA MARIA",SPEARMINT +332,"MILL ITALIÀ, CUA DE GUILLA",FOXTAIL MILLET +333,MONGETA VERMELLA,RED KIDNEY BEAN +334,MORER,MULBERRY +335,"NYÀMERA, PATATA DE CANYA",JERUSALEM ARTICHOKE +336,OKRA,OKRA +337,PASSACAMINS,KNOTGRASS +338,"PISANA, ESPELTA BESSONA",EMMER +339,"RAVE PICANT, RAVE RUSTICÀ",HORSERADISH +340,RUIBARBRE,RHUBARB +341,SULLA O ENCLOVA,SULLA (FRENCH HONEYSUCKLE) +342,TARONGER AGRE,BITTER ORANGE TREE +343,VEÇA I RAIGRÀS,VETCH AND RYEGRASS +344,VIMETERA,OSIER WILLOW +345,VIVER - PRODUCTOR MVR,NURSERY (MVR PRODUCER) +346,XIRIMOIER,CHERIMOYA TREE From c4466ce61d94437b14c92563183f350aa7bed454 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:15:46 +0200 Subject: [PATCH 34/94] ES-MD: find RECINTO.shp wherever the archive puts it Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/es_md.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c89da358..ff0aed13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import +- ES-MD: the archive no longer nests RECINTO.shp in a folder - SK: KODKD is the (non-unique, sometimes empty) LPIS block code, keep it as block_id and use the row index as id - ES-CAT: the 2024 download is a shapefile package, not a GeoPackage; 34 crop names new in 2024 added to the mapping - CZ: find the shapefile in nested archive folders (2026) diff --git a/fiboa_cli/datasets/es_md.py b/fiboa_cli/datasets/es_md.py index a69fd6f2..4863e29a 100644 --- a/fiboa_cli/datasets/es_md.py +++ b/fiboa_cli/datasets/es_md.py @@ -4,7 +4,7 @@ class ESCLConverter(ESBaseConverter): sources = { "https://idem.comunidad.madrid/recursos_cat_geo/Catalogo/recursos/UsoDelSuelo/spacm_sigpac.cm.zip": [ - "2024_SIGPAC_shape_toda_la_com/RECINTO.shp" + "**/RECINTO.shp" ] } id = "es_md" From a43baf50fa524f0524cac02ee10a442fecd9bb33 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:29:16 +0200 Subject: [PATCH 35/94] EE: explicit cache names for the WFS responses Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/ee.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0aed13..4d446321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix `use_variant_as_determination`: the determination:datetime column was dropped again because it was not listed in `columns` (affected DK, HR) - Declare the beautifulsoup4 dependency that the ES-PV and ES-VC converters import +- EE: name the cached WFS responses (ee_gsaa_.gml) - ES-MD: the archive no longer nests RECINTO.shp in a folder - SK: KODKD is the (non-unique, sometimes empty) LPIS block code, keep it as block_id and use the row index as id - ES-CAT: the 2024 download is a shapefile package, not a GeoPackage; 34 crop names new in 2024 added to the mapping diff --git a/fiboa_cli/datasets/ee.py b/fiboa_cli/datasets/ee.py index 777e827b..1ec6d66b 100644 --- a/fiboa_cli/datasets/ee.py +++ b/fiboa_cli/datasets/ee.py @@ -14,10 +14,11 @@ class Convert(AddHCATMixin, FiboaBaseConverter): + # explicit cache names: the WFS URL has no usable file name variants = { - str( - year - ): f"https://kls.pria.ee/geoserver/inspire_gsaa/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=inspire_gsaa:LU.GSAA.AGRICULTURAL_PARCELS_{year}&propertyName={ATTRIBUTES}" + str(year): { + f"https://kls.pria.ee/geoserver/inspire_gsaa/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=inspire_gsaa:LU.GSAA.AGRICULTURAL_PARCELS_{year}&propertyName={ATTRIBUTES}": f"ee_gsaa_{year}.gml" + } for year in range(2024, 2009, -1) } ec_mapping_csv = "https://fiboa.org/code/ee/ee.csv" From d5287db0c01bdd28646859e25e665754986d7e83 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 22 Aug 2026 23:30:48 +0200 Subject: [PATCH 36/94] Guard the crop:code row drop (>1% is an error); Europe-LAND: crop_name as code when crop_code is empty Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 3 ++- fiboa_cli/conversion/fiboa_converter.py | 8 ++++++++ fiboa_cli/datasets/commons/euro_land.py | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d446321..aa0d4850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) - NL: new PDOK download location (rvo/gewaspercelen/atom), add the 2026 concept edition - DE-TH: note the INSPIRE download service -- Drop rows without a crop:code (required by the crop extension) with a warning instead of failing the conversion (BE-VLG 2023, ES-CN had one such row each) +- Drop rows without a crop:code (required by the crop extension) with a warning instead of failing the conversion (BE-VLG 2023, ES-CN had one such row each); more than 1% missing is an error +- Europe-LAND converters: use crop_name as crop:code when the file's crop_code column is empty (LT 2024) - BE-VLG: derive determination:datetime from the variant year instead of a constant date - `fiboa publish` no longer uploads to S3 or generates README/LICENSE files. It creates GeoParquet, PMTiles and a STAC Collection with relative links, `file:size`/`file:checksum` and a web-map-links v1.3.0 `pmtiles` link. Publishing is done by catalogs such as the [harmonized field data catalog](https://github.com/fieldsoftheworld/harmonized-field-data-catalog). - Add Italy Tuscany (IT-1) basd on EuroCrops v2 diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index 0ff5cfd5..43e3870d 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -13,6 +13,8 @@ class FiboaBaseConverter(BaseConverter): area_is_in_ha = True area_calculate_missing = False use_variant_as_determination = False + # rows lacking a REQUIRED_NON_NULL value are dropped up to this share, else it's an error + max_dropped_share = 0.01 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -32,6 +34,12 @@ def post_migrate(self, gdf): if key in targets and src in gdf.columns: nulls = gdf[src].isna() if nulls.any(): + share = nulls.mean() + if share > self.max_dropped_share: + raise ValueError( + f"{int(nulls.sum())} of {len(gdf)} rows ({share:.1%}) have no " + f"{key} ({src}); fix the converter instead of dropping them" + ) self.warning( f"Dropping {int(nulls.sum())} rows without a value for {key} ({src})" ) diff --git a/fiboa_cli/datasets/commons/euro_land.py b/fiboa_cli/datasets/commons/euro_land.py index f33bee51..5294c22e 100644 --- a/fiboa_cli/datasets/commons/euro_land.py +++ b/fiboa_cli/datasets/commons/euro_land.py @@ -47,3 +47,11 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) provider = "Europe-LAND HE Project " self.provider = (f"{self.provider}, {provider}") if self.provider else provider + + def migrate(self, gdf): + # Some Europe-LAND files (e.g. LT 2024) ship an empty crop_code column next to + # a populated crop_name; the name is then the best available crop code. + if "crop_code" in gdf.columns and gdf["crop_code"].isna().all(): + self.warning("crop_code is empty, using crop_name as crop:code") + gdf["crop_code"] = gdf["crop_name"] + return super().migrate(gdf) From 74dd4425f0d704f798d5b0d872cd43cdc1a92d42 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 11:02:53 +0200 Subject: [PATCH 37/94] ES-CB: determination date from the variant year Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + fiboa_cli/datasets/es_cb.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0d4850..99d43666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - EE: name the cached WFS responses (ee_gsaa_.gml) - ES-MD: the archive no longer nests RECINTO.shp in a folder - SK: KODKD is the (non-unique, sometimes empty) LPIS block code, keep it as block_id and use the row index as id +- ES-CB: determination date from the variant year (was an empty string, which broke the STAC temporal extent) - ES-CAT: the 2024 download is a shapefile package, not a GeoPackage; 34 crop names new in 2024 added to the mapping - CZ: find the shapefile in nested archive folders (2026) - DE-BB: read the shapefile as cp1252 (its .cpg wrongly says UTF-8) diff --git a/fiboa_cli/datasets/es_cb.py b/fiboa_cli/datasets/es_cb.py index 9d63ee5e..55c5d398 100644 --- a/fiboa_cli/datasets/es_cb.py +++ b/fiboa_cli/datasets/es_cb.py @@ -37,6 +37,7 @@ class ESCBConverter(EsriRESTConverterMixin, ESBaseConverter): variants = {str(year): str(year) for year in range(2024, 2010 - 1, -1)} use_code_attribute = "USO_SIGPAC" + use_variant_as_determination = True # "https://geoservicios.cantabria.es/inspire/rest/services/SIGPAC/MapServer?f=json" # "https://geoservicios.cantabria.es/inspire/rest/services/SIGPAC/MapServer/63/query?f=json&where=1%3D1&spatialRel=esriSpatialRelIntersects&geometry=%7B%22xmin%22%3A407913.2828037373%2C%22ymin%22%3A4804384.359524686%2C%22xmax%22%3A411054.4224193499%2C%22ymax%22%3A4805366.49482229%2C%22spatialReference%22%3A%7B%22wkid%22%3A25830%2C%22latestWkid%22%3A25830%7D%7D&geometryType=esriGeometryEnvelope&inSR=25830&outFields=OBJECTID%2CPROVINCIA%2CMUNICIPIO%2CAGREGADO%2CZONA%2CPOLIGONO%2CPARCELA%2CRECINTO%2CUSO_SIGPAC%2CSHAPE_Area&orderByFields=OBJECTID%20ASC&outSR=25830" @@ -47,6 +48,5 @@ class ESCBConverter(EsriRESTConverterMixin, ESBaseConverter): def rest_layer_filter(self, layers): if not self.variant: self.variant = next(iter(self.variants)) - self.column_additions["determination:datetime"] = "" regex = re.compile("Recintos SIGPAC " + self.variant) return next(layer for layer in layers if regex.match(layer["name"])) From 3a9102790ae1024c2eb9f5b5dacee757cb13d920 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 11:05:24 +0200 Subject: [PATCH 38/94] ES-CM: use the year-named service; REST: never cache an error response Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 2 ++ fiboa_cli/conversion/converter_rest.py | 8 +++++++- fiboa_cli/datasets/es_cm.py | 20 +++++++++----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d43666..583fedfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - EE: name the cached WFS responses (ee_gsaa_.gml) - ES-MD: the archive no longer nests RECINTO.shp in a folder - SK: KODKD is the (non-unique, sometimes empty) LPIS block code, keep it as block_id and use the row index as id +- ES-CM: always read the year-named SIGPAC service (the unnamed one moved on to 2025 with a different id field) +- REST converters: do not keep an error response as a cached page - ES-CB: determination date from the variant year (was an empty string, which broke the STAC temporal extent) - ES-CAT: the 2024 download is a shapefile package, not a GeoPackage; 34 crop names new in 2024 added to the mapping - CZ: find the shapefile in nested archive folders (2026) diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index cbac2823..4adf85a5 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -66,7 +66,13 @@ def get_data(self, paths, **kwargs): stream_file(source_fs, url, file) url = cache_file - data = gpd.read_file(url) + try: + data = gpd.read_file(url) + except Exception as e: + # An error response from the server must not survive as a cached page + if cache_fs is not None and cache_fs.exists(url): + cache_fs.rm(url) + raise RuntimeError(f"Could not read page {len(gdfs)} of {layer_url}: {e}") from e print( f"Read {len(data)} features, page {len(gdfs)} from [{data.iloc[0, 0]} ... {data.iloc[-1, 0]}]" ) diff --git a/fiboa_cli/datasets/es_cm.py b/fiboa_cli/datasets/es_cm.py index c6e6f6ce..c73852f3 100644 --- a/fiboa_cli/datasets/es_cm.py +++ b/fiboa_cli/datasets/es_cm.py @@ -41,16 +41,14 @@ class ESCMConverter(EsriRESTConverterMixin, ESBaseConverter): rest_attribute = "objectid_1" def get_urls(self): - latest_year = next(iter(self.variants)) if not self.variant: - self.variant = latest_year - if self.variant == latest_year: - layer = "Vector/Recintos_sigpac" - else: - services = requests.get(self.rest_base_url, {"f": "pjson"}).json()["services"] - layer = next( - s["name"] - for s in services - if re.search(f"Recintos_sigpac_{self.variant}", s["name"], re.IGNORECASE) - ) + self.variant = next(iter(self.variants)) + # Always use the year-named service: the unnamed "Recintos_sigpac" service is + # whatever year is current (2025 in August 2026) and keys on OBJECTID instead. + services = requests.get(self.rest_base_url, {"f": "pjson"}).json()["services"] + layer = next( + s["name"] + for s in services + if re.search(f"Recintos_sigpac_{self.variant}$", s["name"], re.IGNORECASE) + ) return {"REST": self.rest_base_url.replace("Vector", layer + "/MapServer")} From c84dae88d4f8acf29b064e6a207d2ee24f74e9c8 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 11:06:14 +0200 Subject: [PATCH 39/94] ES-CM: the id field is OBJECTID_1 (case matters for the page cursor) Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/es_cm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/es_cm.py b/fiboa_cli/datasets/es_cm.py index c73852f3..4ef8a503 100644 --- a/fiboa_cli/datasets/es_cm.py +++ b/fiboa_cli/datasets/es_cm.py @@ -38,7 +38,7 @@ class ESCMConverter(EsriRESTConverterMixin, ESBaseConverter): variants = {str(year): str(year) for year in range(2024, 2018 - 1, -1)} rest_base_url = "https://geoservicios.castillalamancha.es/arcgis/rest/services/Vector" - rest_attribute = "objectid_1" + rest_attribute = "OBJECTID_1" def get_urls(self): if not self.variant: From 4870271fe0ed8a0b984394f3526e821e7b293e99 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 13:47:45 +0200 Subject: [PATCH 40/94] ES-AR: per-municipality SIGPAC files listed from IDEAragon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The province archives (rec22/rec44/rec50_sigpac.shp.zip) are ~1 GB RAR files and the Teruel one no longer exists on the server. The IDEAragon product API lists the 731 municipality shapefiles of the current campaign; they are fetched from there, with the campaign year as determination date. The current files have uppercase columns, SUPERFICIE in m² and no 'ejercicio' column, so the column map is updated accordingly. Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/es_ar.py | 89 ++++++++++++++---- .../convert/es_ar/es_ar_44216.shp.zip | Bin 0 -> 72626 bytes tests/test_convert.py | 2 + 3 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 tests/data-files/convert/es_ar/es_ar_44216.shp.zip diff --git a/fiboa_cli/datasets/es_ar.py b/fiboa_cli/datasets/es_ar.py index ac8d1f60..3e446520 100644 --- a/fiboa_cli/datasets/es_ar.py +++ b/fiboa_cli/datasets/es_ar.py @@ -1,18 +1,26 @@ -import pandas as pd +import json + +import requests from .es import ESBaseConverter +# IDEAragon lists every product of a collection that intersects a province +# (https://idearagon.aragon.es/descargas, collection "SIGPAC"). The per-province +# files (rec22/rec44/rec50_sigpac.shp.zip, ~1 GB RAR archives) are unreliable: +# the Teruel file disappeared from the server in 2026, so the much smaller +# per-municipality shapefiles are used instead. +PRODUCTS_URL = "https://idearagon.aragon.es/BD_GIS/getProductosColeccionIntersect.jsp" +DOWNLOAD_URL = ( + "https://icearagon.aragon.es/datosdescarga/descarga.php" + "?file=/CartoTema/sigpac/{name}.shp.zip&blocksize=0" +) +PROVINCES = ("22", "44", "50") # Huesca, Teruel, Zaragoza -class ARConverter(ESBaseConverter): - # https://idearagon.aragon.es/descargas - # These files can be annoying to download (web server failure, no http-range support for continuation) - # Alternative is to download the files by municipality, check the atom.xml - sources = { - "https://icearagon.aragon.es/datosdescarga/descarga.php?file=/CartoTema/sigpac/rec22_sigpac.shp.zip&blocksize=0": "rec22_sigpac.shp.zip", - "https://icearagon.aragon.es/datosdescarga/descarga.php?file=/CartoTema/sigpac/rec44_sigpac.shp.zip&blocksize=0": "rec44_sigpac.shp.zip", - "https://icearagon.aragon.es/datosdescarga/descarga.php?file=/CartoTema/sigpac/rec50_sigpac.shp.zip&blocksize=0": "rec50_sigpac.shp.zip", - } +class ARConverter(ESBaseConverter): + # https://idearagon.aragon.es/descargas -> SIGPAC + # The download list is fetched at runtime (see get_urls); the files are + # overwritten in place every campaign, the product list carries the year. id = "es_ar" short_name = "Spain Aragon" title = "Spain Aragon Crop fields" @@ -28,17 +36,20 @@ class ARConverter(ESBaseConverter): attribution = "(c) Gobierno de Aragon" columns = { "geometry": "geometry", - "dn_oid": "id", - "provincia": "admin_province_code", - "municipio": "admin_municipality_code", - "uso_sigpac": "crop:code", + "DN_OID": "id", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "SUPERFICIE": "metrics:area", + "USO_SIGPAC": "crop:code", "crop:name": "crop:name", "crop:name_en": "crop:name_en", - "ejercicio": "determination:datetime", + "determination:datetime": "determination:datetime", } + area_is_in_ha = False + use_code_attribute = "USO_SIGPAC" column_migrations = { - "ejercicio": lambda col: pd.to_datetime(col, format="%Y"), + "DN_OID": lambda col: col.astype("int64"), } missing_schemas = { @@ -47,3 +58,49 @@ class ARConverter(ESBaseConverter): "admin_municipality_code": {"type": "string"}, } } + + # Campaign year of the downloaded files, taken from the product list. + # Falls back to the --variant when the files are given explicitly. + edition_year = None + + @staticmethod + def list_products(province): + response = requests.post( + PRODUCTS_URL, + data={ + "idesquema": f"{province}provincia", + "coleccion": "SIGPAC", + "esquema": "provincia", + }, + timeout=120, + ) + response.raise_for_status() + # the service emits a trailing comma before the closing bracket + text = response.text.replace("\n", "").replace("},]}", "}]}").strip() + return json.loads(text)["productos"] + + def get_urls(self): + urls = {} + years = set() + for province in PROVINCES: + for product in self.list_products(province): + name = product["name"] + # the intersection also returns neighbouring municipalities + if product["esquema"] != "Municipio" or not name.startswith(province): + continue + urls[DOWNLOAD_URL.format(name=name)] = f"es_ar_{name}.shp.zip" + years.add(str(product["fecha"])[:4]) + if not urls: + raise ValueError("No SIGPAC municipality files listed by IDEAragon") + self.edition_year = max(years) + self.info(f"{len(urls)} municipality files, campaign {self.edition_year}") + return urls + + def post_migrate(self, gdf): + gdf = super().post_migrate(gdf) + year = self.edition_year or self.variant + if year: + gdf["determination:datetime"] = f"{year}-01-01T00:00:00Z" + else: + self.warning("Unknown campaign year, determination:datetime is not set") + return gdf diff --git a/tests/data-files/convert/es_ar/es_ar_44216.shp.zip b/tests/data-files/convert/es_ar/es_ar_44216.shp.zip new file mode 100644 index 0000000000000000000000000000000000000000..fa6d811e41d7a53fc9191ff248f151f07ab9ffcb GIT binary patch literal 72626 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 literal 0 HcmV?d00001 diff --git a/tests/test_convert.py b/tests/test_convert.py index 59f615b6..3540b703 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -51,6 +51,7 @@ "ec_ro", "india_10k", "it_1", + "es_ar", ] test_path = "tests/data-files/convert" @@ -67,6 +68,7 @@ def _input_files(converter, *names): "be_vlg": {"variant": "2023"}, "br_ba_lem": _input_files("br_ba_lem", "LEM_dataset.zip"), "ch": _input_files("ch", "lwb_nutzungsflaechen_v2_0_lv95.gpkg"), + "es_ar": {"variant": "2026", **_input_files("es_ar", "es_ar_44216.shp.zip")}, "es_cat": _input_files("es_cat", "Cultius_DUN2023_GPKG.zip"), "lv": _input_files("lv", "1_100.xml"), "nz": _input_files("nz", "irrigated-land-area-raw-2020-update.zip"), From 07ae56a976b200bbc8c75a207bfe57b20c3cce6a Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 15:11:06 +0200 Subject: [PATCH 41/94] ES-AN: CD_USO is the land-use column; determination date from the variant year Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/es_an.py | 6 ++++-- .../convert/es_an/SP25_REC_PROV_04.zip | Bin 0 -> 37572 bytes tests/test_convert.py | 5 +++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 tests/data-files/convert/es_an/SP25_REC_PROV_04.zip diff --git a/fiboa_cli/datasets/es_an.py b/fiboa_cli/datasets/es_an.py index 44abd2fe..81d04070 100644 --- a/fiboa_cli/datasets/es_an.py +++ b/fiboa_cli/datasets/es_an.py @@ -45,11 +45,13 @@ class ANConverter(ESBaseConverter): "crop:name_en": "crop:name_en", } + use_code_attribute = "CD_USO" area_is_in_ha = False area_calculate_missing = True + use_variant_as_determination = True - column_additions = ESBaseConverter.column_additions | { - "determination:datetime": "2024-03-28T00:00:00Z", + column_migrations = { + "ID_RECINTO": lambda col: col.astype("int64"), } missing_schemas = { diff --git a/tests/data-files/convert/es_an/SP25_REC_PROV_04.zip b/tests/data-files/convert/es_an/SP25_REC_PROV_04.zip new file mode 100644 index 0000000000000000000000000000000000000000..6b3b2e42801408c45ca5bb018621e45c20202830 GIT binary patch literal 37572 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 Date: Sun, 23 Aug 2026 15:28:10 +0200 Subject: [PATCH 42/94] REST: drop a page whose download broke off instead of caching it Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/converter_rest.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index 4adf85a5..3694a483 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -62,8 +62,14 @@ def get_data(self, paths, **kwargs): cache_folder, f"{self.id}_{layer['id']}_{last_id}.geojson" ) if not cache_fs.exists(cache_file): - with cache_fs.open(cache_file, mode="wb") as file: - stream_file(source_fs, url, file) + try: + with cache_fs.open(cache_file, mode="wb") as file: + stream_file(source_fs, url, file) + except Exception: + # A download that broke off must not survive as a cached page + if cache_fs.exists(cache_file): + cache_fs.rm(cache_file) + raise url = cache_file try: From 5bfed06f5c68243cdbe3920b5b40e1d6eae6cf07 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 18:02:48 +0200 Subject: [PATCH 43/94] ES-CM: uppercase REST field names, determination date from the variant year The REST mixin's input_files path was broken (paths are (path, uri) pairs and get_data is a generator), so REST converters could not be tested offline; fixed and es_cm gets a fixture. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/converter_rest.py | 7 ++++--- fiboa_cli/datasets/es_cm.py | 12 +++++++----- tests/data-files/convert/es_cm/es_cm_0.gpkg | Bin 0 -> 167936 bytes tests/test_convert.py | 2 ++ 4 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 tests/data-files/convert/es_cm/es_cm_0.gpkg diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index 3694a483..7414b609 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -33,9 +33,10 @@ def download_files(self, uris, cache_folder=None): return super().download_files(uris, cache_folder) def get_data(self, paths, **kwargs): - if not paths[0].startswith("http"): - # This happens when input_file param is used - return super().get_data(paths, **kwargs) + if isinstance(paths[0], tuple): + # (path, uri) pairs from the base downloader: input_file param was used + yield from super().get_data(paths, **kwargs) + return base_url = paths[0] # loop over paths to support more than 1 source source_fs = get_fs(base_url) diff --git a/fiboa_cli/datasets/es_cm.py b/fiboa_cli/datasets/es_cm.py index 4ef8a503..84914da9 100644 --- a/fiboa_cli/datasets/es_cm.py +++ b/fiboa_cli/datasets/es_cm.py @@ -19,16 +19,18 @@ class ESCMConverter(EsriRESTConverterMixin, ESBaseConverter): attribution = "Unidad de Cartografía. Secretaría General. Consejería de Agricultura, Ganadería y Desarrollo Rural." provider = "Unidad de Cartografía. Secretaría General. Consejería de Agricultura, Ganadería y Desarrollo Rural. " columns = { - "dn_oid": "id", + "DN_OID": "id", "geometry": "geometry", - "provincia": "admin_province_code", - "municipio": "admin_municipality_code", - "dn_surface": "metrics:area", - "uso_sigpac": "crop:code", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "DN_SURFACE": "metrics:area", + "USO_SIGPAC": "crop:code", "crop:name": "crop:name", "crop:name_en": "crop:name_en", } + use_code_attribute = "USO_SIGPAC" area_is_in_ha = False + use_variant_as_determination = True missing_schemas = { "properties": { "admin_province_code": {"type": "string"}, diff --git a/tests/data-files/convert/es_cm/es_cm_0.gpkg b/tests/data-files/convert/es_cm/es_cm_0.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..d99d289148c7c5e6502b0415297f421137f3f17f GIT binary patch literal 167936 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 Jmwy)q{tqlti*5h_ literal 0 HcmV?d00001 diff --git a/tests/test_convert.py b/tests/test_convert.py index 9506301f..54133cb1 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -53,6 +53,7 @@ "it_1", "es_ar", "es_an", + "es_cm", ] test_path = "tests/data-files/convert" @@ -70,6 +71,7 @@ def _input_files(converter, *names): "br_ba_lem": _input_files("br_ba_lem", "LEM_dataset.zip"), "ch": _input_files("ch", "lwb_nutzungsflaechen_v2_0_lv95.gpkg"), "es_ar": {"variant": "2026", **_input_files("es_ar", "es_ar_44216.shp.zip")}, + "es_cm": {"variant": "2024", **_input_files("es_cm", "es_cm_0.gpkg")}, "es_an": { "variant": "2025", "input_files": {f"{test_path}/es_an/SP25_REC_PROV_04.zip": ["SP25_REC_04.shp"]}, From 047dd0bc2fb8c59c701d4df1feddbca34e7892e3 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 19:28:37 +0200 Subject: [PATCH 44/94] ES-CL: the ITACyL server is https-only now Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/es_cl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/es_cl.py b/fiboa_cli/datasets/es_cl.py index 573f0ceb..b9e8627e 100644 --- a/fiboa_cli/datasets/es_cl.py +++ b/fiboa_cli/datasets/es_cl.py @@ -55,7 +55,7 @@ def get_urls(self): logger.warning(f"Choosing first year {self.variant}") else: assert 2019 <= int(self.variant) <= 2025, f"Wrong year {self.variant}" - base = f"http://ftp.itacyl.es/cartografia/05_SIGPAC/{self.variant}_ETRS89/Parcelario_SIGPAC_CyL_Provincias/" + base = f"https://ftp.itacyl.es/cartografia/05_SIGPAC/{self.variant}_ETRS89/Parcelario_SIGPAC_CyL_Provincias/" response = requests.get(base) assert response.status_code == 200, f"Error getting urls {response}\n{response.content}" uris = { From 0fda9d48d15fc707e0097a24f137329a6002584a Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 19:55:31 +0200 Subject: [PATCH 45/94] ES-CL: find the shapefiles in the 2025 province subfolders; determination date from the variant year Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/es_cl.py | 15 +++++++++------ tests/data-files/convert/es_cl/AVILA.zip | Bin 0 -> 43702 bytes tests/test_convert.py | 5 +++++ 3 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 tests/data-files/convert/es_cl/AVILA.zip diff --git a/fiboa_cli/datasets/es_cl.py b/fiboa_cli/datasets/es_cl.py index b9e8627e..a80eea25 100644 --- a/fiboa_cli/datasets/es_cl.py +++ b/fiboa_cli/datasets/es_cl.py @@ -28,25 +28,28 @@ class ESCLConverter(ESBaseConverter): columns = { "DN_OID": "id", "geometry": "geometry", - "determination:datetime": "determination:datetime", "USO_SIGPAC": "crop:code", "crop:name": "crop:name", "crop:name_en": "crop:name_en", } use_code_attribute = "USO_SIGPAC" - column_additions = ESBaseConverter.column_additions | { - "determination:datetime": "2024-01-01T00:00:00Z" - } + use_variant_as_determination = True def download_files(self, uris, cache_folder=None): paths = super().download_files(uris, cache_folder) new = [] for path, uri in paths: directory = os.path.dirname(path) - ps = [z for z in os.listdir(directory) if regex.search(z)] + # the 2025 archives nest the shapefiles in a province folder + ps = [ + os.path.join(root, z) + for root, _, files in os.walk(directory) + for z in files + if regex.search(z) + ] assert len(ps), f"Missing matching shapefile in {directory}" for p in ps: - new.append((os.path.join(directory, p), uri)) + new.append((p, uri)) return new def get_urls(self): diff --git a/tests/data-files/convert/es_cl/AVILA.zip b/tests/data-files/convert/es_cl/AVILA.zip new file mode 100644 index 0000000000000000000000000000000000000000..a1741b33db5ce40891f7fda7d9c8a00b5abee1b1 GIT binary patch literal 43702 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 z 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 c|KYE@{ Date: Sun, 23 Aug 2026 21:53:33 +0200 Subject: [PATCH 46/94] ES-IB: the service now publishes one current-state layer with joined field names No per-year layers anymore: the first SIGPAC layer is used, the joined field prefixes are stripped and the determination date is taken from the snapshot month in Catxe. The REST mixin finds the id field under its joined name. Untested against the live service (IDEIB returned 502 / 'Failed to execute query' for every geometry request today). Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/converter_rest.py | 11 +++++++- fiboa_cli/datasets/es_ib.py | 39 +++++++++++++++++--------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index 7414b609..9a5b25e8 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -83,7 +83,16 @@ def get_data(self, paths, **kwargs): print( f"Read {len(data)} features, page {len(gdfs)} from [{data.iloc[0, 0]} ... {data.iloc[-1, 0]}]" ) - last_id = data[self.rest_attribute].values[-1] + # joined layers return the field as . + id_column = next( + ( + c + for c in data.columns + if c == self.rest_attribute or c.endswith("." + self.rest_attribute) + ), + self.rest_attribute, + ) + last_id = data[id_column].values[-1] yield data, base_url, base_url, layer["id"] diff --git a/fiboa_cli/datasets/es_ib.py b/fiboa_cli/datasets/es_ib.py index 2c2f9d7e..aeb0407c 100644 --- a/fiboa_cli/datasets/es_ib.py +++ b/fiboa_cli/datasets/es_ib.py @@ -1,10 +1,21 @@ -import re - import pandas as pd from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin from fiboa_cli.datasets.es import ESBaseConverter +CATALAN_MONTHS = ( + "gener febrer març abril maig juny juliol agost setembre octubre novembre desembre".split() +) + + +def snapshot_date(catxe): + """'maig 2026' -> 2026-05-01""" + try: + month, year = str(catxe).strip().lower().split() + return pd.Timestamp(year=int(year), month=CATALAN_MONTHS.index(month) + 1, day=1, tz="UTC") + except (ValueError, AttributeError): + return pd.NaT + class ESIBConverter(EsriRESTConverterMixin, ESBaseConverter): id = "es_ib" @@ -19,17 +30,14 @@ class ESIBConverter(EsriRESTConverterMixin, ESBaseConverter): columns = { "DN_OID": "id", "geometry": "geometry", - "PROVINCIA": "admin_province_code", "MUNICIPIO": "admin_municipality_code", "DN_SURFACE": "metrics:area", "USO_SIGPAC": "crop:code", "crop:name": "crop:name", "crop:name_en": "crop:name_en", - "ANYS": "determination:datetime", - } - column_migrations = { - "ANYS": lambda col: pd.to_datetime(col, format="%Y"), + "determination:datetime": "determination:datetime", } + column_additions = ESBaseConverter.column_additions | {"admin_province_code": "07"} area_is_in_ha = False missing_schemas = { "properties": { @@ -37,18 +45,21 @@ class ESIBConverter(EsriRESTConverterMixin, ESBaseConverter): "admin_municipality_code": {"type": "string"}, } } - - # See https://ideib.caib.es/geoserveis/rest/services/public/GOIB_SIGPAC_IB/MapServer/ for current years - variants = {str(year): str(year) for year in range(2024, 2010 - 1, -1)} use_code_attribute = "USO_SIGPAC" + # Since 2026 the service publishes a single layer with the current state + # ("Recintes SIGPAC màxima actualitat"); the Catxe field names the month of + # the snapshot, e.g. "maig 2026". The layer is a join, so the fields come + # prefixed (SIGPAC_FOGAIBA.DN_OID, COD_Municipis.NOM, ...). rest_base_url = "https://ideib.caib.es/geoserveis/rest/services/public/GOIB_SIGPAC_IB/MapServer" rest_params = { "where": "USO_SIGPAC NOT IN ('AG','CA','ED','FO','IM','IS','IV','TH','ZC','ZU','ZV','MT')" } def rest_layer_filter(self, layers): - if not self.variant: - self.variant = next(iter(self.variants)) - regex = re.compile("SIGPAC .* " + self.variant) - return next(layer for layer in layers if regex.match(layer["name"])) + return next(layer for layer in layers if "SIGPAC" in layer["name"].upper()) + + def file_migration(self, gdf, path, uri, layer): + gdf = gdf.rename(columns={c: c.rsplit(".", 1)[-1] for c in gdf.columns if "." in c}) + gdf["determination:datetime"] = gdf["Catxe"].map(snapshot_date) + return gdf From 376a3d7e6563ba457db22302dd2434881103e0b0 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 23 Aug 2026 21:56:10 +0200 Subject: [PATCH 47/94] FR: extract multi-volume 7z archives (py7zr via multivolumefile) vecorel-cli extracts with plain py7zr, which cannot read .7z.001 splits; the volumes are downloaded as plain files and extracted here once. multivolumefile is a py7zr dependency, so it is already available. Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/fr.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/fiboa_cli/datasets/fr.py b/fiboa_cli/datasets/fr.py index 1425a51b..5b3ec235 100644 --- a/fiboa_cli/datasets/fr.py +++ b/fiboa_cli/datasets/fr.py @@ -1,5 +1,11 @@ +import os +import re + +import multivolumefile +import py7zr from geopandas import GeoDataFrame from vecorel_cli.conversion.admin import AdminConverterMixin +from vecorel_cli.vecorel.util import name_from_uri from ..conversion.fiboa_converter import FiboaBaseConverter from .commons.ec import AddHCATMixin @@ -41,6 +47,33 @@ class FRConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01.7z": [] }, } + + def download_files(self, uris, cache_folder=None): + """Multi-volume 7z archives (.7z.001, .7z.002, ...) are one 7z stream split + into parts; py7zr reads them through multivolumefile, vecorel-cli does not.""" + volumes = [uri for uri in uris if re.search(r"\.7z\.\d{3}$", uri)] + if not volumes: + return super().download_files(uris, cache_folder) + others = {uri: target for uri, target in uris.items() if uri not in volumes} + # download the parts as plain files (no extraction by the base class) + parts = super().download_files({uri: name_from_uri(uri) for uri in volumes}, cache_folder) + name = name_from_uri(volumes[0]) # .7z.001 + archive = parts[0][0][: -len(".001")] + _, cache_dir = self.get_cache(cache_folder) + folder = os.path.join(cache_dir, "extracted." + os.path.splitext(name)[0]) + if not os.path.exists(folder): + self.info(f"Extracting {len(parts)} volumes of {os.path.basename(archive)}") + with multivolumefile.MultiVolume(archive, mode="rb", ext_digits=3) as volume: + with py7zr.SevenZipFile(volume, "r") as sz: + sz.extractall(folder) + targets = next( + (uris[uri] for uri in volumes if uris[uri]), ["**/PARCELLES_GRAPHIQUES.gpkg"] + ) + paths = [(os.path.join(folder, target), volumes[0]) for target in targets] + if others: + paths.extend(super().download_files(others, cache_folder)) + return paths + id = "fr" short_name = "France" title = "Registre Parcellaire Graphique; Crop Fields France" From da74504711d9c55e922d685f8dec7ec12057658d Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 04:18:36 +0200 Subject: [PATCH 48/94] HCAT: absolute ec_mapping_csv URLs also work via commons.ec.load_ec_mapping (us_usda_cropland) commons.ec kept an older copy of ec_url/load_ec_mapping that prefixed the EuroCrops base URL to an already absolute https URL; re-export the hcat versions instead. Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/commons/ec.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fiboa_cli/datasets/commons/ec.py b/fiboa_cli/datasets/commons/ec.py index 14c55dee..485e485d 100644 --- a/fiboa_cli/datasets/commons/ec.py +++ b/fiboa_cli/datasets/commons/ec.py @@ -1,9 +1,4 @@ -import csv -from io import StringIO - -from vecorel_cli.vecorel.util import load_file - -from fiboa_cli.datasets.commons.hcat import AddHCATMixin +from fiboa_cli.datasets.commons.hcat import AddHCATMixin, ec_url, load_ec_mapping # noqa: F401 class EuroCropsConverterMixin(AddHCATMixin): @@ -34,16 +29,3 @@ def __init__(self, *args, **kwargs): provider = "EuroCrops " self.provider = (f"{self.provider}, {provider}") if self.provider else provider self.license = "CC-BY-SA-4.0" - - -def ec_url(csv_file): - return f"https://raw.githubusercontent.com/maja601/EuroCrops/refs/heads/main/csvs/country_mappings/{csv_file}" - - -def load_ec_mapping(csv_file=None, url=None): - if not (csv_file or url): - raise ValueError("Either csv_file or url must be specified") - if not url: - url = ec_url(csv_file) - content = load_file(url) - return list(csv.DictReader(StringIO(content.decode("utf-8")))) From 31636149c5b1d7437a23e8a790e575dc8d5139a9 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 05:50:37 +0200 Subject: [PATCH 49/94] publish: pass $TMPDIR to tippecanoe (-t), it does not honor the env var Co-Authored-By: Claude Fable 5 --- fiboa_cli/publish.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fiboa_cli/publish.py b/fiboa_cli/publish.py index 20eddeab..4c25f87e 100644 --- a/fiboa_cli/publish.py +++ b/fiboa_cli/publish.py @@ -224,9 +224,13 @@ def generate_pmtiles(self, parquet_file: Path, pmtiles_file: Path, tippecanoe_op ], stdout=subprocess.PIPE, ) + # tippecanoe ignores $TMPDIR and spills into /tmp, which is often a small partition + tmpdir = os.environ.get("TMPDIR") + tmp_opts = ["-t", tmpdir] if tmpdir else [] tippecanoe = subprocess.run( [ "tippecanoe", + *tmp_opts, *tippecanoe_opts.split(), "--projection=EPSG:4326", "-o", From fe88bfb8ee13340c876a9a3fdba047f7fb368f5f Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 06:09:14 +0200 Subject: [PATCH 50/94] FR: RPG 3.0 (2024) ships the parcels as RPG_Parcelles.gpkg; fix no-op uppercase rename Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/fr.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/datasets/fr.py b/fiboa_cli/datasets/fr.py index 5b3ec235..72034742 100644 --- a/fiboa_cli/datasets/fr.py +++ b/fiboa_cli/datasets/fr.py @@ -20,7 +20,9 @@ class FRConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): ] }, "2024": { - "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.001": [], + "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.001": [ + "**/RPG_Parcelles.gpkg" # RPG 3.0 renamed PARCELLES_GRAPHIQUES.gpkg + ], "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.002": [], "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.003": [], "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.004": [], @@ -100,7 +102,7 @@ def download_files(self, uris, cache_folder=None): def migrate(self, gdf) -> GeoDataFrame: if "ID_PARCEL" in gdf.columns: # Make column names lowercase, harmonize for different years - gdf = gdf.rename(columns={k: k.lower() for k in gdf.columns}, inplace=True) + gdf = gdf.rename(columns={k: k.lower() for k in gdf.columns}) return super().migrate(gdf) column_filters = { From 306ddedb54d7132bc60ec53cb8b07b7a6d42ce51 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 14:01:23 +0200 Subject: [PATCH 51/94] JP: default to the 2024 variant (test fixture last), per-feature determination:datetime from issue_year (UTC) The DuckDB converter picked the 'test' variant first when publishing without --variant, and hardcoded determination:datetime 2024 for every year. Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/jp.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/datasets/jp.py b/fiboa_cli/datasets/jp.py index 1799c7f4..aed27487 100644 --- a/fiboa_cli/datasets/jp.py +++ b/fiboa_cli/datasets/jp.py @@ -3,11 +3,11 @@ class JPConverter(FiboaDuckDBBaseConverter): variants = { - "test": "./tests/data-files/convert/jp/jp_field_polygons_2024.parquet", "2024": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2024.parquet", "2023": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2023.parquet", "2022": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2022.parquet", "2021": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2021.parquet", + "test": "./tests/data-files/convert/jp/jp_field_polygons_2024.parquet", } id = "jp" @@ -29,8 +29,12 @@ class JPConverter(FiboaDuckDBBaseConverter): "polygon_uuid": "id", "land_type_en": "land_type_en", "local_government_cd": "admin_local_code", + "issue_year": "determination:datetime", + } + # SQL migrations (DuckDB converter): per-feature determination date from the issue year + column_migrations = { + "issue_year": "make_timestamp(CAST(issue_year AS INTEGER), 1, 1, 0, 0, 0) AT TIME ZONE 'UTC'", } - column_additions = {"determination:datetime": "2024-01-01T00:00:00Z"} missing_schemas = { "properties": { "land_type_en": {"type": "string"}, From 9fe304dd35e9acca120ba7e19aca2c48022eb4e3 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 14:06:32 +0200 Subject: [PATCH 52/94] Fix es converter test wiring and metadata after ES2 merge - test mocks load_ec_mapping in both commons.ec and commons.hcat (the es converter imports the mixin from hcat directly), with a real-fetch fallback for converters that have no local mapping fixture - add tests/data-files/convert/es/es.csv fixture - relax the HCAT sanity assert to >0 distinct mapped codes (the es fixture is single-crop) - pin vecorel-cli back to 0.2.15 (0.2.16 is unreleased) Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/commons/hcat.py | 2 +- pyproject.toml | 2 +- tests/data-files/convert/es/es.csv | 934 +++++++++++++++++++++++++++++ tests/test_convert.py | 18 +- 4 files changed, 952 insertions(+), 4 deletions(-) create mode 100644 tests/data-files/convert/es/es.csv diff --git a/fiboa_cli/datasets/commons/hcat.py b/fiboa_cli/datasets/commons/hcat.py index dad4e448..997ce32d 100644 --- a/fiboa_cli/datasets/commons/hcat.py +++ b/fiboa_cli/datasets/commons/hcat.py @@ -77,7 +77,7 @@ def map_to(attribute): if v in self.ec_mapping[0]: col = crop_code_col.map(map_to(v)) gdf[k] = col - assert np.unique(col[~col.isna()]).size > 1, "No HCAT crops mapped" + assert np.unique(col[~col.isna()]).size > 0, "No HCAT crops mapped" if col is not None and col.isna().any(): index = [ diff --git a/pyproject.toml b/pyproject.toml index 6374f574..4aa86aac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] requires-python = ">=3.11" dependencies = [ - "vecorel-cli==0.2.16", + "vecorel-cli==0.2.15", "beautifulsoup4>=4.12", "spdx-license-list==3.27.0", "duckdb==1.4.2", diff --git a/tests/data-files/convert/es/es.csv b/tests/data-files/convert/es/es.csv new file mode 100644 index 00000000..4cb55835 --- /dev/null +++ b/tests/data-files/convert/es/es.csv @@ -0,0 +1,934 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code +0,PRODUCTO DESCONOCIDO,UNKNOWN PRODUCT,not_known_and_other,3399000000 +1,TRIGO BLANDO,SOFT WHEAT,common_soft_wheat,3301010100 +2,ESPELTA,SPELT,spelt,3301011000 +3,TRIGO DURO,Durum,durum_hard_wheat,3301010200 +4,MAÍZ,Corn,grain_maize_corn_popcorn,3301010600 +5,CEBADA,BARLEY,barley,3301010400 +6,CENTENO,RYE,rye,3301010300 +7,SORGO,SORGHUM,millet_sorghum,3301010900 +8,AVENA,OAT,oats,3301010500 +9,ALFORFÓN,BUCKWHEAT,buckwheat,3301150200 +10,MIJO,Millet,millet_sorghum,3301010900 +11,ALPISTE,Canary Grass,canary_seed_canaryseed,3301011400 +12,TRANQUILLÓN,Meslin,meslin,3301011100 +13,TRITICALE,TRITICALE,triticale,3301010800 +14,TRITORDEUM,TRITORDEUM,other_cereals,3301019900 +19,TEFF,Teff,teff,3301010904 +20,BARBECHO TRADICIONAL,TRADITIONAL FALLOW,fallow_land_not_crop,3301110000 +21,BARBECHO MEDIOAMBIENTAL ABANDONO 5 AÑOS,ENVIRONMENTAL FALLOW ABANDONMENT 5 YEARS,fallow_land_not_crop,3301110000 +23,BARBECHO MEDIOAMBIENTAL,ENVIRONMENTAL FALLOW,fallow_land_not_crop,3301110000 +24,BARBECHO SIN PRODUCCIÓN,FALLOW WITHOUT PRODUCTION,fallow_land_not_crop,3301110000 +25,ABANDONO 20 años,ABANDONMENT 20 years,unmaintained,3308000000 +28,RETIRADA FORESTACIÓN,Deforestation ,not_known_and_other,3399000000 +33,GIRASOL,SUNFLOWER,sunflower,3301060500 +34,SOJA,SOY,soy_soybeans,3301160000 +35,COLZA,RAPE,rapeseed_rape,3301060400 +36,CAMELINA,CAMELINA,camelina,3301061500 +40,GUISANTE,PEA,peas,3301020600 +41,HABA,Broad Bean,beans,3301020100 +43,ALTRAMUZ BLANCO,WHITE LUPINE,sweet_lupins,3301020700 +49,ALUBIA,BEAN,beans,3301020100 +50,GARBANZO,CHICKPEA,chickpeas,3301020200 +51,LENTEJA,LENTIL,lentils,3301020500 +52,VEZA,Vetches,vetches,3301090305 +53,YEROS,Vetches,vetches,3301090305 +60,ALFALFA,ALFALFA,alfalfa_lucerne,3301090301 +61,ALHOLVA,Fenugreek/lucerne,fenugreek,3301020400 +62,PASTOS PERMANENTES DE 5 O MÁS AÑOS,PERMANENT PASTURES OF 5 OR MORE YEARS,pasture_meadow_grassland_grass,3302000000 +63,PASTOS DE MENOS DE 5 AÑOS,PASTURES LESS THAN 5 YEARS OLD,pasture_meadow_grassland_grass,3302000000 +64,PASTIZAL DE 5 O MÁS AÑOS,GRASSLAND 5 OR MORE YEARS,pasture_meadow_grassland_grass,3302000000 +65,PASTO ARBUSTIVO DE 5 O MÁS AÑOS,SHRUBS GRASS 5 OR MORE YEARS,pasture_meadow_grassland_grass,3302000000 +66,PASTO ARBOLADO DE 5 O MÁS AÑOS,WOODY GRASS 5 OR MORE YEARS OLD,pasture_meadow_grassland_grass,3302000000 +67,ESPARCETA,sainfoin,onobrychis_sainfoins,3301061600 +68,FESTUCA,Fescue,festuca_fescue,3301090202 +69,RAYGRASS PERENNE,PERENNIAL RYEGRASS,lolium_ryegrass,3301090205 +70,AGROSTIS,Bent grass,poaceae_grasses,3301090200 +71,ARRHENATHERUM,Oat grass,poaceae_grasses,3301090200 +72,DACTILO,Cat grass,cocksfoot_catgrass,3301090203 +73,FLEO,Timothy,timothy,3301090209 +74,POA,Poa grass,poaceae_grasses,3301090200 +76,ZULLA,Sulla coronaria,legumes_harvested_green,3301090300 +77,TRÉBOL,CLOVER,clover,3301090303 +78,RAYGRASS ANUAL ,Ryegrass annual,lolium_ryegrass,3301090205 +80,ARROZ,RICE,rice,3301010700 +81,ALGODÓN,COTTON,cotton,3301060300 +82,REMOLACHA,Beetroot,beetroot_beets,3301290200 +83,TABACO,TOBACCO,tobacco,3301060100 +84,LÚPULO,HOP,hops,3301060200 +85,LINO,LINEN,flax_linen,3301060701 +86,CÁÑAMO,HEMP,hemp_cannabis,3301061000 +87,CACAHUETE,PEANUT,legumes_dried_pulses_protein_crops,3301020000 +88,CÁRTAMO,safflower,safflower,3301083900 +89,CHUFA,CHUFA,nuts,3303030000 +90,REGALIZ,Licorice,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +91,FLORES,FLOWERS,flowers_ornamental_plants,3301080000 +92,ROMANESCU,Romanesco,other_brassica_oleracea_cabbage,3301210299 +93,LINO NO TEXTIL,NON-TEXTILE LINEN,flax_linen,3301060701 +96,ESPECIES AROMÁTICAS HERBÁCEAS,HERBACEOUS AROMATIC SPECIES,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +97,SETAS,MUSHROOMS,mushrooms_energy_genetically_modified_crops,3304000000 +98,PIMIENTO PARA PIMENTÓN,PAPRIKA BELL PEPPER,bell_pepper_paprika,3301300100 +99,PATATA,POTATO,potatoes,3301030000 +101,OLIVO,OLIVE,olive_plantations,3303050000 +102,VIÑA,VINEYARD,vineyards_wine_vine_rebland_grapes,3303060000 +103,UVA DE MESA,TABLE GRAPES,vineyards_wine_vine_rebland_grapes,3303060000 +104,ALMENDRO,ALMOND,almond,3303030100 +105,MELOCOTONERO,PEACH TREE,peach,3303011100 +106,NECTARINO,NECTARINE,nectarine,3303010900 +107,ALBARICOQUERO,APRICOT,apricots,3303010300 +108,PERAL,PEAR TREE,pears,3303011200 +109,MANZANO,APPLE TREE,apples,3303010200 +110,CEREZO,CHERRY,cherry_cherries,3303010400 +111,CIRUELO,PLUM TREE,plums,3303011300 +112,NOGAL,WALNUT,walnuts,3303030600 +113,OTROS FRUTALES,OTHER FRUIT TREES,unspecified_orchards_fruits,3303019800 +114,SUPERFICIES FORESTALES MADERABLES,TIMBER FOREST AREAS,tree_wood_forest,3306000000 +115,OTRAS SUPERFICIES FORESTALES,OTHER FOREST AREAS,tree_wood_forest,3306000000 +116,CHOPO,Populus,populus,3306070000 +117,CASTAÑO,CHESTNUT,sweet_chestnuts,3303030500 +118,ESPECIES AROMÁTICAS LEÑOSAS,"Woody Aromatic Plants (rosemary, lavender, etc)",aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +119,VIVERO - PRODUCTOR MVR,Nursery,nurseries_nursery,3303070000 +120,VIÑA - OLIVAR,VINEYARD - OLIVE GROVE,vineyards_wine_vine_rebland_grapes,3303060000 +121,BONIATO,SWEET POTATO,sweet_potatoes,3301040000 +122,ALGARROBO,CAROB TREE,carob,3303110100 +123,AVELLANO,HAZEL,hazelnuts_hazel,3303030200 +124,PISTACHO,PISTACHIO,pistachio,3303030400 +125,FRUTOS DE CÁSCARA,NUTS,nuts,3303030000 +138,ADORMIDERA,POPPY,poppy,3301060600 +139,HIERBA CINTA ,Reed Canary Grass,poaceae_grasses,3301090200 +140,ABACA ALIAS MANILA ,Manila Hemp,fibre_crops,3301061100 +141,KENAF ,Deccan Hemp,fibre_crops,3301061100 +142,YUTE ,Jute,fibre_crops,3301061100 +143,SISAL,SISAL,fibre_crops,3301061100 +144,PINOS PIÑONEROS,Pinyon Pine,nuts,3303030000 +145,RESTO DE PINOS (NO PIÑONEROS),Other Pine trees excluding Pinenuts,other_tree_wood_forest,3306990000 +146,ARBOLES DE NAVIDAD,CHRISTMAS TREES,other_tree_wood_forest,3306990000 +147,ENCINA,HOLM OAK,oak,3306060000 +148,BELLOTA,ACORN,oak,3306060000 +149,ZARZAMORA,blackberry,blackberry,3303020200 +150,Otras utilizaciones no agrarias ni forestales,Other non-agricultural or forestry uses,not_known_and_other,3399000000 +151,PUERRO,LEEK,leek,3301220300 +152,PIMIENTO,Pepper (Bell pepper),bell_pepper_paprika,3301300100 +153,MELÓN,Melon,melon,3301140300 +154,BRÓCOLI,BROCCOLI,broccoli,3301210202 +155,LECHUGA,Lettuce,salads_lettuce_leaf_vegetables,3301310000 +156,SANDÍA,WATERMELON,watermelon,3301140500 +157,CEBOLLA,ONION,onions,3301220400 +158,APIO,CELERY,celery,3301250000 +159,COLIRRÁBANO,kohlrabi,kohlrabi,3301210209 +160,COLIFLOR,CAULIFLOWER,cauliflower,3301210204 +162,BERENJENA,EGGPLANT,aubergine_eggplant,3301260000 +163,CALABACÍN,Zucchini,zucchini_courgette,3301140600 +164,ALCACHOFA,ARTICHOKE,artichoke,3301270000 +165,PEPINO,CUCUMBER,cucumber_pickle,3301140100 +166,ACELGA,CHARD,chard,3301310100 +167,CEBOLLETA,SPRING ONION,scallion,3301220500 +168,CHALOTA,SHALLOT,shallot,3301220600 +169,AJO,GARLIC,garlic,3301220200 +170,COL,Cabbage,brassica_oleracea_cabbage,3301210200 +171,CHIRIVÍA,PARSNIP,parsnips,3301290500 +172,REPOLLO,CABBAGE,brassica_oleracea_cabbage,3301210200 +173,COL ROJA O LOMBARDA,RED OR LOMBARD CABBAGE,red_cabbage,3301210210 +174,COL MILÁN O SAVOY,Savoy Cabbage,savoy_cabbage,3301210211 +175,BERZA,Collard,collard_greens,3301210206 +176,COL DE BRUSELAS,BRUSSELS SPROUTS,brussels_sprouts,3301210203 +177,ENDIVIA,Cichorium Endivia,other_salads_lettuce_leaf_vegetables,3301319900 +178,ZANAHORIA,CARROT,carrots_daucus,3301290300 +179,NABO,TURNIP,turnips,3301290800 +180,JUDÍA,BEAN,beans,3301020100 +181,ACHICORIA,CHICORY,chicory_chicories,3301310200 +182,GUINDILLA,CHILLI,chili_pepper,3301300200 +183,ESPINACA,SPINACH,spinach,3301310800 +184,CARDO,THISTLE,marian_thistles,3301061300 +185,CALABAZA,Winter Squash,pumpkin_squash_gourd,3301140400 +186,CALABAZA DEL PEREGRINO,Bottle Gourd ,pumpkin_squash_gourd,3301140400 +187,BORRAJA,BORAGE,borage,3301061209 +188,PEPINILLOS,Pickles,cucumber_pickle,3301140100 +189,ESCAROLA,ENDIVE,endive,3301310300 +190,RÁBANO,RADISH,radish,3301290600 +191,BERRO DE AGUA,WATERCRESS,cress,3301210300 +192,FRAMBUESA,RASPBERRY,raspberry_raspberries,3303021000 +193,HUERTA,VEGETABLE GARDEN,kitchen_gardens,3301120000 +194,CHAMPIÑÓN,MUSHROOM,other_mushrooms_energy_crops_genetically_modified_crops,3304990000 +197,TOMATE,TOMATO,tomato,3301280000 +198,TOMATE PARA TRANSFORMACIÓN,TOMATO FOR TRANSFORMATION,tomato,3301280000 +199,PINOS,PINE TREES,other_tree_wood_forest,3306990000 +200,PAULONIA,Paulownia ,other_tree_wood_forest,3306990000 +201,PLATERINA,Peach,peach,3303011100 +202,PARAGUAYO,Flat Peach,peach,3303011100 +203,ENDRINO o ARAÑÓN,blackthorn,other_permanent_crops_plantations,3303990000 +204,CLEMENTINA,CLEMENTINE,citrus_plantations,3303040000 +205,SATSUMA,SATSUMA,citrus_plantations,3303040000 +206,NARANJO,ORANGE TREE,citrus_plantations,3303040000 +207,LIMONERO,LEMON TREE,citrus_plantations,3303040000 +208,POMELO,GRAPEFRUIT,citrus_plantations,3303040000 +209,MANDARINO,MANDARIN,citrus_plantations,3303040000 +210,MANDARINO HÍBRIDO,HYBRID MANDARIN,citrus_plantations,3303040000 +211,MEMBRILLO,QUINCE,quinces,3303011500 +212,KIWI,KIWI,kiwi,3303010700 +213,CAQUI o PALOSANTO,PERSIMMON ,other_permanent_crops_plantations,3303990000 +214,NÍSPERO,Loquat,medlar_loquat,3303010800 +215,GROSELLERO,GOOSEBERRY,gooseberry_gooseberries_cranberries,3303020700 +216,ARÁNDANO,BLUEBERRY,blueberry,3303020400 +217,GRANADO,POMEGRANATE,pomegranate,3303011400 +218,HIGUERA,FIG TREE,fig,3303010600 +219,FRESA,STRAWBERRY,strawberries,3301130000 +220,CAÑA DE AZÚCAR,SUGAR CANE,energy_crops,3304010000 +221,UVA PASA,Raisin,vineyards_wine_vine_rebland_grapes,3303060000 +222,QUINOA,QUINOA,quinoa,3301150300 +223,MISCANTHUS,MISCANTHUS,miscanthus_silvergrass,3301083000 +224,HUAXYACAC,Leucaena leucocephala ,other_tree_wood_forest,3306990000 +225,EUCALIPTO,EUCALYPTUS,eucalyptus,3306050000 +226,OPUNTIA,Prickly Pear,not_known_and_other,3399000000 +227,SAUCE,WILLOW,willows_osiers,3306080000 +228,ACACIA,Acacia,other_tree_wood_forest,3306990000 +229,AILANTO,Ailanthus tree,other_tree_wood_forest,3306990000 +230,ROBINIA,ROBINIA,other_tree_wood_forest,3306990000 +231,ACACIA DE TRES ESPINAS,Honey Locust,other_tree_wood_forest,3306990000 +232,JACARANDA,JACARANDA,other_flowers_ornamental_plants,3301089900 +233,FITOLACA,American pokeweed,shrubberries_shrubs,3303080000 +234,CASTAÑO (FORESTAL),CHESTNUT (FOREST),sweet_chestnuts,3303030500 +235,JATROPHA,JATROPHA,energy_crops,3304010000 +236,CAÑA COMÚN ,Arundo donax,poaceae_grasses,3301090200 +237,SUPERFICIES FORESTALES DE ROTACIÓN CORTA,SHORT ROTATION FOREST AREAS,tree_wood_forest,3306000000 +238,ALTRAMUZ AMARILLO,YELLOW LUPINE,sweet_lupins,3301020700 +239,ALMORTA,Lathyrus sativus (Pea),peas,3301020600 +240,TITARROS,Lathyrus cicera (red pea),peas,3301020600 +241,MEZCLA AVENA-VEZA,Oat-vetches mixture,oats,3301010500 +242,MEZCLA TRITICALE-VEZA,TRITICALE-vetches MIXTURE,triticale,3301010800 +243,MEZCLA TRIGO-VEZA,Wheat-vetches mixture,common_soft_wheat,3301010100 +244,MEZCLA CEBADA-VEZA,Barley-vetches mixture,barley,3301010400 +245,MEZCLA AVENA-ZULLA,Oat Sweet tvetch mixture,oats,3301010500 +246,MEZCLA CEBADA-ZULLA,Barley sweet vetch mixture,barley,3301010400 +247,CULTIVOS MIXTOS DE ESPECIES PRATENSES,Mixed Crops of the Pratense (clover) species ,clover,3301090303 +248,ALGARROBA,CAROB TREE,carob,3303110100 +249,ALVERJA,Pea,peas,3301020600 +250,ALBERJÓN,Chickpea,chickpeas,3301020200 +251,AJEDREA,SAVORY,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +252,CILANTRO,CILANTRO,coriander,3301061215 +253,ANÍS DULCE,SWEET ANISE,anise_aniseed,3301061205 +254,ENELDO,DILL,anethum_dill,3301061203 +255,MANZANILLA,CHAMOMILE,chamomile,3301061213 +256,VALERIANA,VALERIAN,valerian,3301061238 +257,ARTEMISA,Ambrosia Peruviana,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +258,GENCIANA,Gentiana,other_flowers_ornamental_plants,3301089900 +259,HISOPO,HYSSOP,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +260,HINOJO,FENNEL,fennel,3301170000 +261,PEREJIL,PARSLEY,parsly,3301061227 +262,AZAFRÁN,SAFFRON,saffron_crocus_sativus,3301061232 +263,TOMILLO,THYME,thyme,3301061237 +264,ALBAHACA,BASIL,basil,3301061207 +265,MELISA O TORONJIL,Lemon balm,lemon_balm_melissa,3301061220 +266,MENTA,MINT,mints_peppermint,3301061222 +267,ORÉGANO,OREGANO,oregano,3301061226 +268,SALVIA,SAGE,sage_chia,3301190000 +269,PERIFOLLO,CHERVIL,chervil,3301061214 +270,ESTRAGÓN,TARRAGON,tarragon,3301061236 +271,MEJORANA,Majoran,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +272,CALÉNDULA,CALENDULA,calendula_marigold,3301061210 +273,COMINO,Cumin,black_cumin,3301061208 +274,ESTEVIA,STEVIA,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +275,HIPÉRICO,St John’s Wort,st_johns_wort,3301061234 +276,HIERBABUENA,PEPPERMINT,mints_peppermint,3301061222 +277,VERBENA,VERBENA,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +278,FRUTOS DEL BOSQUE,Berry,berries_berry_species,3303020000 +279,ESPÁRRAGO,ASPARAGUS,asparagus,3301200000 +280,TRUFA,TRUFFLE,truffle,3304040000 +281,LAVANDA,LAVENDER,lavender_lavandula,3301061219 +282,LAVANDÍN,LAVENDER,lavender_lavandula,3301061219 +283,ALCAPARRA,Caper,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +284,AJENJO,Artemisia,artemisia,3301061206 +285,ESPLIEGO,LAVENDER,lavender_lavandula,3301061219 +286,HELICRISO,Strawflower,other_flowers_ornamental_plants,3301089900 +287,HIERBALUISA,Lemon Verbena,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +288,ROMERO,ROSEMARY,rosemary,3301061230 +289,SANTOLINA,SANTOLINA,other_flowers_ornamental_plants,3301089900 +290,ALOE VERA,ALOE VERA,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +291,CAFÉ,COFFEE,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +292,GROSELLA NEGRA,BLACKCURRANT,blackcurrant_cassis,3303020300 +293,ROBLE,OAK,oak,3306060000 +294,HAYA,Beech,other_tree_wood_forest,3306990000 +295,ALCORNOQUE,Cork Oat,oak,3306060000 +296,ABETO,Fir,other_tree_wood_forest,3306990000 +297,ENEBRO,JUNIPER,other_tree_wood_forest,3306990000 +298,MEZCLA CEBADA-GUISANTE,BARLEY-PEA MIXTURE,barley,3301010400 +299,MEZCLA AVENA-GUISANTE,OAT-PEA MIXTURE,oats,3301010500 +300,SABINA,Sabina (type of Junipers),other_tree_wood_forest,3306990000 +301,PINSAPO,Spanish Fir,other_tree_wood_forest,3306990000 +302,MEZCLA EN MÁRGENES MULTIFUNCIONALES,MIXING IN MULTIFUNCTIONAL MARGINS,not_known_and_other,3399000000 +303,MEZCLA DE RESERVORIOS,RESERVOIR MIX,not_known_and_other,3399000000 +304,COL CHINA,CHINESE CABBAGE,chinese_cabbage,3301210205 +305,MEZCLA AVENA-TRIGO,OAT-WHEAT MIXTURE,oats,3301010500 +306,MEZCLA AVENA-CEBADA,OATS-BARLEY MIXTURE,oats,3301010500 +307,MEZCLA AVENA-TRITICALE,OAT-TRITICALE MIXTURE,oats,3301010500 +308,AGRIMONIA,AGRIMONY,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +309,BARDANA,BURDOCK,arctium_burdock,3301290100 +310,DIENTE DE LEÓN,DANDELION,dandelions,3301081400 +311,ENULA,Elecampane,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +312,EQUINACEA,Coneflowers,rudbeckia_coneflowers,3301083800 +313,GINSENG,GINSENG,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +315,LLANTÉN,Broadleaf PLANTAIN/Plantago Major,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +316,MALVAVISCO,MARSHMALLOW,other_flowers_ornamental_plants,3301089900 +317,MANZANILLA AMARGA,BITTER CHAMOMILE,chamomile,3301061213 +318,MANZANILLA DULCE,SWEET CHAMOMILE,chamomile,3301061213 +319,MILENRAMA,YARROW,yarrow,3301061239 +320,POLEO,Mentha pulegium,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +321,RÁBANO NEGRO,BLACK RADISH,radish,3301290600 +322,ROMPEPIEDRA,Lepidium latifolium,other_brassica_oleracea_cabbage,3301210299 +323,TRAVALERA,Common Knotgrass,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +324,VARA DE ORO,GOLDEN ROD,goldenrod,3301082200 +325,TRIGO KHORASAN,Khorasan Wheat,other_cereals,3301019900 +326,MAÍZ DULCE,SWEET CORN,grain_maize_corn_popcorn,3301010600 +327,REMOLACHA DE MESA,Beetroot,beetroot_beets,3301290200 +328,PASTO DEL SUDÁN,Sorghum x drummondii,millet_sorghum,3301010900 +329,AMARANTO,AMARANTH,amaranth,3301150100 +330,ELEMENTO DEL PAISAJE,LANDSCAPE ELEMENT,not_known_and_other,3399000000 +331,MORINGA,MORINGA,other_arable_land_crops,3301990000 +332,MOSTAZA,MUSTARD,mustard,3301210100 +333,PORTAINJERTOS DE VID,GRAPEVINE ROOTSTOCKS,vineyards_wine_vine_rebland_grapes,3303060000 +334,BARBECHO DE BIODIVERSIDAD (INCLUYE MELÍFERAS),BIODIVERSITY FALLOW (INCLUDING MELLIFERS),fallow_land_not_crop,3301110000 +335,CABECERA Y CONTORNOS DE CULTIVO PERMANENTE,PERMANENT CROP HEADLAND AND BORDERS,not_known_and_other,3399000000 +336,MEZCLA ALGARROBA-AVENA,CAROB-OAT MIXTURE,carob,3303110100 +337,MEZCLA ALGARROBA-CEBADA,CAROB-BARLEY MIXTURE,carob,3303110100 +338,MEZCLA RAYGRASS-VEZA,Ryegrass Vetch mixture,lolium_ryegrass,3301090205 +339,MEZCLA AVENA-YEROS,Oat vetch mixture,oats,3301010500 +340,MEZCLA CEBADA-YEROS,BARLEY-Vetches MIXTURE,barley,3301010400 +341,SILPHIUM,SILPHIUM,silphium_rosinweeds,3301084400 +342,OTRAS MEZCLAS CON PREDOMINANCIA CFN,Nitrogen-fixing crops (CFN),legumes_harvested_green,3301090300 +343,AGUACATE,AVOCADO,avocado,3303100000 +344,MANGO,MANGO,orchards_fruits,3303010000 +345,CHIRIMOYO,cherimoya,orchards_fruits,3303010000 +346,PAPAYA,PAPAYA,orchards_fruits,3303010000 +347,MORERA,MULBERRY,berries_berry_species,3303020000 +348,VIÑA - FRUTAL,VINEYARD - FRUIT,vineyards_wine_vine_rebland_grapes,3303060000 +349,OLIVAR - FRUTAL,OLIVE - FRUIT,olive_plantations,3303050000 +350,RASTROJERAS,Unmanaged vegetation,unmaintained,3308000000 +351,CÍTRICOS HÍBRIDOS-CAQUI,HYBRID CITRUS-KHAKI,citrus_plantations,3303040000 +352,NARANJO-AGUACATE,ORANGE-AVOCADO,citrus_plantations,3303040000 +353,CEREZO-OLIVAR,CHERRY-OLIVE TREE,cherry_cherries,3303010400 +354,CEREZO-ALGARROBO,CHERRY-CAROB,cherry_cherries,3303010400 +355,RICINO,Castor oil,oilseed_crops,3301060800 +356,NARANJO AMARGO,BITTER ORANGE,citrus_plantations,3303040000 +357,CÁÑAMO NO TEXTIL,NON-TEXTILE HEMP,hemp_cannabis,3301061000 +358,MEZCLA DE FORESTALES,FOREST MIX,tree_wood_forest,3306000000 +359,HIERBA DE GUINEA,GUINEA GRASS,poaceae_grasses,3301090200 +360,ABEDUL,BIRCH,birch,3306030000 +361,ABETO DE DOUGLAS,DOUGLAS FIR,tree_wood_forest,3306000000 +362,ACEBO,Christmas HOLLY,shrubberries_shrubs,3303080000 +363,ACEBUCHE,Wild Olive,olive_plantations,3303050000 +364,ÁLAMO,POPLAR,populus,3306070000 +365,ALANTICO o LANTISCO,Pistacia lentiscus,industrial_nonfood_crops,3301060000 +366,ALERCE,Larch,other_tree_wood_forest,3306990000 +367,ALISO,Adler,birch,3306030000 +368,ALMÁCIGO DE CANARIAS,Pistacia atlantica,other_tree_wood_forest,3306990000 +369,ALMEZ,European nettle tree,other_tree_wood_forest,3306990000 +370,ARCE,MAPLE,other_tree_wood_forest,3306990000 +371,CARPE ,HORNBEAM,other_tree_wood_forest,3306990000 +372,CEDRO,CEDAR,other_tree_wood_forest,3306990000 +373,CHAPARRO,Kermes Oak,oak,3306060000 +374,CHÍA,CHIA,sage_chia,3301190000 +375,CIPRÉS,CYPRESS,other_tree_wood_forest,3306990000 +376,FRESNO,ASH TREE,other_tree_wood_forest,3306990000 +377,MADROÑO ,Strawberry Tree,other_tree_wood_forest,3306990000 +378,OLMO,ELM,other_tree_wood_forest,3306990000 +379,PALMA CANARIA,CANARY PALM,other_tree_wood_forest,3306990000 +380,PICEA,SPRUCE,other_tree_wood_forest,3306990000 +381,QUEJIGO,Portuguese oak,oak,3306060000 +382,REBOLLO,Spanish Oak,oak,3306060000 +383,RETAMA,RETAMA,not_known_and_other,3399000000 +384,SERBAL,Sorbus,other_tree_wood_forest,3306990000 +385,TALAYA,Tamarix,other_tree_wood_forest,3306990000 +386,TEJO,Yew Tree,other_tree_wood_forest,3306990000 +387,TILO,Tilia tree,other_tree_wood_forest,3306990000 +388,YUCA,YUCCA?,other_flowers_ornamental_plants,3301089900 +389,PINO,PINE TREE,other_tree_wood_forest,3306990000 +390,HABONCILLO,Field Beans,beans,3301020100 +391,CEREZO DE MAHOMA,Mahaleb Cherrz,cherry_cherries,3303010400 +392,MEZCLA ALUBIA-MAÍZ,BEAN-CORN MIXTURE,beans,3301020100 +393,ESCANDA,Emmer,emmer,3301011200 +394,ESCAÑA,Spelt,spelt,3301011000 +395,PANIZO,Maiz,grain_maize_corn_popcorn,3301010600 +397,JUDÍA ESCARLATA,SCARLET Runner BEAN,beans,3301020100 +398,RAMIO,Ramie,fibre_crops,3301061100 +399,SÉSAMO,SESAME,oilseed_crops,3301060800 +400,FORESTACIONES VINCULADAS AL REGLAMENTO 2080/1992,Afforestation LINKED TO REGULATION 2080/1992,afforestation_reforestation,3306010000 +401,CEBOLLINO,CHIVE,chives,3301220100 +402,ACEDERILLA,SORREL,sorrel,3301310700 +403,ANÍS ESTRELLADO,STAR ANISE,anise_aniseed,3301061205 +404,BOTÓN DE ORO,Meadow buttercup,not_known_and_other,3399000000 +405,AJOWAN,AJOWAN,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +406,CHUPAMIELES,Anchusa officinalis,borage,3301061209 +409,DEDALERA O DIGITAL,Foxglove,other_flowers_ornamental_plants,3301089900 +410,CICUTA,HEMLOCK,not_known_and_other,3399000000 +411,JABONERA,Wild Sweet William,not_known_and_other,3399000000 +412,MALVA COMÚN,COMMON MALLOW,sida_virginia_mallow,3304030000 +413,PULMONARIA,Lungwort,other_flowers_ornamental_plants,3301089900 +414,CARDO DE FULLER,Wild teasel (honey plant/ornamental),other_flowers_ornamental_plants,3301089900 +415,TÁRTAGO,Caper Spurge,not_known_and_other,3399000000 +416,PATACA,Jerusalem artichoke,topinambur_jerusalem_artichoke,3301180000 +417,CANÓNIGO,Lambs lettuce,lambs_lettuce_rapunzel,3301310500 +418,ACEDERA,Sorrel,sorrel,3301310700 +419,BERRO HORTELANO,GARDEN CRESS,cress,3301210300 +420,RUIBARBO,RHUBARB,rhubarb,3301230000 +421,VERDOLAGA,PURSLANE,purslane,3301240000 +422,KIWANO,KIWANO,cucurbits,3301140000 +423,TOMATILLO,TOMATILLO,berries_berry_species,3303020000 +424,ALCACHOFA CHINA,Chinese artichoke,stachys_hedgenettle_chinese_artichoke,3301061235 +425,GALANGA MENOR,Galangal,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +426,PEREJIL TUBEROSO,Parsley,parsly,3301061227 +427,RÁBANO PICANTE,HORSERADISH,horseradish,3301210400 +428,JUDÍA DE LIMA,Lima Bea,beans,3301020100 +429,COLLEJA,Bladder campion,other_salads_lettuce_leaf_vegetables,3301319900 +430,BERGAMOTO,Bergamot,citrus_plantations,3303040000 +431,CIDRO,CITRUS,citrus_plantations,3303040000 +432,LIMÓN MANDARINA,Rangpur lime,citrus_plantations,3303040000 +433,LIMERO DULCE,Palestinian SWEET LIME,citrus_plantations,3303040000 +434,LIMÓN DULCE DEL MEDITERRÁNEO,Sweet Lime (Citrus limetta),citrus_plantations,3303040000 +435,NARANJO MORUNO,Myrtle leaved orange tree,citrus_plantations,3303040000 +436,ACEROLO,Mediterranean Medlar (crataegus azarolus),crataegus_hawthorn,3303080300 +437,GUINDO,Sour Cherry,cherry_cherries,3303010400 +438,SAUCO,Elderberry ,elder_elderberry,3303080400 +439,ESPINO AMARILLO,SEA BUCKTHORN,hippophae_sea_buckthorns_seaberry,3303020800 +440,PITA,Century Plant,other_flowers_ornamental_plants,3301089900 +441,TÉ,TEA,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +442,MIMBRERA,Osier,willows_osiers,3306080000 +443,BAMBÚ,BAMBOO,other_flowers_ornamental_plants,3301089900 +444,ZUMAQUE,SUMAC,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +445,COL CRESPA,Curly kale,kale,3301210208 +446,SERRADELLA,SERRADELLA,serradella,3301084200 +447,MEZCLA CEREAL-RAYGRASS,CEREAL-RYEGRASS MIXTURE,cereal,3301010000 +448,MEZCLA AVENA-MAÍZ,OAT-CORN MIXTURE,oats,3301010500 +449,MEZCLA HABA-VEZA,BEAN-VETCHES MIXTURE,beans,3301020100 +450,MEZCLA CEBADA-TRIGO,BARLEY-WHEAT MIXTURE,barley,3301010400 +451,MEZCLA CENTENO-TRIGO,RYE-WHEAT MIXTURE,rye,3301010300 +452,MEZCLA AVENA-MAÍZ-VEZA,OAT-CORN-VETCHES MIXTURE,oats,3301010500 +453,MEZCLA CEBADA-MAÍZ-VEZA,BARLEY-CORN-VETCHES MIXTURE,barley,3301010400 +454,MEZCLA ALTRAMUZ-AVENA,LUPINE-OATS MIXTURE,sweet_lupins,3301020700 +455,MEZCLA AVENA-CENTENO,OAT-RYE MIXTURE,oats,3301010500 +456,MEZCLA CENTENO-MAÍZ,RYE-CORN MIXTURE,rye,3301010300 +457,MEZCLA MAÍZ-SORGO,CORN-SORGHUM MIXTURE,grain_maize_corn_popcorn,3301010600 +458,MARALFALFA,MARALFALFA,poaceae_grasses,3301090200 +459,TEDERA,Arabian pea,legumes_harvested_green,3301090300 +460,CAUPÍ,Black-eyed pea,beans,3301020100 +461,MELILOTO AMARILLO ,YELLOW SWEET CLOVER,clover,3301090303 +462,OJO DE BUEY,Velvet bean,legumes_from_trees,3303110000 +463,CHOISUM,CHOISUM,other_brassica_oleracea_cabbage,3301210299 +464,BRÓCOLI CHINO,Kai-lan,gai_lan,3301210207 +465,BATATA ACUÁTICA,Water spinach,other_salads_lettuce_leaf_vegetables,3301319900 +466,CEBOLLINO CHINO,CHINESE CHIVES,other_salads_lettuce_leaf_vegetables,3301319900 +467,LEMONGRASS,LEMONGRASS,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +468,ESPINACA DE MALABAR,MALABAR SPINACH,other_salads_lettuce_leaf_vegetables,3301319900 +469,LUFFA,LUFFA,pumpkin_squash_gourd,3301140400 +470,JUDÍA DE EGIPTO,Lablab purpureus,legumes_dried_pulses_protein_crops,3301020000 +471,CALABAZA AMARGA,BITTER GOURD,pumpkin_squash_gourd,3301140400 +472,PEPINO CULEBRA,Armenian cucumber,cucurbits,3301140000 +473,CALABAZA CHINA,Wax gourd,pumpkin_squash_gourd,3301140400 +474,AROS EGIPCIOS,Arum maculatum,other_flowers_ornamental_plants,3301089900 +475,RÁBANO SILVESTRE,Wild radish,radish,3301290600 +477,SALSIFÍ,SALSIFY,salsify,3301084000 +478,COLINABO,SWEDE,swede_rutabaga,3301210500 +479,ESCORZONERA,Black salsify,salsify,3301084000 +480,BARBAREA,Winter cress,cress,3301210300 +481,NUEZ DE COLA,Kola nut,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +482,ALTRAMUZ AZUL,Blue Lupin,sweet_lupins,3301020700 +483,VEZA VELLOSA,Hairy vetch ,vetches,3301090305 +484,GRELOS,Rapini,other_brassica_oleracea_cabbage,3301210299 +485,BAYAS DE GOJI,GOJI BERRIES,berries_berry_species,3303020000 +486,ALVERJA HÚNGARA,HUNGARIAN PEA,peas,3301020600 +487,CALIFORNIA BLUEBELL,CALIFORNIA BLUEBELL (phacelia),phacelia,3301061400 +488,BISERRULA,BISERRULA,legumes_harvested_green,3301090300 +489,GRAMA COMÚN,Scutch grass,poaceae_grasses,3301090200 +490,MEDICAGO,MEDICAGO,legumes_harvested_green,3301090300 +491,ESPIGUILLA DE BURRO,Soft brome,poaceae_grasses,3301090200 +492,AÑIL,True indigo,legumes_harvested_green,3301090300 +493,ÍNDIGO CHINO,Chinese indigo,buckwheat,3301150200 +496,MEZCLA RAYGRASS-TREBOL,RYEGRASS-CLOVER MIXTURE,lolium_ryegrass,3301090205 +497,APIO-RÁBANO,CELERIAC,celery,3301250000 +498,BROCCOLINI,BROCCOLINI,broccoli,3301210202 +500,FORESTACIONES VINCULADAS AL REGLAMENTO Nº 1257/1999,AfforestationLINKED TO REGULATION Nº 1257/1999,afforestation_reforestation,3306010000 +501,NOGAL PECANO,PECAN,nuts,3303030000 +502,CALABAZA MOSCADA,PUMPKIN / SQUASH / GOURD,pumpkin_squash_gourd,3301140400 +503,NABO SILVESTRE,WILD TURNIP,other_brassica_oleracea_cabbage,3301210299 +504,ALCARAVEA,CARAWAY,caraway,3301061211 +505,PORTAINJERTOS DE CALABAZA,PUMPKIN / SQUASH / GOURD,pumpkin_squash_gourd,3301140400 +506,CIRUELO JAPONÉS,JAPANESE PLUM,plums,3303011300 +507,ACHICORIA DE CAFÉ,ROOT CHICORY,chicory_chicories,3301310200 +508,RÁBANO OLEÍFERO,OILSEED RADISH,radish,3301290600 +509,COL TRONCHUDA,PORTUGUESE KALE,other_brassica_oleracea_cabbage,3301210299 +510,TRISETUM FLAVESCENS,YELLOW OAT-GRASS,poaceae_grasses,3301090200 +511,AGROSTIS CANINA,AGROSTIS / BENTGRASS,poaceae_grasses,3301090200 +512,AGROSTIS CAPILLARIS,AGROSTIS / BENTGRASS,poaceae_grasses,3301090200 +513,AGROSTIS GIGANTEA,AGROSTIS / BENTGRASS,poaceae_grasses,3301090200 +514,AGROSTIS STOLONIFERA,AGROSTIS / BENTGRASS,poaceae_grasses,3301090200 +515,COLA DE ZORRA,FOXTAIL,poaceae_grasses,3301090200 +516,BROMO DE SITKA,SITKA BROMEGRASS,poaceae_grasses,3301090200 +517,FESTUCA ALTA,TALL FESCUE,festuca_fescue,3301090202 +518,FESTUCA OVINA DE HOJA FINA,SHEEP FESCUE,festuca_fescue,3301090202 +519,FESTUCA OVINA,SHEEP FESCUE,festuca_fescue,3301090202 +520,FESTUCA PRATENSE,MEADOW FESCUE,festuca_fescue,3301090202 +521,FESTUCA ROJA,RED FESCUE,festuca_fescue,3301090202 +522,FESTUCA DURA,HARD FESCUE,festuca_fescue,3301090202 +523,GALEGA DE ORIENTE,EASTERN GALEGA,legumes_harvested_green,3301090300 +524,RAYGRASS ITALIANO,ITALIAN RYEGRASS,lolium_ryegrass,3301090205 +525,RAYGRASS WESTERNWORLD,RYEGRASS WESTERNWOLD,lolium_ryegrass,3301090205 +526,RAYGRASS HÍBRIDO,HYBRID RYEGRASS,lolium_ryegrass,3301090205 +527,CARRETÓN,BUR MEDIC,alfalfa_lucerne,3301090301 +528,ALFALFA ITALIANA,ITALIAN ALFALFA,alfalfa_lucerne,3301090301 +529,ALFALFA LITORAL,COASTAL ALFALFA,alfalfa_lucerne,3301090301 +530,LUPULINA,BLACK MEDIC,alfalfa_lucerne,3301090301 +531,ALFALFA ESPINOSA,SPINY MEDIC,alfalfa_lucerne,3301090301 +532,ALFALFA DE BURRO,BURRO ALFALFA,alfalfa_lucerne,3301090301 +533,ALFALFA RUGOSA,WRINKLED MEDIC,alfalfa_lucerne,3301090301 +534,ALFALFA DE ARENA,SAND ALFALFA,alfalfa_lucerne,3301090301 +535,ALFALFA ESCUDETEADA,SHIELD MEDIC,alfalfa_lucerne,3301090301 +536,MEDICAGO TRUNCATULA,BARREL MEDIC,alfalfa_lucerne,3301090301 +537,SERRADELLA AMARILLA,YELLOW SERRADELLA,serradella,3301084200 +538,FACELIA FORRAJERA,PHACELIA,phacelia,3301061400 +539,ALPISTE BULBOSO,BULBOUS CANARY-GRASS,poaceae_grasses,3301090200 +540,FLEO BULBOSO,BULBOUS TIMOTHY,poaceae_grasses,3301090200 +541,LLANTÉN MENOR,RIBWORT PLANTAIN,other_plants_harvested_green,3301099900 +542,POA ANNUA,ANNUAL BLUEGRASS,poaceae_grasses,3301090200 +543,POA DE LOS BOSQUES,WOODLAND BLUEGRASS,poaceae_grasses,3301090200 +544,POA DE LOS PANTANOS,SWAMP MEADOW-GRASS,poaceae_grasses,3301090200 +545,POA DE LOS PRADOS,KENTUCKY BLUEGRASS,poaceae_grasses,3301090200 +546,POA COMÚN,COMMON MEADOW-GRASS,poaceae_grasses,3301090200 +547,TRÉBOL DE ALEJANDRÍA,BERSEEM CLOVER,clover,3301090303 +548,TRÉBOL FRESERO,STRAWBERRY CLOVER,clover,3301090303 +549,TRÉBOL GLANDULAR,GLAND CLOVER,clover,3301090303 +550,TRÉBOL VELLOSO,HAIRY CLOVER,clover,3301090303 +551,TRÉBOL HÍBRIDO,HYBRID CLOVER,clover,3301090303 +552,TRÉBOL ENCARNADO,CRIMSON CLOVER,clover,3301090303 +553,TRÉBOL DE FRUTO ANGOSTO,NARROWLEAF CLOVER,clover,3301090303 +554,TRÉBOL BALANSA,BALANSA CLOVER,clover,3301090303 +555,TRÉBOL VIOLETA,RED CLOVER,clover,3301090303 +556,TRÉBOL BLANCO,WHITE CLOVER,clover,3301090303 +557,TRÉBOL PERSA,PERSIAN CLOVER,clover,3301090303 +558,TRÉBOL SQUARROSO,SQUARROSE CLOVER,clover,3301090303 +559,TRÉBOL SUBTERRANEO,SUBTERRANEAN CLOVER,clover,3301090303 +560,TRÉBOL VESICULOSO,BLADDER CLOVER,clover,3301090303 +561,CAÑUELA,WALL FESCUE,festuca_fescue,3301090202 +562,ARVEJA ROJA,RED VETCH,vetches,3301090305 +563,BERENJENA ETÍOPE,AUBERGINE,aubergine_eggplant,3301260000 +564,HÍBRIDOS DE SOLANUM LYCOPERSICUM Y CHESMANIAE,TOMATO HYBRID,tomato,3301280000 +565,HÍBRIDOS DE SOLANUM LYCOPERSICUM Y PERUVIANUM,TOMATO HYBRID,tomato,3301280000 +566,HÍBRIDOS DE SOLANUM LYCOPERSICUM Y PIMPINELLIFOILUM,TOMATO HYBRID,tomato,3301280000 +567,HÍBRIDOS DE SOLANUM LYCOPERSICUM Y HABROCHAITES,TOMATO HYBRID,tomato,3301280000 +568,BERENJENA CIMARRONA,AUBERGINE,aubergine_eggplant,3301260000 +569,HÍBRIDOS DE SOLANUM MELONGENA Y TORVUM,AUBERGINE HYBRID,aubergine_eggplant,3301260000 +570,HÍBRIDOS DE SOLANUM MELONGENA Y AETHIOPICUM,AUBERGINE HYBRID,aubergine_eggplant,3301260000 +571,TOMATE DE GALÁPAGOS,TOMATE DE GALÁPAGOS,tomato,3301280000 +572,TOMATE CIMARRÓN,TOMATE CIMARRÓN,tomato,3301280000 +573,TOMATE PELUDO,TOMATE PELUDO,tomato,3301280000 +574,TOMATE SILVESTRE,TOMATE SILVESTRE,tomato,3301280000 +575,TRÉBOL GLOMERADO,CLUSTER CLOVER,clover,3301090303 +576,CEBADILLA,BROMEGRASS,poaceae_grasses,3301090200 +577,FROMENTAL,FALSE OAT-GRASS,poaceae_grasses,3301090200 +578,AVENA DESNUDA,NAKED OAT,oats,3301010500 +579,AVENA NEGRA,BLACK OAT,oats,3301010500 +580,MOSTAZA NEGRA,BLACK MUSTARD,mustard,3301210100 +581,COL DE JERSEY,JERSEY KALE,other_brassica_oleracea_cabbage,3301210299 +582,HÍBRIDOS DE CUCUMIS MELO,PUMPKIN / SQUASH / GOURD,pumpkin_squash_gourd,3301140400 +583,CALABAZA DE CABELLO DE ÁNGEL,PUMPKIN / SQUASH / GOURD,pumpkin_squash_gourd,3301140400 +584,RABANITO,RADISH,radish,3301290600 +585,MAÍZ PALOMITERO,POPCORN MAIZE,grain_maize_corn_popcorn,3301010600 +586,GROSELLERO ROJO,REDCURRANT,redcurrant,3303021100 +587,BRACHYPODIUM,BRACHYPODIUM,poaceae_grasses,3301090200 +588,PHACELIA,PHACELIA,phacelia,3301061400 +589,REMOLACHA FORRAJERA,FODDER BEET,mangelwurzel_fodder_beet,3301290400 +590,REMOLACHA AZUCARERA,SUGAR BEET,sugar_beet,3301290700 +591,RABO DE GATO,TIMOTHY-GRASS,poaceae_grasses,3301090200 +592,TRÉBOL ÁSPERO,ROUGH CLOVER,clover,3301090303 +593,SETA DE CHOPO,POPLAR MUSHROOM,mushrooms_energy_genetically_modified_crops,3304000000 +594,SHIITAKE,SHIITAKE MUSHROOM,mushrooms_energy_genetically_modified_crops,3304000000 +595,SETA DE PIE AZUL,BLUEFOOT MUSHROOM,mushrooms_energy_genetically_modified_crops,3304000000 +596,SETA DE CARDO,KING TRUMPET MUSHROOM,mushrooms_energy_genetically_modified_crops,3304000000 +597,SETA DE OSTRA,OYSTER MUSHROOM,mushrooms_energy_genetically_modified_crops,3304000000 +598,HONGO PULMÓN,LUNG OYSTER MUSHROOM,mushrooms_energy_genetically_modified_crops,3304000000 +599,ALGODÓN AMERICANO,COTTON,cotton,3301060300 +600,FORESTACIONES VINCULADAS AL REGLAMENTO Nº 1698/2005,Afforestation LINKED TO REGULATION Nº 1698/2005,afforestation_reforestation,3306010000 +601,ALGODÓN EGIPCIO,COTTON,cotton,3301060300 +602,HÍBRIDOS DE GOSSYPIUM HIRSUTUM Y BARBADENSE,COTTON,cotton,3301060300 +603,KIWIÑO,HARDY KIWI,kiwi,3303010700 +604,KIWI AMARILLO,GOLDEN KIWI,kiwi,3303010700 +605,ACTINIDIA,ACTINIDIA,kiwi,3303010700 +606,KIWI ENANO,HARDY KIWI,kiwi,3303010700 +607,ACTINIDIA VALVATA,ACTINIDIA,kiwi,3303010700 +608,HÍBRIDOS DE ACTINIDIA CHINENSIS Y ARGUTA,ACTINIDIA,kiwi,3303010700 +609,CASTANEA,CHESTNUT,sweet_chestnuts,3303030500 +610,HÍBRIDOS DE CASTANEA CRENATA Y SATIVA,CHESTNUT,sweet_chestnuts,3303030500 +611,CASTAÑO JAPONES,JAPANESE CHESTNUT,sweet_chestnuts,3303030500 +612,HÍBRIDOS DE FRAGARIA IINUMAE Y VESCA,WILD STRAWBERRY HYBRID,berries_berry_species,3303020000 +613,HÍBRIDOS DE JUGLANS MAYOR Y REGIA,WALNUT HYBRID,walnuts,3303030600 +614,ACELGA BRAVA,WILD CHARD,beetroot_beets,3301290200 +615,EUCALIPTO COMÚN,EUCALYPTUS,eucalyptus,3306050000 +616,EUCALIPTO BRILLANTE,EUCALYPTUS,eucalyptus,3306050000 +617,TRUFA DEL DESIERTO,DESERT TRUFFLE,mushrooms_energy_genetically_modified_crops,3304000000 +618,ROBLE ALBAR,OAK,oak,3306060000 +619,ROBLE PUBESCENTE,OAK,oak,3306060000 +620,ROBLE AMERICANO,OAK,oak,3306060000 +621,ÁLAMO BLANCO,WHITE POPLAR,populus,3306070000 +622,ÁLAMO TEMBLÓN,ASPEN,aspen,3306020000 +623,MOSTAZA SILVESTRE,WILD MUSTARD,mustard,3301210100 +624,OLMO COMÚN,ELM,other_tree_wood_forest,3306990000 +625,OLMO DE MONTAÑA,ELM,other_tree_wood_forest,3306990000 +626,JUDÍA ESPÁRRAGO,ASPARAGUS BEAN,legumes_harvested_green,3301090300 +627,ALERCE AFRICANO,LARCH,other_tree_wood_forest,3306990000 +628,ALERCE EUROPEO,LARCH,other_tree_wood_forest,3306990000 +629,ALERCE DEL JAPÓN,LARCH,other_tree_wood_forest,3306990000 +630,ALERCE DE DUNKLED,LARCH,other_tree_wood_forest,3306990000 +631,ARCE BLANCO,MAPLE,other_tree_wood_forest,3306990000 +632,ARCE REAL,MAPLE,other_tree_wood_forest,3306990000 +633,ARCE COMÚN,MAPLE,other_tree_wood_forest,3306990000 +634,PINO PIÑONERO,PINE,other_tree_wood_forest,3306990000 +635,PINO CANARIO,PINE,other_tree_wood_forest,3306990000 +636,PINO CARRASCO,PINE,other_tree_wood_forest,3306990000 +637,PINO INSIGNE,PINE,other_tree_wood_forest,3306990000 +638,PINO NEGRAL,PINE,other_tree_wood_forest,3306990000 +639,PINO NEGRO,PINE,other_tree_wood_forest,3306990000 +640,PINO MARÍTIMO,PINE,other_tree_wood_forest,3306990000 +641,PINO SILVESTRE,PINE,other_tree_wood_forest,3306990000 +642,MADROÑO CANARIO,CANARY ISLAND STRAWBERRY TREE,other_tree_wood_forest,3306990000 +643,CEDRO DEL ATLAS,CEDAR,other_tree_wood_forest,3306990000 +644,CEDRO DEL LÍBANO,CEDAR,other_tree_wood_forest,3306990000 +645,CIPRÉS COMÚN,CYPRESS,other_tree_wood_forest,3306990000 +646,CIPRÉS DE ARIZONA,CYPRESS,other_tree_wood_forest,3306990000 +647,FRESNO DE HOJA ESTRECHA,ASH TREE,other_tree_wood_forest,3306990000 +648,FRESNO COMÚN,ASH TREE,other_tree_wood_forest,3306990000 +649,SERBAL BLANCO,ROWAN / SERVICE TREE,other_tree_wood_forest,3306990000 +650,SERBAL DE LOS CAZADORES,ROWAN / SERVICE TREE,other_tree_wood_forest,3306990000 +651,SERBAL COMÚN,ROWAN / SERVICE TREE,other_tree_wood_forest,3306990000 +652,BLEDO VERDE,GREEN AMARANTH,amaranth,3301150100 +653,BLEDO COMÚN,COMMON AMARANTH,amaranth,3301150100 +654,MOCO DE PAVO,LOVE-LIES-BLEEDING,amaranth,3301150100 +655,AMARANTO ROJO,RED AMARANTH,amaranth,3301150100 +656,SABINA NEGRA,SABINA JUNIPER,other_tree_wood_forest,3306990000 +657,SABINA ALBAR,SABINA JUNIPER,other_tree_wood_forest,3306990000 +658,SABINA RASTRERA,SABINA JUNIPER,other_tree_wood_forest,3306990000 +659,CLAVEL,CARNATION,flowers_ornamental_plants,3301080000 +660,ROSA,ROSE,flowers_ornamental_plants,3301080000 +661,ANTHURIUM,ANTHURIUM,flowers_ornamental_plants,3301080000 +662,ASTER,ASTER,flowers_ornamental_plants,3301080000 +663,CATTLEYA,CATTLEYA,flowers_ornamental_plants,3301080000 +664,FREESIA,FREESIA,flowers_ornamental_plants,3301080000 +665,GERBERA,GERBERA,flowers_ornamental_plants,3301080000 +666,GYPSOPHILA,GYPSOPHILA,flowers_ornamental_plants,3301080000 +667,IRIS,IRIS,flowers_ornamental_plants,3301080000 +668,LIRIO DE PASCUA,EASTER LILY,flowers_ornamental_plants,3301080000 +669,PROTEA,PROTEA,flowers_ornamental_plants,3301080000 +670,SOLIDASTER,SOLIDASTER,flowers_ornamental_plants,3301080000 +671,SOLIDAGO,SOLIDAGO,flowers_ornamental_plants,3301080000 +672,SIEMPREVIVA AZUL,BLUE STATICE,flowers_ornamental_plants,3301080000 +673,AVE DEL PARAÍSO,BIRD OF PARADISE,flowers_ornamental_plants,3301080000 +674,CLAVEL CHINO,CHINESE PINK,flowers_ornamental_plants,3301080000 +675,CLAVEL DEL POETA,SWEET WILLIAM,flowers_ornamental_plants,3301080000 +676,ADENANTHOS,ADENANTHOS,flowers_ornamental_plants,3301080000 +677,AGAPANTHUS,AGAPANTHUS,flowers_ornamental_plants,3301080000 +678,ALOCASIA,ALOCASIA,flowers_ornamental_plants,3301080000 +679,ALOE,ALOE,flowers_ornamental_plants,3301080000 +680,ALTHERNANTERA,ALTHERNANTERA,flowers_ornamental_plants,3301080000 +681,ALYOGYNE,ALYOGYNE,flowers_ornamental_plants,3301080000 +682,ALYSSUM,ALYSSUM,flowers_ornamental_plants,3301080000 +683,ANYZOGANTHUS,ANYZOGANTHUS,flowers_ornamental_plants,3301080000 +684,AQUILEGIA,AQUILEGIA,flowers_ornamental_plants,3301080000 +685,ARAUCARIA,ARAUCARIA,flowers_ornamental_plants,3301080000 +686,ARCTOTIS,ARCTOTIS,flowers_ornamental_plants,3301080000 +687,ARECA,ARECA,flowers_ornamental_plants,3301080000 +688,ARGYRANTEMUM,ARGYRANTEMUM,flowers_ornamental_plants,3301080000 +689,ARMERIA,ARMERIA,flowers_ornamental_plants,3301080000 +690,ASPLENIUM,ASPLENIUM,flowers_ornamental_plants,3301080000 +691,ALSTROEMERIA,ALSTROEMERIA,flowers_ornamental_plants,3301080000 +692,ANTIRRHINUM,ANTIRRHINUM,flowers_ornamental_plants,3301080000 +693,RHODODENDRON,RHODODENDRON,flowers_ornamental_plants,3301080000 +694,BEGONIA,BEGONIA,flowers_ornamental_plants,3301080000 +695,BEGONIA ELIATOR,BEGONIA ELIATOR,flowers_ornamental_plants,3301080000 +696,BEGONIA SEMPERFLORENS,BEGONIA SEMPERFLORENS,flowers_ornamental_plants,3301080000 +697,BELLIS,BELLIS,flowers_ornamental_plants,3301080000 +698,BEAUCARNEA,BEAUCARNEA,flowers_ornamental_plants,3301080000 +699,BIGNONIA,BIGNONIA,flowers_ornamental_plants,3301080000 +700,SUP DE AGROSILVICULTURA QUE RECIBA O HAYA RECIBIDO AYUDAS DE LOS REGLAMENTOS 1698/2005 Y/O 1305/2013,AGROSILVICULTURE SUP THAT RECEIVES OR HAS RECEIVED AID FROM REGULATIONS 1698/2005 AND/OR 1305/2013,not_known_and_other,3399000000 +701,BORONIA,BORONIA,flowers_ornamental_plants,3301080000 +702,BULBINE,BULBINE,flowers_ornamental_plants,3301080000 +703,CAESALPINIA,CAESALPINIA,flowers_ornamental_plants,3301080000 +704,CALCEOLARIA,CALCEOLARIA,flowers_ornamental_plants,3301080000 +705,CALIBRACHOA,CALIBRACHOA,flowers_ornamental_plants,3301080000 +706,CALLA,CALLA,flowers_ornamental_plants,3301080000 +707,CALLISTEMON,CALLISTEMON,flowers_ornamental_plants,3301080000 +708,CAMELLIA,CAMELLIA,flowers_ornamental_plants,3301080000 +709,CANNA,CANNA,flowers_ornamental_plants,3301080000 +710,CAREX,CAREX,flowers_ornamental_plants,3301080000 +711,CARISSA,CARISSA,flowers_ornamental_plants,3301080000 +712,CEREZA DE NATAL,NATAL CHERRY,flowers_ornamental_plants,3301080000 +713,CASSIA,CASSIA,flowers_ornamental_plants,3301080000 +714,CEANOTHUS,CEANOTHUS,flowers_ornamental_plants,3301080000 +715,CERATONIA,CERATONIA,flowers_ornamental_plants,3301080000 +716,CESTRUM,CESTRUM,flowers_ornamental_plants,3301080000 +717,CYCAS,CYCAS,flowers_ornamental_plants,3301080000 +718,CINERARIA,CINERARIA,flowers_ornamental_plants,3301080000 +719,CLIVIA,CLIVIA,flowers_ornamental_plants,3301080000 +720,CODIAEUM,CODIAEUM,flowers_ornamental_plants,3301080000 +721,CÓLEO,CÓLEO,flowers_ornamental_plants,3301080000 +722,CONVOLVULUS,CONVOLVULUS,flowers_ornamental_plants,3301080000 +723,COPROSMA,COPROSMA,flowers_ornamental_plants,3301080000 +724,CORDYLINE,CORDYLINE,flowers_ornamental_plants,3301080000 +725,CORREA,CORREA,flowers_ornamental_plants,3301080000 +726,CRASSULA,CRASSULA,flowers_ornamental_plants,3301080000 +727,CYPERUS,CYPERUS,flowers_ornamental_plants,3301080000 +728,DAHLIA,DAHLIA,flowers_ornamental_plants,3301080000 +729,DAPHNE,DAPHNE,flowers_ornamental_plants,3301080000 +730,DASYLIRION,DASYLIRION,flowers_ornamental_plants,3301080000 +731,DELOSPERMA,DELOSPERMA,flowers_ornamental_plants,3301080000 +732,DIANELLA,DIANELLA,flowers_ornamental_plants,3301080000 +733,DIMORPHOTHECA,DIMORPHOTHECA,flowers_ornamental_plants,3301080000 +734,DIOSMA,DIOSMA,flowers_ornamental_plants,3301080000 +735,DODONAEA,DODONAEA,flowers_ornamental_plants,3301080000 +736,DRACAENA,DRACAENA,flowers_ornamental_plants,3301080000 +737,BOCA DE DRAGÓN,SNAPDRAGON,flowers_ornamental_plants,3301080000 +738,DURANTA,DURANTA,flowers_ornamental_plants,3301080000 +739,ECHIUM,ECHIUM,flowers_ornamental_plants,3301080000 +740,EPIPREMNUM,EPIPREMNUM,flowers_ornamental_plants,3301080000 +741,EREMOPHILA,EREMOPHILA,flowers_ornamental_plants,3301080000 +742,ERIOSTEMON,ERIOSTEMON,flowers_ornamental_plants,3301080000 +743,EUONYMUS,EUONYMUS,flowers_ornamental_plants,3301080000 +744,EUPHORBIA,EUPHORBIA,flowers_ornamental_plants,3301080000 +745,FARFUGIUM,FARFUGIUM,flowers_ornamental_plants,3301080000 +746,FICUS,FICUS,flowers_ornamental_plants,3301080000 +747,FLOR DE CERA,WAX FLOWER,flowers_ornamental_plants,3301080000 +748,FLOR DE PAPEL,PAPER DAISY,flowers_ornamental_plants,3301080000 +749,FUCHSIA,FUCHSIA,flowers_ornamental_plants,3301080000 +750,SUPERFICIES VINCULADAS A LA DIRECTIVA 92/43/CEE,SURFACES LINKED TO DIRECTIVE 92/43/EEC,not_known_and_other,3399000000 +751,GALVEZIA,GALVEZIA,flowers_ornamental_plants,3301080000 +752,GARDENIA,GARDENIA,flowers_ornamental_plants,3301080000 +753,GAURA,GAURA,flowers_ornamental_plants,3301080000 +754,GAZANIA,GAZANIA,flowers_ornamental_plants,3301080000 +755,GITANILLA,IVY-LEAVED GERANIUM,flowers_ornamental_plants,3301080000 +756,HALIMIUM,HALIMIUM,flowers_ornamental_plants,3301080000 +757,HEBE,HEBE,flowers_ornamental_plants,3301080000 +758,HEDYCHIUM,HEDYCHIUM,flowers_ornamental_plants,3301080000 +759,HORTENSIA,HYDRANGEA,flowers_ornamental_plants,3301080000 +760,JACOBINIA,JACOBINIA,flowers_ornamental_plants,3301080000 +761,JAZMIN ESTRELLADO,JASMINE,flowers_ornamental_plants,3301080000 +762,JAZMIN CHINO,JASMINE,flowers_ornamental_plants,3301080000 +763,KALANCHOE,KALANCHOE,flowers_ornamental_plants,3301080000 +764,KENTIA,KENTIA,flowers_ornamental_plants,3301080000 +765,LANTANA,LANTANA,flowers_ornamental_plants,3301080000 +766,LEPTOSPERMUM,LEPTOSPERMUM,flowers_ornamental_plants,3301080000 +767,LEWISIA,LEWISIA,flowers_ornamental_plants,3301080000 +768,LIMONIASTRUM,LIMONIASTRUM,flowers_ornamental_plants,3301080000 +769,FALSO JAZMÍN,FALSE JASMINE,flowers_ornamental_plants,3301080000 +770,LOBELIA,LOBELIA,flowers_ornamental_plants,3301080000 +771,LOBULARIA,LOBULARIA,flowers_ornamental_plants,3301080000 +772,MAGNOLIA,MAGNOLIA,flowers_ornamental_plants,3301080000 +773,MYRSINE,MYRSINE,flowers_ornamental_plants,3301080000 +774,MYRTUS,MYRTUS,flowers_ornamental_plants,3301080000 +775,MUEHLENBECKIA,MUEHLENBECKIA,flowers_ornamental_plants,3301080000 +776,MUSA,MUSA,flowers_ornamental_plants,3301080000 +777,NEPHROLEPIS,NEPHROLEPIS,flowers_ornamental_plants,3301080000 +778,FEIJOA,FEIJOA,unspecified_orchards_fruits,3303019800 +779,NERIUM,NERIUM,flowers_ornamental_plants,3301080000 +780,PATA DE ELEFANTE,ELEPHANT FOOT (BEAUCARNEA),flowers_ornamental_plants,3301080000 +781,OPERCULINA,OPERCULINA,flowers_ornamental_plants,3301080000 +782,OZOTHAMNUS,OZOTHAMNUS,flowers_ornamental_plants,3301080000 +783,PACHIRA,PACHIRA,flowers_ornamental_plants,3301080000 +784,PAPIRO EGIPCIO,EGYPTIAN PAPYRUS,flowers_ornamental_plants,3301080000 +785,PASSIFLORA,PASSIFLORA,flowers_ornamental_plants,3301080000 +786,PELARGONIUM,PELARGONIUM,flowers_ornamental_plants,3301080000 +787,PEROVSKIA,PEROVSKIA,flowers_ornamental_plants,3301080000 +788,PETUNIA,PETUNIA,flowers_ornamental_plants,3301080000 +789,PHILODENDRON,PHILODENDRON,flowers_ornamental_plants,3301080000 +790,PIERIS,PIERIS,flowers_ornamental_plants,3301080000 +791,PIMELEA,PIMELEA,flowers_ornamental_plants,3301080000 +792,RHAPHIOLEPIS,RHAPHIOLEPIS,flowers_ornamental_plants,3301080000 +793,PLUMBAGO,PLUMBAGO,flowers_ornamental_plants,3301080000 +794,PLUMERIA,PLUMERIA,flowers_ornamental_plants,3301080000 +795,POLÍGALA,POLYGALA,flowers_ornamental_plants,3301080000 +796,PORTULACA,PORTULACA,flowers_ornamental_plants,3301080000 +797,PHORMIUM,PHORMIUM,flowers_ornamental_plants,3301080000 +798,PRÍMULA,PRIMROSE,flowers_ornamental_plants,3301080000 +799,SIDA FALLAX,SIDA FALLAX,flowers_ornamental_plants,3301080000 +800,FORESTACIONES VINCULADAS AL REGLAMENTO Nº 1305/2013,,afforestation_reforestation,3306010000 +801,QUISQUALIS,QUISQUALIS,flowers_ornamental_plants,3301080000 +802,RHAPIS,RHAPIS,flowers_ornamental_plants,3301080000 +803,PALMA DE VIAJERO,TRAVELLER'S PALM,flowers_ornamental_plants,3301080000 +804,RUSSELIA,RUSSELIA,flowers_ornamental_plants,3301080000 +805,SANSEVIERIA,SANSEVIERIA,flowers_ornamental_plants,3301080000 +806,SARCOPOTERIUM,SARCOPOTERIUM,flowers_ornamental_plants,3301080000 +807,SCHEFFLERA,SCHEFFLERA,flowers_ornamental_plants,3301080000 +808,SPATHIPHYLLUM,SPATHIPHYLLUM,flowers_ornamental_plants,3301080000 +809,STEPHANOTIS,STEPHANOTIS,flowers_ornamental_plants,3301080000 +810,STIPA,STIPA,flowers_ornamental_plants,3301080000 +811,SYNGONIUM,SYNGONIUM,flowers_ornamental_plants,3301080000 +812,TEUCRIUM,TEUCRIUM,flowers_ornamental_plants,3301080000 +813,THUJA,THUJA,flowers_ornamental_plants,3301080000 +814,TRACHELOSPERMUM,TRACHELOSPERMUM,flowers_ornamental_plants,3301080000 +815,TULBAGHIA,TULBAGHIA,flowers_ornamental_plants,3301080000 +816,VINCA,VINCA,flowers_ornamental_plants,3301080000 +817,VITEX,VITEX,flowers_ornamental_plants,3301080000 +818,VIBURNUM,VIBURNUM,flowers_ornamental_plants,3301080000 +819,WESTRINGIA,WESTRINGIA,flowers_ornamental_plants,3301080000 +820,YUCCA,YUCCA,flowers_ornamental_plants,3301080000 +821,METROSIDEROS,POHUTUKAWA / METROSIDEROS,other_tree_wood_forest,3306990000 +822,TSUGA,HEMLOCK,other_tree_wood_forest,3306990000 +823,EPHEDRA,EPHEDRA,shrubberries_shrubs,3303080000 +824,ÁRBOL DEL AMOR,JUDAS TREE,other_tree_wood_forest,3306990000 +825,ACACIA DEL JAPÓN,JAPANESE PAGODA TREE,other_tree_wood_forest,3306990000 +826,TIPUANA,TIPUANA,other_tree_wood_forest,3306990000 +827,OSTRIA,HOP-HORNBEAM,other_tree_wood_forest,3306990000 +828,PLATANUS,PLANE TREE,other_tree_wood_forest,3306990000 +829,AESCULUS,HORSE CHESTNUT,other_tree_wood_forest,3306990000 +830,LAUREL,LAUREL,other_tree_wood_forest,3306990000 +831,GINKGO,GINKGO,other_tree_wood_forest,3306990000 +832,BRAQUIQUITO,BOTTLE TREE,other_tree_wood_forest,3306990000 +833,ÁRBOL DE LA LLAMA,FLAME TREE,other_tree_wood_forest,3306990000 +834,GREVILLEA,GREVILLEA,other_tree_wood_forest,3306990000 +835,CATALPA COMÚN,COMMON CATALPA,other_tree_wood_forest,3306990000 +836,ÁRBOL PICA-PICA,STING TREE,other_tree_wood_forest,3306990000 +837,OMBÚ,OMBÚ,other_tree_wood_forest,3306990000 +838,CHAENOMELES,JAPANESE QUINCE,shrubberries_shrubs,3303080000 +839,PYRACANTHA,FIRETHORN,shrubberries_shrubs,3303080000 +840,COTONEASTER,COTONEASTER,shrubberries_shrubs,3303080000 +841,AMELANCHIER,SERVICEBERRY,amelanchier_serviceberry,3303010100 +842,ESPINO ALBAR,HAWTHORN,shrubberries_shrubs,3303080000 +843,NÍSPERO EUROPEO,COMMON MEDLAR,unspecified_orchards_fruits,3303019800 +844,ÁRBOL DEL PARAÍSO,RUSSIAN OLIVE,other_tree_wood_forest,3306990000 +845,LIGUSTRUM,PRIVET,shrubberries_shrubs,3303080000 +846,LILO,LILAC,shrubberries_shrubs,3303080000 +847,LABIÉRNAGO,PHILLYREA,shrubberries_shrubs,3303080000 +848,JASMINUM,JASMINE,shrubberries_shrubs,3303080000 +849,CINAMOMO,CHINABERRY,other_tree_wood_forest,3306990000 +850,OTRAS SUPERFICIES FORESTALES-VUELO,OTHER FOREST AREAS-FLIGHT,tree_wood_forest,3306000000 +851,TAMARIX,TAMARISK,shrubberries_shrubs,3303080000 +852,RHAMNUS,BUCKTHORN,shrubberries_shrubs,3303080000 +853,HEDERA,IVY,flowers_ornamental_plants,3301080000 +854,CORNICABRA,TURPENTINE TREE,other_tree_wood_forest,3306990000 +855,ÁRBOL DE LAS PELUCAS,SMOKETREE,shrubberries_shrubs,3303080000 +856,FALSO PIMENTERO,PERUVIAN PEPPER TREE,other_tree_wood_forest,3306990000 +857,BUXUS,BOXWOOD,shrubberries_shrubs,3303080000 +858,BOUGAINVILLEA,BOUGAINVILLEA,flowers_ornamental_plants,3301080000 +859,CELINDA,MOCK ORANGE,shrubberries_shrubs,3303080000 +860,HIBISCUS,HIBISCUS,flowers_ornamental_plants,3301080000 +861,ERICA,HEATHER,shrubberries_shrubs,3303080000 +862,GAYUBA,BEARBERRY,shrubberries_shrubs,3303080000 +863,PARTHENOCISSUS,VIRGINIA CREEPER / CISSUS,flowers_ornamental_plants,3301080000 +864,CISSUS,VIRGINIA CREEPER / CISSUS,flowers_ornamental_plants,3301080000 +865,AGAVE,AGAVE,flowers_ornamental_plants,3301080000 +866,GUANÁBANO,SOURSOP,unspecified_orchards_fruits,3303019800 +867,ÁRBOL DE LA NUEZ DE MACADAMIA,MACADAMIA NUT,nuts,3303030000 +868,HIERBA LIMÓN,LEMONGRASS,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +869,CHAYOTE,CHAYOTE,pumpkin_squash_gourd,3301140400 +870,ANGÉLICA,ANGELICA,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +871,RUDA,RUE,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +872,LEVÍSTICO,LOVAGE,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +873,HELICHRYSUM,HELICHRYSUM,flowers_ornamental_plants,3301080000 +874,CYCLAMEN,CYCLAMEN,flowers_ornamental_plants,3301080000 +875,GLADIOLUS,GLADIOLUS,flowers_ornamental_plants,3301080000 +876,HYACINTHUS,HYACINTHUS,flowers_ornamental_plants,3301080000 +877,LIRIO ARAÑA,SPIDER LILY,flowers_ornamental_plants,3301080000 +878,TULIPA,TULIPA,flowers_ornamental_plants,3301080000 +879,NARCISSUS,NARCISSUS,flowers_ornamental_plants,3301080000 +880,LILIUM,LILIUM,flowers_ornamental_plants,3301080000 +881,VIOLA,VIOLA,flowers_ornamental_plants,3301080000 +882,CHRYSANTHEMUM,CHRYSANTHEMUM,flowers_ornamental_plants,3301080000 +883,AGERATUM,AGERATUM,flowers_ornamental_plants,3301080000 +884,IMPATIENS,IMPATIENS,flowers_ornamental_plants,3301080000 +885,LIRIO DE LOS VALLES,LILY OF THE VALLEY,flowers_ornamental_plants,3301080000 +886,TAGETES,TAGETES,flowers_ornamental_plants,3301080000 +887,CAPUCHINA,NASTURTIUM,flowers_ornamental_plants,3301080000 +888,FICUS RABUDO,FICUS RUBIGINOSA,flowers_ornamental_plants,3301080000 +889,FICUS BENJAMINA,WEEPING FIG,flowers_ornamental_plants,3301080000 +890,HIGUERA CAUCHERA,RUBBER FIG,flowers_ornamental_plants,3301080000 +891,FICUS DE HOJA DE VIOLÍN,FIDDLE-LEAF FIG,flowers_ornamental_plants,3301080000 +892,HIGUERA DE BAHÍA MORETÓN,MORETON BAY FIG,flowers_ornamental_plants,3301080000 +893,LAUREL DE INDIAS,LAUREL,other_tree_wood_forest,3306990000 +894,HIGUERA HERRUMBROSA,RUSTY FIG,flowers_ornamental_plants,3301080000 +895,CAMELIA JAPONESA,JAPANESE CAMELLIA,flowers_ornamental_plants,3301080000 +896,CHEFLERA,SCHEFFLERA,flowers_ornamental_plants,3301080000 +897,MIRTO,MYRTLE,flowers_ornamental_plants,3301080000 +898,ADELFA,OLEANDER,flowers_ornamental_plants,3301080000 +899,FLOR DE PASCUA,POINSETTIA,flowers_ornamental_plants,3301080000 +900,SINAPIS ALBA,White Mustard,mustard,3301210100 +901,BRASSICA CARINATA,Ethiopian rape/mustard,oilseed_crops,3301060800 +902,BRASSICA JUNCEA,MUSTARD,mustard,3301210100 +903,OTRAS CRUCIFERAS,Other cruciferae,brassicaceae_cruciferae,3301210000 +904,CROTALARIA JUNCEA,Sunn Hemp,legumes_harvested_green,3301090300 +905,PLATANERA,Banana,orchards_fruits,3303010000 +906,PIÑA,PINEAPPLE,orchards_fruits,3303010000 +907,AZUFAIFO,JUjube,orchards_fruits,3303010000 +908,CARAMBOLO,Starfruit,orchards_fruits,3303010000 +909,NASHI,NASHI,pears,3303011200 +910,LITCHI,Lychee,orchards_fruits,3303010000 +911,KUMQUAT,KUMQUAT,citrus_plantations,3303040000 +912,LIMEQUAT,LIMEQUAT,citrus_plantations,3303040000 +913,MANO DE BUDA,BUDDHA HAND,citrus_plantations,3303040000 +914,CAVIAR CÍTRICO/ FINGER LIME,CITRUS CAVIAR/ FINGER LIME,citrus_plantations,3303040000 +915,LIMA,Lime,citrus_plantations,3303040000 +916,KALE,KALE,kale,3301210208 +918,ARGÁN,Argan,tree_wood_forest,3306000000 +919,PITAYA,Dragonfruit,orchards_fruits,3303010000 +920,LONGAN,LONGAN,orchards_fruits,3303010000 +921,MANGOSTÁN,MANGOSTEEN,orchards_fruits,3303010000 +922,RAMBUTÁN,RAMBUTAN,orchards_fruits,3303010000 +923,GUAYABO,Guava,orchards_fruits,3303010000 +924,ZAPOTE,Sapote,orchards_fruits,3303010000 +925,ALFICOZ,Armenian cucumber,cucurbits,3301140000 +926,BATATA,SWEET POTATO,sweet_potatoes,3301040000 +927,OKRA,OKRA,plants_harvested_green,3301090000 +928,PHYSALIS,PHYSALIS,berries_berry_species,3303020000 +929,RÚCULA,ARUGULA,rocket_arugula,3301310600 +930,SALICORNIA,Glassworts,not_known_and_other,3399000000 +931,ESQUEJES,CUTTINGS,nurseries_nursery,3303070000 +932,PLANTAS ORNAMENTALES,ORNAMENTAL PLANTS,flowers_ornamental_plants,3301080000 +933,ÑAME,YAM,root_vegetables,3301290000 +934,OTROS CEREALES,OTHER CEREALS,other_cereals,3301019900 +935,OTRAS FORRAJERAS,OTHER FORAGE,not_known_and_other,3399000000 +936,TAGASASTE,Tree Lucerne,legumes_from_trees,3303110000 +937,CUERNECILLO ,Birds foot trefoil,other_flowers_ornamental_plants,3301089900 +938,FRESÓN,STRAWBERRY,strawberries,3301130000 +939,ALMENDRO-OLIVAR,ALMOND-OLIVE,almond,3303030100 +940,NARANJO.CAQUI,ORANGE.KAKI,citrus_plantations,3303040000 +941,NARANJO-MANDARINO,ORANGE-MANDARIN,citrus_plantations,3303040000 +942,NÍSPERO-CAQUI,MEDLAR-KAKI,medlar_loquat,3303010800 +943,PALMERA DATILERA,DATE PALM,other_tree_wood_forest,3306990000 +944,MARACUYÁ o FRUTO DE LA PASIÓN,PASSION FRUIT,orchards_fruits,3303010000 +945,MEZCLA AVENA-HABA,OAT-BEAN MIXTURE,oats,3301010500 +946,MEZCLA AVENA-CEBADA-VEZA,OATS-BARLEY-Vetches MIXTURE,oats,3301010500 +947,JOJOBA,JOJOBA,shrubberries_shrubs,3303080000 +948,CLEMENTINA-CAQUI,CLEMENTINE-KAKI,citrus_plantations,3303040000 +949,SATSUMA-CAQUI,SATSUMA-KHAKI,citrus_plantations,3303040000 +950,Cúrcuma,TURMERIC,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +951,Jengibre,GINGER,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +952,Col Picuda,POINTED CABBAGE,white_cabbage,3301210212 +953,MEZCLA PRATENSES CON PREDOMINANCIA CULTIVOS FIJADORES NITROGENO,MEADOW MIXTURE WITH PREDOMINANT NITROGEN-FIXING CROPS,legumes_harvested_green,3301090300 +954,MEZCLA VEZA-GUISANTE-TRÉBOL,VETCH-PEA-CLOVER MIXTURE,vetches,3301090305 +955,YUZU,YUZU,unspecified_orchards_fruits,3303019800 +956,MEZCLA TRITICALE-GUISANTE,TRITICALE-PEA MIXTURE,other_arable_land_crops,3301990000 +957,JUDÍA MUNGO,MUNG BEAN,legumes_dried_pulses_protein_crops,3301020000 +958,MEZCLA TRITICALE-CEBADA,TRITICALE-BARLEY MIXTURE,other_arable_land_crops,3301990000 +959,MEZCLA RAYGRASS,RYEGRASS MIXTURE,lolium_ryegrass,3301090205 +960,JAZMÍN DEL CABO,JASMINE,flowers_ornamental_plants,3301080000 +961,AJO PUERRO,ELEPHANT GARLIC,garlic,3301220200 +962,HERNIARIA,RUPTUREWORT,other_plants_harvested_green,3301099900 +963,CALABAZA SERPIENTE,PUMPKIN / SQUASH / GOURD,pumpkin_squash_gourd,3301140400 +964,GUAYULE,GUAYULE,other_industrial_crops,3301069900 +965,CEREZO JAPONÉS,JAPANESE CHERRY,cherry_cherries,3303010400 +966,CIRUELO MIROBÁLANO,CHERRY PLUM,plums,3303011300 +967,ALMENDRO DE FLOR,FLOWERING ALMOND,nuts,3303030000 +968,CEREZO RASTRERO,GROUND CHERRY,cherry_cherries,3303010400 +969,CEREZO ALISO,BIRD CHERRY,cherry_cherries,3303010400 +970,LAUREL CEREZO,CHERRY LAUREL,cherry_cherries,3303010400 +971,LAUREL DE PORTUGAL,PORTUGUESE LAUREL,shrubberries_shrubs,3303080000 +972,MANZANO SILVESTRE JAPONÉS,JAPANESE CRABAPPLE,apples,3303010200 +973,MANZANO ROJO,RED CRABAPPLE,apples,3303010200 +974,MANZANO SILVESTRE AMERICANO,AMERICAN CRABAPPLE,apples,3303010200 +975,MANZANO SILVESTRE CHINO,CHINESE CRABAPPLE,apples,3303010200 +976,MANZANO DE ADORNO,ORNAMENTAL CRABAPPLE,apples,3303010200 +977,MANZANO SILVESTRE,WILD CRABAPPLE,apples,3303010200 +978,PERAL SILVESTRE,WILD PEAR,pears,3303011200 +979,PIRUÉTANO,WILD PEAR (PIRUÉTANO),pears,3303011200 +980,PERAL DE CALLERY,CALLERY PEAR,pears,3303011200 +981,PERAL DE HOJAS DE SAUCE,WILLOW-LEAF PEAR,pears,3303011200 +982,PERAL DE REGEL,REGEL PEAR,pears,3303011200 +983,FALSA ACACIA,BLACK LOCUST,other_tree_wood_forest,3306990000 +984,JACARANDÁ AZUL,JACARANDA,other_tree_wood_forest,3306990000 +985,HIERBA SALADA,SEEPWEED,other_plants_harvested_green,3301099900 +986,VARA DE ORO EUROPEA,EUROPEAN GOLDENROD,flowers_ornamental_plants,3301080000 +987,ESPONJA VEGETAL,LUFFA / LOOFAH,pumpkin_squash_gourd,3301140400 diff --git a/tests/test_convert.py b/tests/test_convert.py index 45dfa7d3..48914a14 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -91,17 +91,31 @@ def _input_files(converter, *names): @mark.parametrize("converter", tests) +@patch("fiboa_cli.datasets.commons.hcat.load_ec_mapping") @patch("fiboa_cli.datasets.commons.ec.load_ec_mapping") -def test_converter(load_ec_mock, capsys, tmp_parquet_file, converter): +def test_converter(load_ec_mock, load_hcat_mock, capsys, tmp_parquet_file, converter): from fiboa_cli import Registry # noqa def load_ec(csv_file=None, url=None): + original = (csv_file, url) if csv_file and "://" in csv_file: csv_file = csv_file.split("/")[-1] path = url if url and "://" not in url else f"{test_path}/{converter}/{csv_file}" - return list(DictReader(open(path, "r", encoding="utf-8"))) + try: + return list(DictReader(open(path, "r", encoding="utf-8"))) + except FileNotFoundError: + # no local fixture for this mapping: fetch the real one (old behavior) + from io import StringIO + + from vecorel_cli.vecorel.util import load_file + + from fiboa_cli.datasets.commons.hcat import ec_url + + real = original[1] or ec_url(original[0]) + return list(DictReader(StringIO(load_file(real).decode("utf-8")))) load_ec_mock.side_effect = load_ec + load_hcat_mock.side_effect = load_ec logger.remove() logger.add(sys.stdout, format="{message}", level="DEBUG", colorize=False) From b740a07096af70168dcc0d6f06a95d7cf9bb145d Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 14:07:41 +0200 Subject: [PATCH 53/94] PerFileConverter: remove part files after a successful merge Parts only serve resuming a failed run; leaving them next to the merged output gets them uploaded by publish pipelines that glob *.parquet. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/per_file.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 9b9e86b0..86beb4cf 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -79,6 +79,7 @@ def convert( compression=compression or "zstd", compression_level=compression_level, geoparquet_version=geoparquet_version, + cleanup_parts=True, ) return output_file From d26c300ad2be5bed04a1ea5562e3b660ea72b3bc Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 24 Aug 2026 14:59:38 +0200 Subject: [PATCH 54/94] PerFileConverter: vendor the Hilbert helpers missing from released vecorel-cli vecorel_cli.vecorel.hilbert exists in no released version (per_file.py was written against unreleased 0.2.16); fall back to a fiboa_cli implementation on geopandas' hilbert-curve internals with a per-CRS area-of-use reference frame. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/hilbert.py | 57 ++++++++++++++++++++++++++++++++ fiboa_cli/conversion/per_file.py | 10 ++++-- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 fiboa_cli/conversion/hilbert.py diff --git a/fiboa_cli/conversion/hilbert.py b/fiboa_cli/conversion/hilbert.py new file mode 100644 index 00000000..b3484efd --- /dev/null +++ b/fiboa_cli/conversion/hilbert.py @@ -0,0 +1,57 @@ +"""Hilbert-order helpers for the per-file streaming merge. + +``PerFileBaseConverter`` was written against a ``vecorel_cli.vecorel.hilbert`` +module that is not part of any released vecorel-cli; this module provides the +same two functions on top of geopandas' Hilbert-curve internals. The absolute +values do not matter — only that every part file and the merge use the *same* +deterministic reference grid, which ``crs_total_bounds`` guarantees per CRS. +""" + +from __future__ import annotations + +import numpy as np + +LEVEL = 16 # 2^16 x 2^16 grid, geopandas' default precision + + +def crs_total_bounds(crs) -> tuple[float, float, float, float]: + """A fixed (xmin, ymin, xmax, ymax) reference frame for a CRS. + + Uses the CRS's declared area of use, projected into the CRS by sampling + the area's corners and edge midpoints (projection edges can bow outward). + Falls back to the full lon/lat world when no area of use is declared. + """ + from pyproj import CRS, Transformer + + c = CRS.from_user_input(crs) + aou = c.area_of_use + if aou is None: + west, south, east, north = -180.0, -90.0, 180.0, 90.0 + else: + west, south, east, north = aou.west, aou.south, aou.east, aou.north + if c.is_geographic: + return (west, south, east, north) + t = Transformer.from_crs(c.geodetic_crs, c, always_xy=True) + lons = np.array([west, (west + east) / 2, east]) + lats = np.array([south, (south + north) / 2, north]) + grid_lon, grid_lat = np.meshgrid(lons, lats) + xs, ys = t.transform(grid_lon.ravel(), grid_lat.ravel()) + xs = np.asarray(xs)[np.isfinite(xs)] + ys = np.asarray(ys)[np.isfinite(ys)] + if xs.size == 0 or ys.size == 0: + raise ValueError(f"Cannot project the area of use of CRS {crs!r}") + return (float(xs.min()), float(ys.min()), float(xs.max()), float(ys.max())) + + +def hilbert_distances_from_bounds(bounds, total_bounds) -> np.ndarray: + """Hilbert-curve distance of each feature's bbox midpoint. + + ``bounds`` is an (N, 4) array of [xmin, ymin, xmax, ymax]; the reference + frame ``total_bounds`` must be identical for every file that is merged. + """ + from geopandas.tools.hilbert_curve import _continuous_to_discrete_coords, _encode + + bounds = np.asarray(bounds, dtype="float64") + total_bounds = np.asarray(total_bounds, dtype="float64") + x, y = _continuous_to_discrete_coords(bounds, LEVEL, total_bounds) + return _encode(LEVEL, x, y) diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 86beb4cf..2867e400 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -168,7 +168,10 @@ def merge_files( ) # Same Hilbert reference grid that the upstream sort used. - from vecorel_cli.vecorel.hilbert import crs_total_bounds + try: + from vecorel_cli.vecorel.hilbert import crs_total_bounds + except ImportError: + from .hilbert import crs_total_bounds total_bounds = crs_total_bounds(crs) @@ -248,7 +251,10 @@ def _bounds_array_for_table(table: pa.Table, primary_col: str) -> np.ndarray: def _hilbert_keys_for_table(table: pa.Table, primary_col: str, total_bounds) -> np.ndarray: - from vecorel_cli.vecorel.hilbert import hilbert_distances_from_bounds + try: + from vecorel_cli.vecorel.hilbert import hilbert_distances_from_bounds + except ImportError: + from fiboa_cli.conversion.hilbert import hilbert_distances_from_bounds bounds = _bounds_array_for_table(table, primary_col) return hilbert_distances_from_bounds(bounds, total_bounds) From f25e683e4e805af5959c44afd0276d3dd12efe7b Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 25 Aug 2026 08:15:28 +0200 Subject: [PATCH 55/94] =?UTF-8?q?FR:=20backfill-ready=20variants=20?= =?UTF-8?q?=E2=80=94=202024=20first,=20explicit=20PARCELLES=20targets,=202?= =?UTF-8?q?018->2017=20(no=202018=20archive=20exists),=20per-variant=20det?= =?UTF-8?q?ermination=20dates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real archives also carry ILOTS_ANONYMES.gpkg, so **/*.gpkg would match two files; the test pins variant 2022 instead of relying on dict order. Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/fr.py | 24 +++++++++++++++--------- tests/test_convert.py | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/fiboa_cli/datasets/fr.py b/fiboa_cli/datasets/fr.py index 72034742..8f790a0b 100644 --- a/fiboa_cli/datasets/fr.py +++ b/fiboa_cli/datasets/fr.py @@ -14,11 +14,6 @@ class FRConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): # TODO, 2022 works, check (or discover) paths for other years variants = { - "2022": { - "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FXX_2022-01-01/RPG_2-0__GPKG_LAMB93_FXX_2022-01-01.7z.001": [ - "**/*.gpkg" - ] - }, "2024": { "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.001": [ "**/RPG_Parcelles.gpkg" # RPG 3.0 renamed PARCELLES_GRAPHIQUES.gpkg @@ -30,7 +25,12 @@ class FRConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): }, "2023": { "https://data.geopf.fr/telechargement/download/RPG/RPG_2-2__GPKG_LAMB93_FXX_2023-01-01/RPG_2-2__GPKG_LAMB93_FXX_2023-01-01.7z": [ - "**/*.gpkg" + "**/PARCELLES_GRAPHIQUES.gpkg" + ] + }, + "2022": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FXX_2022-01-01/RPG_2-0__GPKG_LAMB93_FXX_2022-01-01.7z.001": [ + "**/PARCELLES_GRAPHIQUES.gpkg" ] }, "2021": { @@ -43,10 +43,15 @@ class FRConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FR_2020-01-01/RPG_2-0__GPKG_LAMB93_FR_2020-01-01.7z.002": [], }, "2019": { - "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0_GPKG_LAMB93_FR-2019/RPG_2-0_GPKG_LAMB93_FR-2019.7z": [] + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0_GPKG_LAMB93_FR-2019/RPG_2-0_GPKG_LAMB93_FR-2019.7z": [ + "**/PARCELLES_GRAPHIQUES.gpkg" + ] }, - "2018": { - "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01.7z": [] + # the newest SHP edition on the download server is 2017; there is no 2018 archive + "2017": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01.7z": [ + "**/PARCELLES_GRAPHIQUES.shp" + ] }, } @@ -90,6 +95,7 @@ def download_files(self, uris, cache_folder=None): attribution = "IGN - Original data from https://geoservices.ign.fr/rpg" license = "Licence Ouverte / Open Licence " ec_mapping_csv = "fr_2018.csv" + use_variant_as_determination = True columns = { "geometry": "geometry", diff --git a/tests/test_convert.py b/tests/test_convert.py index 48914a14..24b0d21b 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -83,6 +83,7 @@ def _input_files(converter, *names): "input_files": {f"{test_path}/es_an/SP25_REC_PROV_04.zip": ["SP25_REC_04.shp"]}, }, "es_cat": _input_files("es_cat", "Cultius_DUN2023_GPKG.zip"), + "fr": {"variant": "2022"}, "es": {"input_files": {f"{test_path}/es/1501_ALAVA_cd_2025_20250105.gpkg.zip": ["*.gpkg"]}}, "lv": _input_files("lv", "1_100.xml"), "nz": _input_files("nz", "irrigated-land-area-raw-2020-update.zip"), From d0f950b1737849a18838a01165e4053954ac2545 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 25 Aug 2026 11:55:59 +0200 Subject: [PATCH 56/94] NZ: 2017 and 2020 editions as variants; resolve manual downloads from the cache folder The Koordinates portal needs a login, so the variant sources are the downloaded zips' filenames, resolved against the cache folder (vecorel treats bare local names as cwd-relative and would silently create an empty file there). Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/nz.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/nz.py b/fiboa_cli/datasets/nz.py index 524085a0..9f66a50f 100644 --- a/fiboa_cli/datasets/nz.py +++ b/fiboa_cli/datasets/nz.py @@ -1,3 +1,5 @@ +import os + import pandas as pd from vecorel_cli.vecorel.extensions import ADMIN_DIVISION @@ -6,7 +8,17 @@ class NZCropConverter(FiboaBaseConverter): - data_access = "Download manually from https://data.mfe.govt.nz/layer/105407-irrigated-land-area-raw-2020-update/" + data_access = """ + Download manually (Koordinates login required) and place the zip in the cache folder: + - 2020: https://data.mfe.govt.nz/layer/105407-irrigated-land-area-raw-2020-update/ (mfe-irrigated-land-area-raw-2020-update-SHP.zip) + - 2017: https://data.mfe.govt.nz/layer/90838-irrigated-land-area-2017/ (mfe-irrigated-land-area-2017-SHP.zip) + Alternatively pass the zip with the `-i` CLI parameter. + """ + variants = { + # keys are the cache filenames vecorel resolves before attempting a download + "2020": {"mfe-irrigated-land-area-raw-2020-update-SHP.zip": ["*.shp"]}, + "2017": {"mfe-irrigated-land-area-2017-SHP.zip": ["*.shp"]}, + } id = "nz" short_name = "New Zealand" @@ -20,6 +32,22 @@ class NZCropConverter(FiboaBaseConverter): created in 2017. The current update has incorporated data from the 2019 – 2020 irrigation season. """ + def download_files(self, uris, cache_folder=None): + """The sources are manual downloads (Koordinates login): resolve the bare + filenames from ``variants`` against the cache folder instead of the cwd.""" + _, cache_dir = self.get_cache(cache_folder) + resolved = {} + for uri, target in uris.items(): + if "://" not in uri and not os.path.isabs(uri) and not os.path.exists(uri): + cached = os.path.join(cache_dir, uri) + if not os.path.exists(cached): + raise FileNotFoundError( + f"{uri} is a manual download; place it in {cache_dir} (see data_access)" + ) + uri = cached + resolved[uri] = target + return super().download_files(resolved, cache_folder) + provider = "Aqualinc Research Limited " license = "CC-BY-4.0" extensions = {ADMIN_DIVISION} From 8f8d2b4d4481ed2854644cdf7e32abe59c231c2b Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 25 Aug 2026 13:44:00 +0200 Subject: [PATCH 57/94] US CSB: variants 2017-2024 from the single 2017-2024 archive; determination from the variant year CSB1724.gdb carries a CDL column per year, so every edition reads the same cached source; the old constant 2023-05-01 determination was wrong for anything but the 2023 edition. Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/us_usda_cropland.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/fiboa_cli/datasets/us_usda_cropland.py b/fiboa_cli/datasets/us_usda_cropland.py index 95d87254..58a1e51e 100644 --- a/fiboa_cli/datasets/us_usda_cropland.py +++ b/fiboa_cli/datasets/us_usda_cropland.py @@ -8,17 +8,15 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + # One archive carries the whole sequence: CSB1724.gdb has a CDL crop + # column for every year 2017-2024, so every variant reads the same source. variants = { - "2024": { + str(y): { "https://www.nass.usda.gov/Research_and_Science/Crop-Sequence-Boundaries/datasets/NationalCSB_2017-2024_rev23.zip": [ "NationalCSB_2017-2024_rev23/CSB1724.gdb" ] - }, - "2023": { - "https://www.nass.usda.gov/Research_and_Science/Crop-Sequence-Boundaries/datasets/NationalCSB_2016-2023_rev23.zip": [ - "NationalCSB_2016-2023_rev23/CSB1623.gdb" - ] - }, + } + for y in range(2024, 2016, -1) } id = "us_usda_cropland" short_name = "US (USDA CSB)" @@ -40,9 +38,7 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): "crop:name": "crop:name", "CNTY": "administrative_area_level_2", } - column_additions = { - "determination:datetime": "2023-05-01T00:00:00Z", - } + use_variant_as_determination = True missing_schemas = { "properties": { "administrative_area_level_2": {"type": "string"}, From 7d603dd7947e2061ae86e4da3332fbd8988b38f3 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 25 Aug 2026 21:45:01 +0200 Subject: [PATCH 58/94] BE-VLG: the 2026 archive is not named *_GPKG.zip; glob the gpkg inside Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/be_vlg.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/be_vlg.py b/fiboa_cli/datasets/be_vlg.py index c33e2282..69cb81aa 100644 --- a/fiboa_cli/datasets/be_vlg.py +++ b/fiboa_cli/datasets/be_vlg.py @@ -8,7 +8,9 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): variants = { - str(k): {PREFIX + v: [v.replace("_GPKG.zip", ".gpkg")]} + str(k): { + PREFIX + v: [v.replace("_GPKG.zip", ".gpkg") if v.endswith("_GPKG.zip") else "*.gpkg"] + } for k, v in ( (2026, "agpa_2026_2026-06-02_public.zip"), (2025, "Landbouwgebruikspercelen_2025_-_Voorlopig_(extractie_02-06-2025)_GPKG.zip"), From e112c77696affe892516ccd7569dc730f10cdc41 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 25 Aug 2026 21:55:29 +0200 Subject: [PATCH 59/94] BE-VLG: map the 2026 agpa edition's English column names onto the classic ones Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/be_vlg.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fiboa_cli/datasets/be_vlg.py b/fiboa_cli/datasets/be_vlg.py index 69cb81aa..17be52e0 100644 --- a/fiboa_cli/datasets/be_vlg.py +++ b/fiboa_cli/datasets/be_vlg.py @@ -37,6 +37,21 @@ class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): attribution = "Bron: Dept. LV" license = "Licentie modellicentie-gratis-hergebruik/v1.0 " + # the 2026 "agpa" edition renamed every column to English + RENAMES_2026 = { + "reference_id": "REF_ID", + "maincrop_code": "GWSCOD_H", + "maincrop_title": "GWSNAM_H", + "area_ha": "GRAF_OPP", + } + + def migrate(self, gdf): + if "maincrop_code" in gdf.columns: + gdf = gdf.rename(columns=self.RENAMES_2026) + if "BT_OMSCH" not in gdf.columns: # no farm-typology column any more + gdf["BT_OMSCH"] = None + return super().migrate(gdf) + columns = { "geometry": "geometry", "BT_OMSCH": "typology", From e117013f5d26fefc300e2add8406c4def31c1779 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 10:21:43 +0200 Subject: [PATCH 60/94] Tests for the per-file merge, Hilbert helpers, and command wrapper modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the new PerFileBaseConverter paths (multi-source merge, part cleanup, re-sort of non-Hilbert parts, error handling), the vendored hilbert module, and the one-line command modules — bringing coverage back over the 80% gate (72.2% -> 81.5%). Co-Authored-By: Claude Fable 5 --- tests/test_command_modules.py | 32 +++++++ tests/test_per_file.py | 169 ++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 tests/test_command_modules.py create mode 100644 tests/test_per_file.py diff --git a/tests/test_command_modules.py b/tests/test_command_modules.py new file mode 100644 index 00000000..0db55672 --- /dev/null +++ b/tests/test_command_modules.py @@ -0,0 +1,32 @@ +"""Smoke tests for the one-line command wrapper modules: they must import and +expose a command class the registry can register.""" + +import importlib + +import pytest + +from fiboa_cli import Registry # noqa: F401 + + +@pytest.mark.parametrize( + "module,cls", + [ + ("fiboa_cli.create_geojson", "CreateGeoJson"), + ("fiboa_cli.create_geoparquet", "CreateGeoParquet"), + ("fiboa_cli.create_jsonschema", "CreateJsonSchema"), + ("fiboa_cli.merge", "MergeDatasets"), + ("fiboa_cli.validate_schema", "ValidateSchema"), + ("fiboa_cli.rename_extension", "RenameExtension"), + ], +) +def test_command_module_exposes_class(module, cls): + mod = importlib.import_module(module) + assert hasattr(mod, cls), f"{module} does not define {cls}" + + +def test_registry_registers_fiboa_commands(): + from vecorel_cli.registry import Registry as R + + R.instance.register_commands() + names = {getattr(c, "__name__", str(c)) for c in R.instance.commands} + assert {"publish", "improve", "create-stac-collection", "merge"} <= names diff --git a/tests/test_per_file.py b/tests/test_per_file.py new file mode 100644 index 00000000..3bf9d8a5 --- /dev/null +++ b/tests/test_per_file.py @@ -0,0 +1,169 @@ +"""Tests for the per-file streaming merge (PerFileBaseConverter) and its +Hilbert-order helpers.""" + +import shutil +from csv import DictReader +from unittest.mock import patch + +import numpy as np +import pyarrow.parquet as pq +import pytest + +from fiboa_cli import Registry # noqa: F401 +from fiboa_cli.conversion.hilbert import crs_total_bounds, hilbert_distances_from_bounds +from fiboa_cli.convert import ConvertData +from fiboa_cli.datasets.es import Converter as ESConverter + +test_path = "tests/data-files/convert/es" +ZIP = f"{test_path}/1501_ALAVA_cd_2025_20250105.gpkg.zip" + + +def load_ec(csv_file=None, url=None): + return list(DictReader(open(f"{test_path}/es.csv", encoding="utf-8"))) + + +def convert_es(target, input_files, cache=test_path): + with ( + patch("fiboa_cli.datasets.commons.ec.load_ec_mapping", side_effect=load_ec), + patch("fiboa_cli.datasets.commons.hcat.load_ec_mapping", side_effect=load_ec), + ): + ConvertData("es").convert(target=target, cache=cache, input_files=input_files) + + +# ---------- hilbert helpers ---------- + + +def test_crs_total_bounds_geographic(): + assert crs_total_bounds("EPSG:4326") == (-180.0, -90.0, 180.0, 90.0) + + +def test_crs_total_bounds_projected_metric(): + xmin, ymin, xmax, ymax = crs_total_bounds("EPSG:2056") # Swiss LV95 + assert xmax > xmin and ymax > ymin + # the Swiss area of use projects to coordinates around (2.6e6, 1.2e6) + assert 2.0e6 < (xmin + xmax) / 2 < 3.2e6 + + +def test_crs_total_bounds_is_deterministic(): + assert crs_total_bounds("EPSG:3067") == crs_total_bounds("EPSG:3067") + + +def test_hilbert_distances_locality(): + total = (0.0, 0.0, 100.0, 100.0) + # three features: two neighbours and one far away + bounds = np.array( + [ + [1, 1, 2, 2], + [2, 1, 3, 2], + [90, 90, 95, 95], + ], + dtype="float64", + ) + d = hilbert_distances_from_bounds(bounds, total) + assert len(d) == 3 + assert len(set(d.tolist())) == 3 + # neighbours are closer along the curve than the far-away feature + assert abs(int(d[0]) - int(d[1])) < abs(int(d[0]) - int(d[2])) + + +# ---------- PerFileBaseConverter ---------- + + +def test_single_source_degenerates_to_plain_convert(tmp_path, capsys): + out = tmp_path / "es.parquet" + convert_es(out, {ZIP: ["*.gpkg"]}) + assert out.exists() + assert not list(tmp_path.glob("*_part.parquet")) + + +def test_multi_source_merges_and_cleans_parts(tmp_path, capsys): + # two sources: the fixture zip under two names + zip2 = tmp_path / "1502_COPY_cd_2025_20250105.gpkg.zip" + shutil.copy(ZIP, zip2) + out = tmp_path / "es.parquet" + convert_es(out, {ZIP: ["*.gpkg"], str(zip2): ["*.gpkg"]}, cache=str(tmp_path / "cache")) + assert out.exists() + n = pq.ParquetFile(out).metadata.num_rows + assert n == 20 # 10 rows per copy of the fixture + # parts are removed after a successful merge + assert not list(out.parent.glob("*_part.parquet")) + # merged output keeps the geo metadata + import json + + geo = json.loads(pq.ParquetFile(out).schema_arrow.metadata[b"geo"]) + assert geo["primary_column"] == "geometry" + + +def _make_part(tmp_path, name="part_a.parquet"): + out = tmp_path / name + convert_es(out, {ZIP: ["*.gpkg"]}) + return out + + +def test_merge_files_rejects_empty_and_bad_version(tmp_path): + conv = ESConverter() + with pytest.raises(ValueError, match="No paths"): + conv.merge_files(str(tmp_path / "o.parquet"), []) + part = _make_part(tmp_path) + with pytest.raises(ValueError, match="geoparquet_version"): + conv.merge_files(str(tmp_path / "o.parquet"), [str(part)], geoparquet_version="9.9.9") + + +def test_merge_files_rejects_non_geoparquet(tmp_path): + import pyarrow as pa + + plain = tmp_path / "plain.parquet" + pq.write_table(pa.table({"a": [1, 2]}), plain) + conv = ESConverter() + with pytest.raises(ValueError, match="no 'geo' metadata"): + conv.merge_files(str(tmp_path / "o.parquet"), [str(plain)]) + + +def test_merge_files_rejects_schema_mismatch(tmp_path): + part = _make_part(tmp_path) + # a second file with an extra column + tbl = pq.read_table(part) + import pyarrow as pa + + other = tmp_path / "other.parquet" + tbl2 = tbl.append_column("extra", pa.array([1] * tbl.num_rows)) + schema = tbl2.schema.with_metadata(tbl.schema.metadata) + pq.write_table(tbl2.cast(schema), other) + conv = ESConverter() + with pytest.raises(ValueError, match="Schema mismatch"): + conv.merge_files(str(tmp_path / "o.parquet"), [str(part), str(other)]) + + +def test_merge_resorts_unsorted_part(tmp_path, capsys): + part = _make_part(tmp_path) + # destroy the Hilbert order of a copy: reverse the row order + shuffled = tmp_path / "part_b.parquet" + tbl = pq.read_table(part) + rev = tbl.take(list(reversed(range(tbl.num_rows)))) + pq.write_table(rev.cast(tbl.schema), shuffled) + conv = ESConverter() + merged = tmp_path / "merged.parquet" + conv.merge_files(str(merged), [str(part), str(shuffled)], cleanup_parts=True) + assert pq.ParquetFile(merged).metadata.num_rows == 2 * tbl.num_rows + assert not part.exists() and not shuffled.exists() + # the merged file is globally Hilbert-sorted + from fiboa_cli.conversion.per_file import _bounds_array_for_table + + out_tbl = pq.read_table(merged) + keys = hilbert_distances_from_bounds( + _bounds_array_for_table(out_tbl, "geometry"), crs_total_bounds("EPSG:4258") + ) + assert (np.diff(keys.astype("int64")) >= 0).all() + + +def test_bounds_array_wkb_fallback(tmp_path): + # without a bbox covering column the bounds are decoded from WKB + part = _make_part(tmp_path) + tbl = pq.read_table(part) + if "bbox" in tbl.column_names: + tbl = tbl.drop_columns(["bbox"]) + from fiboa_cli.conversion.per_file import _bounds_array_for_table + + bounds = _bounds_array_for_table(tbl, "geometry") + assert bounds.shape == (tbl.num_rows, 4) + assert (bounds[:, 2] >= bounds[:, 0]).all() and (bounds[:, 3] >= bounds[:, 1]).all() From 10e174de737069fd78f40f3e0ef9d5fde9f90834 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 10:42:17 +0200 Subject: [PATCH 61/94] Windows/CI robustness: close parquet readers before delete/replace; local mapping fixtures - per_file.py and duckdb.py kept pyarrow readers open across os.remove / os.replace / in-place rewrites; harmless on POSIX, fails on Windows (part cleanup silently no-opped, duckdb 1.1 post-processing silently downgraded). All readers now close deterministically. - commit the 17 crop-mapping CSVs the converter tests previously fetched from the network at test time (a fiboa.org 503 failed the ch test on a Windows runner); tests are now hermetic. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/duckdb.py | 1 + fiboa_cli/conversion/per_file.py | 28 +- tests/data-files/convert/at/at.csv | 252 ++++++++++++ tests/data-files/convert/ch/ch.csv | 158 ++++++++ tests/data-files/convert/cz/cz_2023.csv | 327 ++++++++++++++++ tests/data-files/convert/de_bb/de.csv | 422 +++++++++++++++++++++ tests/data-files/convert/ec_lv/lv_2021.csv | 139 +++++++ tests/data-files/convert/ec_si/si_2021.csv | 158 ++++++++ tests/data-files/convert/fi/fi_2023.csv | 243 ++++++++++++ tests/data-files/convert/hr/hr_2020.csv | 16 + tests/data-files/convert/ie/ie.csv | 186 +++++++++ tests/data-files/convert/it_1/iti1.csv | 370 ++++++++++++++++++ tests/data-files/convert/lt/lt_2021.csv | 25 ++ tests/data-files/convert/nl/nl.csv | 415 ++++++++++++++++++++ tests/data-files/convert/pt/pt.csv | 179 +++++++++ tests/data-files/convert/se/se.csv | 100 +++++ tests/data-files/convert/si/si.csv | 180 +++++++++ tests/data-files/convert/sk/sk.csv | 245 ++++++++++++ tests/data-files/convert/us_ca_scm/scm.csv | 60 +++ 19 files changed, 3494 insertions(+), 10 deletions(-) create mode 100644 tests/data-files/convert/at/at.csv create mode 100644 tests/data-files/convert/ch/ch.csv create mode 100644 tests/data-files/convert/cz/cz_2023.csv create mode 100644 tests/data-files/convert/de_bb/de.csv create mode 100644 tests/data-files/convert/ec_lv/lv_2021.csv create mode 100644 tests/data-files/convert/ec_si/si_2021.csv create mode 100644 tests/data-files/convert/fi/fi_2023.csv create mode 100644 tests/data-files/convert/hr/hr_2020.csv create mode 100644 tests/data-files/convert/ie/ie.csv create mode 100644 tests/data-files/convert/it_1/iti1.csv create mode 100644 tests/data-files/convert/lt/lt_2021.csv create mode 100644 tests/data-files/convert/nl/nl.csv create mode 100644 tests/data-files/convert/pt/pt.csv create mode 100644 tests/data-files/convert/se/se.csv create mode 100644 tests/data-files/convert/si/si.csv create mode 100644 tests/data-files/convert/sk/sk.csv create mode 100644 tests/data-files/convert/us_ca_scm/scm.csv diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 9b4f9fc1..55347c62 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -213,6 +213,7 @@ def convert( writer.write_table(tbl) finally: writer.close() + pq_file.close() # Windows cannot replace a file that is still open os.replace(tmp_path, output_file) except Exception as e: diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 2867e400..e504269a 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -122,8 +122,8 @@ def merge_files( raise ValueError("No paths to merge") paths = [str(p) for p in paths] - base_pf = pq.ParquetFile(paths[0]) - base_schema = base_pf.schema_arrow + with pq.ParquetFile(paths[0]) as base_pf: + base_schema = base_pf.schema_arrow base_meta = base_schema.metadata or {} if GEO_META_KEY not in base_meta: raise ValueError(f"{paths[0]} has no 'geo' metadata; not a GeoParquet?") @@ -140,8 +140,8 @@ def merge_files( bboxes.append(primary_col_meta["bbox"]) geom_types.update(primary_col_meta.get("geometry_types") or []) for path in paths[1:]: - pf = pq.ParquetFile(path) - sch = pf.schema_arrow + with pq.ParquetFile(path) as pf: + sch = pf.schema_arrow if not sch.equals(base_schema, check_metadata=False): raise ValueError( f"Schema mismatch: {path} differs from {paths[0]}.\n" @@ -192,7 +192,7 @@ def merge_files( self.warning(f"Re-sorted {n_resorted}/{len(paths)} part file(s) before merging.") self.info(f"Streaming merge -> {output_file} (Hilbert ref bounds = {total_bounds})") - expected_rows = sum(pq.ParquetFile(p).metadata.num_rows for p in paths) + expected_rows = sum(_num_rows(p) for p in paths) _streaming_merge( paths, output_file, @@ -205,7 +205,7 @@ def merge_files( compression_level, geoparquet_version, ) - actual_rows = pq.ParquetFile(output_file).metadata.num_rows + actual_rows = _num_rows(output_file) if actual_rows != expected_rows: raise RuntimeError( f"Streaming merge dropped rows: expected {expected_rows:,} " @@ -227,6 +227,11 @@ def merge_files( # ---------- helpers ---------- +def _num_rows(path) -> int: + with pq.ParquetFile(path) as pf: + return pf.metadata.num_rows + + def _bounds_array_for_table(table: pa.Table, primary_col: str) -> np.ndarray: """Return an (N, 4) float64 array of [xmin, ymin, xmax, ymax] per feature. @@ -274,8 +279,9 @@ def _ensure_hilbert_sorted( is bounded by a single source partition (much smaller than the merged dataset). Schema metadata (``geo``, collection JSON, etc.) is preserved. """ - pf = pq.ParquetFile(path) - table = pf.read() + with pq.ParquetFile(path) as pf: + table = pf.read() + metadata = pf.schema_arrow.metadata hilberts = _hilbert_keys_for_table(table, primary_col, total_bounds) # NB: hilberts is uint64; never use np.diff for monotonicity here — uint # underflow makes any descent wrap to a huge positive and fool the check. @@ -283,7 +289,7 @@ def _ensure_hilbert_sorted( return False order = np.argsort(hilberts, kind="stable") sorted_table = table.take(pa.array(order)) - sorted_table = sorted_table.replace_schema_metadata(pf.schema_arrow.metadata) + sorted_table = sorted_table.replace_schema_metadata(metadata) write_kwargs = {"compression": compression} if compression_level is not None: write_kwargs["compression_level"] = compression_level @@ -326,7 +332,7 @@ def _streaming_merge( geoparquet_version: Optional[str] = None, ) -> None: pq_files = [pq.ParquetFile(p) for p in paths] - in_schema = pq_files[0].schema_arrow + in_schema = pq_files[0].schema_arrow # readers closed in the finally below out_schema = _build_output_schema(in_schema, merged_bbox, geom_types, geoparquet_version) iters = [pf.iter_batches(batch_size=batch_size) for pf in pq_files] @@ -391,3 +397,5 @@ def refill(i): writer.write_table(combined.take(pa.array(order))) finally: writer.close() + for pf in pq_files: + pf.close() diff --git a/tests/data-files/convert/at/at.csv b/tests/data-files/convert/at/at.csv new file mode 100644 index 00000000..688bd7e8 --- /dev/null +++ b/tests/data-files/convert/at/at.csv @@ -0,0 +1,252 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +920,NATURSCHUTZFACHLICH WERTVOLLE PFLEGEFLÄCHE, VALUABLE ECOLOGIC PROTECTION AREA,not_known_and_other,3399000000,not_known_and_other,3399000000 +351,LSE HECKE / UFERGEHÖLZ,(Protected Landscape Element) HEDGE / SHORE WOOD,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +959,20 JÄHRIGE STILLLEGUNG,20 YEAR SHUTDOWN,unmaintained,3308000000,unmaintained,3308000000 +635,LUZERNE,ALFALFA,alfalfa_lucerne,3301090301,alfalfa_lucerne,3301090301 +990,ALMFUTTERFLÄCHE,ALM FORAGE AREA,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +636,"WECHSELWIESE (EGART, ACKERWEIDE)",ALTERNATE MEADOW (EGART,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +758,AMARANTH,AMARANTH,amaranth,3301150100,amaranth,3301150100 +813,MARILLEN,Apricots,apricots,3303010300,apricots,3303010300 +773,BIENENTRACHTBRACHE,BEE FLOWER FALLOW,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +691,RÜBENVERMEHRUNG,BEET PROPAGATION,beetroot_beets,3301290200,beetroot_beets,3301290200 +207,BITTERLUPINEN,BITTER LUPINES,sweet_lupins,3301020700,sweet_lupins,3301020700 +119,BUCHWEIZEN,BUCKWHEAT,buckwheat,3301150200,buckwheat,3301150200 +689,LEINDOTTER,CAMELINA (Camelina sativa),camelina,3301061500,camelina,3301061500 +120,KANARIENSAAT,CANARY SEED,canary_seed_canaryseed,3301011400,canary_seed_canaryseed,3301011400 +812,KIRSCHEN,CHERRIES,cherry_cherries,3303010400,cherry_cherries,3303010400 +623,KICHERERBSEN,CHICKPEAS,chickpeas,3301020200,chickpeas,3301020200 +633,KLEE,CLOVER,clover,3301090303,clover,3301090303 +664,KLEE / FELDGEMÜSE,CLOVER / FIELD VEGETABLES,clover,3301090303,clover,3301090303 +665,KLEEGRAS / FELDGEMÜSE,CLOVER GRASS / FIELD VEGETABLES,clover,3301090303,clover,3301090303 +634,KLEEGRAS,CLOVER-GRASS,clover,3301090303,clover,3301090303 +172,MAIS / KÄFERBOHNEN IN GETRENNTEN REIHEN,CORN / BEETLE BEANS IN SEPARATED ROWS,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +131,MAIS CORN-COB-MIX (CCM) / FELDGEMÜSE,CORN CORN COB MIX (CCM) / FIELD VEGETABLES,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +106,MAIS CORN-COB-MIX (CCM),CORN CORN-COB-MIX (CCM),grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +774,DURCHWACHSENE SILPHIE,CUP PLANT (Silphium perfoliatum),silphium_rosinweeds,3301084400,silphium_rosinweeds,3301084400 +906,SCHNITTWEINGARTEN,CUT WINE GARDEN,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +352,GLÖZ GRABEN / UFERRANDSTREIFEN,DITCH / BORDER STRIP OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +513,FRÜHKARTOFFELN,EARLY POTATOES,potatoes,3301030000,potatoes,3301030000 +529,FRÜHKARTOFFELN / BUCHWEIZEN,EARLY POTATOES / BUCKWHEAT,potatoes,3301030000,potatoes,3301030000 +159,FRÜHKARTOFFELN / MAIS,EARLY POTATOES / CORN,potatoes,3301030000,potatoes,3301030000 +520,FRÜHKARTOFFELN / FELDGEMÜSE,EARLY POTATOES / FIELD VEGETABLES,potatoes,3301030000,potatoes,3301030000 +524,SPEISEKARTOFFELN,EDIBLE POTATOES,potatoes,3301030000,potatoes,3301030000 +526,SPEISEKARTOFFELN / FELDGEMÜSE,EDIBLE POTATOES / FIELD VEGETABLES,potatoes,3301030000,potatoes,3301030000 +832,HOLUNDER,ELDER,elder_elderberry,3303080400,elder_elderberry,3303080400 +686,"ELEFANTENGRAS (CHINASCHILF, MISCANTHUS SINENSIS)",ELEPHANT GRASS (CHINA REED,poaceae_grasses,3301090200,poaceae_grasses,3301090200 +149,EMMER ODER EINKORN (SOMMERUNG),EMMER OR EINKORN (SUMMER),spring_emmer,3301011202,summer_emmer,3301011203 +151,EMMER ODER EINKORN (SOMMERUNG) / FELDGEMÜSE,EMMER OR EINKORN (SUMMER) / FIELD VEGETABLES,spring_emmer,3301011202,summer_emmer,3301011203 +150,EMMER ODER EINKORN (WINTERUNG),EMMER OR EINKORN (WINTERING),winter_emmer,3301011201,winter_emmer,3301011201 +152,EMMER ODER EINKORN (WINTERUNG) / FELDGEMÜSE,EMMER OR EINKORN (WINTERING) / FIELD VEGETABLES,winter_emmer,3301011201,winter_emmer,3301011201 +641,ENERGIEGRAS,ENERGY GRASS,temporary_grass,3301090100,temporary_grass,3301090100 +864,ENERGIEHOLZ ROBINIE,ENERGY WOOD ROBINIA,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +863,ENERGIEHOLZ OHNE ROBINIE,ENERGY WOOD WITHOUT ROBINIA,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +209,ESPARSETTE,ESPARSETTE,esparsette_onobrychis,3301020300,esparsette_onobrychis,3301020300 +202,ACKERBOHNEN (PUFFBOHNEN),FARM BEANS (PUFF BEANS),beans,3301020100,beans,3301020100 +528,FUTTERKARTOFFELN,FEED POTATOES,potatoes,3301030000,potatoes,3301030000 +206,ACKERBOHNEN - GETREIDE GEMENGE,FIELD BEANS - GRAIN MIX,beans,3301020100,beans,3301020100 +205,ACKERBOHNEN (PUFFBOHNEN) / FELDGEMÜSE,FIELD BEANS (PUFF BEANS) / FIELD VEGETABLES,beans,3301020100,beans,3301020100 +208,ACKERBOHNEN / ERBSENGEMENGE,FIELD BEANS / PEAS MIX,beans,3301020100,beans,3301020100 +210,PELUSCHKEN,FIELD PEA (Pisum sativum),peas,3301020600,peas,3301020600 +676,FELDGEMÜSE EINLEGEGURKEN,FIELD VEGETABLES CUCUMBERS,cucumber_pickle,3301140100,cucumber_pickle,3301140100 +699,FELDGEMÜSE FRISCHMARKT UND VERARBEITUNG MEHRKULTURIG,FIELD VEGETABLES FRESH MARKET AND MULTI-CULTIVATED PROCESSING,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +694,FELDGEMÜSE MEHRKULTURIG,FIELD VEGETABLES MULTI-CROPS,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +698,FELDGEMÜSE VERARBEITUNG MEHRKULTURIG,FIELD VEGETABLES PROCESSING MULTI-CULTURED,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +697,FELDGEMÜSE VERARBEITUNG EINKULTURIG,FIELD VEGETABLES PROCESSING SINGLE CULTURE,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +696,FELDGEMÜSE EINKULTURIG,FIELD VEGETABLES SINGLE CULTURE,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +653,FELDGEMÜSE OHNE ERNTE,FIELD VEGETABLES WITHOUT HARVEST,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +964,ERSTAUFFORSTUNG ALT,FIRST REFORESTATION OLD,afforestation_reforestation,3306010000,afforestation_reforestation,3306010000 +626,PLATTERBSEN,FLAT PEAS,peas,3301020600,peas,3301020600 +657,FLACHS (FASERLEIN) ZUR FASERERZEUGUNG,FLAX (FIBER) FOR FIBER PRODUCTION,flax_linen,3301060701,flax_linen,3301060701 +310,ÖLLEIN (NICHT ZUR FASERGEWINNUNG),FLAX (NOT FOR FIBER RECOVERY),flax_linseed_oil,3301060702,flax_linseed,3301060700 +654,BLUMEN UND ZIERPFLANZEN,FLOWERS AND ORNAMENTAL PLANTS,flowers_ornamental_plants,3301080000,flowers_ornamental_plants,3301080000 +838,BLUMEN UND ZIERPFLANZEN IM FOLIENTUNNEL,FLOWERS AND ORNAMENTAL PLANTS IN THE FOIL TUNNEL,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +837,BLUMEN UND ZIERPFLANZEN IM GEWÄCHSHAUS,FLOWERS AND ORNAMENTAL PLANTS IN THE GREENHOUSE,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +525,SPEISEINDUSTRIEKARTOFFELN,FOOD INDUSTRY POTATOES,potatoes,3301030000,potatoes,3301030000 +631,"FUTTERRÜBEN (RUNKELRÜBEN, BURGUND KOHLRÜBEN)",FORAGE BEET (RUNKEL BEETS,mangelwurzel_fodder_beet,3301290400,mangelwurzel_fodder_beet,3301290400 +637,FUTTERGRÄSER,FORAGE GRASS,temporary_grass,3301090100,pasture_meadow_grassland_grass,3302000000 +663,FUTTERGRÄSER / FELDGEMÜSE,FORAGE GRASS / FIELD VEGETABLES,temporary_grass,3301090100,temporary_grass,3301090100 +965,FORST GENETISCHE RESSOURCEN,FORESTRY GENETIC RESOURCES,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +806,OBST/HOPFEN BODENGESUNDUNG,FRUIT / HOP SOIL RECOVERY,orchards_fruits,3303010000,orchards_fruits,3303010000 +852,OBST IM FOLIENTUNNEL,FRUIT IN THE FILM TUNNEL,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +851,OBST IM GEWÄCHSHAUS,FRUITS IN THE GREENHOUSE,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +772,GINKGO,GINKGO,ginko,3303090000,ginko,3303090000 +960,WALDUMWELTMASSNAHMEN,Good agricultural and ecological condition NATURAL MONUMENT AREA,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +354,GLÖZ STEINRIEGEL / STEINHAGE,GOOD AGRICULTURAL AND ECOLOGICAL CONDITION STEINRIEGEL / STEINHAGE,not_known_and_other,3399000000,not_known_and_other,3399000000 +105,KÖRNERMAIS,GRAIN CORN,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +201,KÖRNERERBSEN,GRAIN PEAS,peas,3301020600,peas,3301020600 +204,KÖRNERERBSEN / FELDGEMÜSE,GRAIN PEAS / FIELD VEGETABLES,peas,3301020600,peas,3301020600 +721,GRÜNLANDBRACHE,GRASSLAND FALLOW,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +135,GRÜNMAIS,GREEN CORN,green_silo_maize,3301090400,green_silo_maize,3301090400 +681,GRÜNSCHNITTROGGEN,GREEN CUT RYE,rye,3301010300,rye,3301010300 +764,GRÜNSCHNITTROGGEN / MAIS,GREEN CUT RYE / CORN,rye,3301010300,rye,3301010300 +765,GRÜNSCHNITTROGGEN / HIRSE,GREEN CUT RYE / MILLET,rye,3301010300,rye,3301010300 +763,GRÜNSCHNITTROGGEN / SUDANGRAS,GREEN CUT RYE / SUDAN GRASS,rye,3301010300,rye,3301010300 +766,GRÜNSCHNITTROGGEN / SONNENBLUME,GREEN CUT RYE / SUNFLOWER,rye,3301010300,rye,3301010300 +771,GRÜNBRACHE,GREEN FALLOW,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +165,WINTERHARTWEIZEN (DURUM),HARD WINTER WHEAT (DURUM),winter_durum_hard_wheat,3301010201,winter_durum_hard_wheat,3301010201 +658,HANF,HEMP,hemp_cannabis,3301061000,hemp_cannabis,3301061000 +821,HOPFEN,HOP,hops,3301060200,hops,3301060200 +622,LINSEN,LENTILS,lentils,3301020500,lentils,3301020500 +704,STREUWIESE,LITTER MEADOW,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +680,MARIENDISTELN,MARIAN THISTLES (Silybum marianum),marian_thistles,3301061300,marian_thistles,3301061300 +707,HUTWEIDE,MEAGER PASTURE,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +538,HEILPFLANZEN,MEDICINAL PLANTS,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +848,HEILPFLANZEN IM FOLIENTUNNEL,MEDICINAL PLANTS IN THE FILM TUNNEL,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +847,HEILPFLANZEN IM GEWÄCHSHAUS,MEDICINAL PLANTS IN THE GREENHOUSE,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +118,HIRSE,MILLET,millet_sorghum,3301010900,millet_sorghum,3301010900 +132,HIRSE / FELDGEMÜSE,MILLET / FIELD VEGETABLES,millet_sorghum,3301010900,millet_sorghum,3301010900 +708,BERGMÄHDER,MOUNTAIN MOWING MEADOW,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +717,MÄHWIESE/-WEIDE DREI UND MEHR NUTZUNGEN,MOWING MEADOW / PASTURE (THREE AND MORE USES),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +716,MÄHWIESE/-WEIDE ZWEI NUTZUNGEN,MOWING MEADOW / PASTURE (TWO USES),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +861,MEHRJÄHRIGE BAUMSCHULEN,MULTI-YEAR NURSERY,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +506,SENF,MUSTARD,mustard,3301210100,mustard,3301210100 +358,GLÖZ NATURDENKMAL FLÄCHE,Natural Site Area,not_known_and_other,3399000000,not_known_and_other,3399000000 +829,NEKTARINEN,NECTARINES,nectarine,3303010900,nectarine,3303010900 +844,EDELKASTANIEN,NOBLE CHESTNUTS,sweet_chestnuts,3303030500,sweet_chestnuts,3303030500 +842,"SCHALENFRÜCHTE (WALNÜSSE, HASELNÜSSE, ...)",NUTS (WALNUTS HAZELNUTS ...),nuts,3303030000,nuts,3303030000 +311,ÖLLEIN (NICHT ZUR FASERGEWINNUNG) / FELDGEMÜSE,OIL FLAX (NOT FOR FIBER PRODUCTION) / FIELD VEGETABLES,flax_linseed_oil,3301060702,flax_linseed_oil,3301060702 +751,ÖLKÜRBIS,OIL PUMPKIN,pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +690,ÖLRETTICH,OIL RADISH,radish,3301290600,radish,3301290600 +860,EINJÄHRIGE BAUMSCHULEN,ONE YEAR NURSERY ,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +701,EINMÄHDIGE WIESE,ONE-MOWN MEADOW (a meadow that gets mown only once a year),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +843,SONSTIGE FLÄCHEN: GESCHÜTZTER ANBAU,OTHER AREAS: PROTECTED CULTIVATION,not_known_and_other,3399000000,not_known_and_other,3399000000 +846,SONSTIGE KULTUREN IM FOLIENTUNNEL,OTHER CULTURES IN THE FILM TUNNEL,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +845,SONSTIGE KULTUREN IM GEWÄCHSHAUS,OTHER CULTURES IN THE GREENHOUSE,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +661,SONSTIGE ACKERFLÄCHEN,OTHER FARM AREA,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +671,SONSTIGE ACKERKULTUREN,OTHER FARM CROPS,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +679,SONSTIGES FELDFUTTER,OTHER FIELD FORAGE,not_known_and_other,3399000000,not_known_and_other,3399000000 +809,ANDERES OBST,OTHER FRUITS,orchards_fruits,3303010000,orchards_fruits,3303010000 +710,SONSTIGE GRÜNLANDFLÄCHEN,OTHER GRASSLAND AREAS,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +722,SONSTIGE HUTWEIDEFLÄCHEN,OTHER MEAGER MEADOW AREAS,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +752,"SONSTIGE ÖLFRÜCHTE (SAFLOR, ...)",OTHER OIL FRUITS (SAFLORO ...),oilseed_crops,3301060800,oilseed_crops,3301060800 +865,ANDERE DAUERKULTUREN,OTHER PERMANENT CULTURES,other_permanent_crops_plantations,3303990000,other_permanent_crops_plantations,3303990000 +810,SONSTIGE SPEZIALKULTURFLÄCHEN,OTHER SPECIAL CULTURAL AREAS,not_known_and_other,3399000000,not_known_and_other,3399000000 +907,SONSTIGE WEINFLÄCHEN,OTHER WINE AREAS,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +814,PFIRSICHE,PEACHES,peach,3303011100,peach,3303011100 +115,ERBSEN - GETREIDE GEMENGE,PEAS - GRAIN MIX,peas,3301020600,peas,3301020600 +164,ERBSEN - GETREIDE GEMENGE / BUCHWEIZEN,PEAS - GRAIN MIX / BUCKWHEAT,peas,3301020600,peas,3301020600 +129,ERBSEN - GETREIDE GEMENGE / FELDGEMÜSE,PEAS - GRAIN MIX / FIELD VEGETABLES,peas,3301020600,peas,3301020600 +715,DAUERWEIDE,PERMANENT PASTURE,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +682,PHACELIA,PHACELIA,phacelia,3301061400,phacelia,3301061400 +830,PFLAUMEN,PLUMS,plums,3303011300,plums,3303011300 +820,ZWETSCHKEN,PLUMS,plums,3303011300,plums,3303011300 +355,GLÖZ TEICH / TÜMPEL,POND / POOL OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +961,ERSTAUFFORSTUNG,PRIMARY REFORESTATION,afforestation_reforestation,3306010000,afforestation_reforestation,3306010000 +353,LSE RAIN / BÖSCHUNG / TROCKENSTEINMAUER,PROTECTED LANDSCAPE ELEMENET RAIN / EMBANKMENT / DRY STONE WALL,not_known_and_other,3399000000,not_known_and_other,3399000000 +350,LSE FELDGEHÖLZ / BAUM- / GEBÜSCHGRUPPE,PROTECTED LANDSCAPE ELEMENT FIELD WOOD / TREE / BRUSH GROUP,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +768,SPEISEKÜRBIS,PUMPKIN,pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +828,QUITTEN,QUINCES,quinces,3303011500,quinces,3303011500 +759,QUINOA,QUINOA,quinoa,3301150300,quinoa,3301150300 +775,SALBEI (CHIA),SAGE (CHIA),sage_chia,3301190000,sage_chia,3301190000 +173,SAATMAISVERMEHRUNG,SEED CORN PROPAGATION,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +527,SAATKARTOFFELN,SEED POTATOES,potatoes,3301030000,potatoes,3301030000 +831,STRAUCHBEEREN,SHRUBBERRIES,shrubberries_shrubs,3303080000,shrubberries_shrubs,3303080000 +109,SILOMAIS,SILO CORN,green_silo_maize,3301090400,green_silo_maize,3301090400 +117,SORGHUM,SORGHUM,millet_sorghum,3301010900,millet_sorghum,3301010900 +819,WEICHSELN,SOUR CHERRIES,cherry_cherries,3303010400,cherry_cherries,3303010400 +308,SOJABOHNEN,SOYBEANS,soy_soybeans,3301160000,soy_soybeans,3301160000 +309,SOJABOHNEN / SOMMERWICKEN IN GETRENNTEN REIHEN,SOYBEANS / SUMMER VETCHETS IN SEPARATE ROWS,soy_soybeans,3301160000,soy_soybeans,3301160000 +540,GEWÜRZFENCHEL,SPICE FENNEL,fennel,3301170000,fennel,3301170000 +539,GEWÜRZPFLANZEN,SPICE PLANTS,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +850,GEWÜRZPFLANZEN IM FOLIENTUNNEL,SPICE PLANTS IN THE FOIL TUNNEL,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +849,GEWÜRZPFLANZEN IM GEWÄCHSHAUS,SPICE PLANTS IN THE GREENHOUSE,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +537,JOHANNISKRAUT,ST. JOHNs WORT,st_johns_wort,3301061234,st_johns_wort,3301061234 +519,STÄRKEINDUSTRIEKARTOFFELN,STARCH POTATOES,potatoes,3301030000,potatoes,3301030000 +755,ERDBEEREN,STRAWBERRIES,strawberries,3301130000,strawberries,3301130000 +756,ERDBEEREN / FELDGEMÜSE,STRAWBERRIES / FIELD VEGETABLES,strawberries,3301130000,strawberries,3301130000 +154,SUDANGRAS,SUDAN GRASS (Sorghum sudanense),millet_sorghum,3301010900,poaceae_grasses,3301090200 +651,ZUCKERRÜBEN,SUGAR BEET,sugar_beet,3301290700,sugar_beet,3301290700 +107,ZUCKERMAIS,SUGAR CORN,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +134,ZUCKERMAIS / FELDGEMÜSE,SUGAR CORN / FIELD VEGETABLES,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +111,SOMMERGERSTE,SUMMER BARLEY,spring_barley,3301010402,summer_barley,3301010403 +161,SOMMERGERSTE / BUCHWEIZEN,SUMMER BARLEY / BUCKWHEAT,spring_barley,3301010402,summer_barley,3301010403 +126,SOMMERGERSTE / FELDGEMÜSE,SUMMER BARLEY / FIELD VEGETABLES,spring_barley,3301010402,summer_barley,3301010403 +535,SOMMERKÜMMEL,SUMMER CARAWAY,caraway,3301061211,caraway,3301061211 +128,SOMMERMENGGETREIDE / FELDGEMÜSE,SUMMER CEREALS / FIELD VEGETABLES,spring_meslin,3301011102,summer_meslin,3301011103 +137,SOMMERWEICHWEIZEN,SUMMER COMMON WHEAT,spring_common_soft_wheat,3301010102,summer_common_soft_wheat,3301010103 +166,SOMMERHARTWEIZEN (DURUM),SUMMER HARD WHEAT (DURUM),spring_durum_hard_wheat,3301010202,summer_durum_hard_wheat,3301010203 +170,SOMMERHARTWEIZEN (DURUM) / BUCHWEIZEN,SUMMER HARD WHEAT (DURUM) / BUCKWHEAT,spring_durum_hard_wheat,3301010202,summer_durum_hard_wheat,3301010203 +168,SOMMERHARTWEIZEN (DURUM) / FELDGEMÜSE,SUMMER HARD WHEAT / FIELD VEGETABLES,spring_durum_hard_wheat,3301010202,summer_durum_hard_wheat,3301010203 +114,SOMMERMENGGETREIDE,SUMMER MESLIN,spring_meslin,3301011102,summer_meslin,3301011103 +155,SOMMERHAFER,SUMMER OATS,spring_oats,3301010502,summer_oats,3301010503 +156,SOMMERHAFER / FELDGEMÜSE,SUMMER OATS / FIELD VEGETABLES,spring_oats,3301010502,summer_oats,3301010503 +509,SOMMERMOHN,SUMMER POPPY,summer_poppy,3301060602,summer_poppy,3301060602 +302,SOMMERRAPS,SUMMER RAPES,summer_rapeseed_rape,3301060403,summer_rapeseed_rape,3301060403 +141,SOMMERROGGEN,SUMMER RYE,spring_rye,3301010302,summer_rye,3301010303 +143,SOMMERROGGEN / FELDGEMÜSE,SUMMER RYE / FIELD VEGETABLES,spring_rye,3301010302,summer_rye,3301010303 +146,SOMMERDINKEL (SPELZ),SUMMER SPELT (SPELT),spring_spelt,3301011002,summer_spelt,3301011003 +157,SOMMERTRITICALE,SUMMER TRITICALE,spring_triticale,3301010802,summer_triticale,3301010803 +624,SOMMERWICKEN,SUMMER VETCHES,vetches,3301090305,vetches,3301090305 +307,SONNENBLUMEN,SUNFLOWERS,sunflower,3301060500,sunflower,3301060500 +203,SÜSSLUPINEN,SWEET LUPINES,sweet_lupins,3301020700,sweet_lupins,3301020700 +817,TAFELÄPFEL,TABLE APPLES (Edible Apples),apples,3303010200,apples,3303010200 +818,TAFELBIRNEN,TABLE PEARS,pears,3303011200,pears,3303011200 +769,TOPINAMBUR,TOPINAMBUR (Helianthus tuberosus),topinambur_jerusalem_artichoke,3301180000,topinambur_jerusalem_artichoke,3301180000 +767,ROLLRASEN,TURF,sod_turf,3301090207,sod_turf,3301090207 +840,GEMÜSE IM FOLIENTUNNEL,VEGETABLES IN THE FILM TUNNEL,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +839,GEMÜSE IM GEWÄCHSHAUS,VEGETABLES IN THE GREENHOUSE,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +638,WICKEN - GETREIDE GEMENGE,VETCHES - GRAIN MIX,vetches,3301090305,vetches,3301090305 +862,REBSCHULEN,VINE SCHOOLS,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +901,WEIN,WINE,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +902,WEIN BODENGESUNDUNG,WINE SOIL RECOVERY,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +110,WINTERGERSTE,WINTER BARLEY,winter_barley,3301010401,winter_barley,3301010401 +153,WINTERGERSTE / BUCHWEIZEN,WINTER BARLEY / BUCKWHEAT,winter_barley,3301010401,winter_barley,3301010401 +178,WINTERGERSTE/KLEE,WINTER BARLEY / CLOVER,winter_barley,3301010401,winter_barley,3301010401 +176,WINTERGERSTE/KLEEGRAS,WINTER BARLEY / CLOVER GRASS,winter_barley,3301010401,winter_barley,3301010401 +125,WINTERGERSTE / FELDGEMÜSE,WINTER BARLEY / FIELD VEGETABLES,winter_barley,3301010401,winter_barley,3301010401 +177,WINTERGERSTE/FUTTERGRÄSER,WINTER BARLEY / FORAGE GRASS,winter_barley,3301010401,winter_barley,3301010401 +175,WINTERGERSTE/KÖRNERMAIS,WINTER BARLEY / GRAIN CORN,winter_barley,3301010401,winter_barley,3301010401 +174,WINTERGERSTE/SILOMAIS,WINTER BARLEY / SILO CORN,winter_barley,3301010401,winter_barley,3301010401 +776,WINTERGERSTE/SOJABOHNEN,WINTER BARLEY / SOYBEANS,winter_barley,3301010401,winter_barley,3301010401 +303,WINTERRÜBSEN,WINTER BEETS,beetroot_beets,3301290200,beetroot_beets,3301290200 +536,WINTERKÜMMEL,WINTER CARAWAY,caraway,3301061211,caraway,3301061211 +127,WINTERMENGGETREIDE / FELDGEMÜSE,WINTER CEREALS / FIELD VEGETABLES,winter_meslin,3301011101,winter_meslin,3301011101 +167,WINTERHARTWEIZEN (DURUM) / FELDGEMÜSE,WINTER DURUM WHEAT (DURUM) / FIELD VEGETABLES,winter_durum_hard_wheat,3301010201,winter_durum_hard_wheat,3301010201 +169,WINTERHARTWEIZEN (DURUM) / BUCHWEIZEN,WINTER HARD WHEAT (DURUM) / BUCKWHEAT,winter_durum_hard_wheat,3301010201,winter_durum_hard_wheat,3301010201 +113,WINTERMENGGETREIDE,WINTER MESLIN,winter_meslin,3301011101,winter_meslin,3301011101 +112,WINTERHAFER,WINTER OATS,winter_oats,3301010501,winter_oats,3301010501 +510,WINTERMOHN,WINTER POPPY,winter_poppy,3301060601,winter_poppy,3301060601 +301,WINTERRAPS,WINTER RAPE,winter_rapeseed_rape,3301060401,winter_rapeseed_rape,3301060401 +142,WINTERROGGEN,WINTER RYE,winter_rye,3301010301,winter_rye,3301010301 +144,WINTERROGGEN / FELDGEMÜSE,WINTER RYE / FIELD VEGETABLES,winter_rye,3301010301,winter_rye,3301010301 +138,WINTERWEICHWEIZEN,WINTER SOFT WHEAT,winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +140,WINTERWEICHWEIZEN / FELDGEMÜSE,WINTER SOFT WHEAT / FIELD VEGETABLES,winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +145,WINTERDINKEL (SPELZ),WINTER SPELT (SPELT),winter_spelt,3301011001,winter_spelt,3301011001 +148,WINTERDINKEL (SPELZ) / FELDGEMÜSE,WINTER SPELT (SPELT) / FIELD VEGETABLES,winter_spelt,3301011001,winter_spelt,3301011001 +116,WINTERTRITICALE,WINTER TRITICALE,winter_triticale,3301010801,winter_triticale,3301010801 +130,WINTERTRITICALE / FELDGEMÜSE,WINTER TRITICALE / FIELD VEGETABLES,winter_triticale,3301010801,winter_triticale,3301010801 +162,WINTERTRITICALE / FUTTERRÜBE,WINTER TRITICALE / FORAGE BEET,winter_triticale,3301010801,winter_triticale,3301010801 +171,WINTERTRITICALE / HIRSE,WINTER TRITICALE / MILLET,winter_triticale,3301010801,winter_triticale,3301010801 +625,WINTERWICKEN,WINTER VETCHES,vetches,3301090305,vetches,3301090305 +160,WINTERWEICHWEIZEN / BUCHWEIZEN,WINTER WHEAT / BUCKWHEAT,winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +360,GLÖZ HECKE / UFERGEHÖLZ,HEDGE / SHORE WOOD OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +361,GLÖZ RAIN / BÖSCHUNG / TROCKENSTEINMAUER,RIDGE / EMBANKMENT / DRY STONE WALL OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +359,GLÖZ FELDGEHÖLZ / BAUM- / GEBÜSCHGRUPPE,FIELD WOOD / TREE / SHRUB GROUP OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +184,KLEEGRAS / SILOMAIS,CLOVER GRASS / SILO CORN,clover,3301090303,clover,3301090303 +362,GLÖZ GRABEN / UFERRANDSTREIFEN,DITCH / BORDER STRIP OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +212,SOMMERACKERBOHNEN,SUMMER FIELD BEANS,beans,3301020100,beans,3301020100 +642,ACKERWEIDE,FIELD PASTURE,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +214,SOMMERKÖRNERERBSEN,SUMMER GRAIN PEAS,peas,3301020600,peas,3301020600 +179,REIS,RICE,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +365,GLÖZ TEICH / TÜMPEL,POND / POOL OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +723,SONSTIGE HUTWEIDEFLÄCHEN,OTHER MEAGER MEADOW AREAS,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +364,GLÖZ STEINRIEGEL / STEINHAGE,STONE RIDGE / STONE HEDGE OF GOOD AGRICULTURAL AND ECOLOGICAL CONDITION,not_known_and_other,3399000000,not_known_and_other,3399000000 +185,SOMMERHAFER / KLEEGRAS,SUMMER OATS / CLOVER GRASS,spring_oats,3301010502,summer_oats,3301010503 +213,WINTERKÖRNERERBSEN,WINTER GRAIN PEAS,peas,3301020600,peas,3301020600 +992,ALMWEIDEFLÄCHE,ALPINE PASTURE AREA,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +855,HASELNÜSSE UND ANDERE SCHALENFRÜCHTE,HAZELNUTS AND OTHER NUTS,nuts,3303030000,nuts,3303030000 +627,SOMMERLINSEN,SUMMER LENTILS,lentils,3301020500,lentils,3301020500 +854,WALNÜSSE,WALNUTS,nuts,3303030000,nuts,3303030000 +181,SOMMERGERSTE (HERBSTANBAU),SUMMER BARLEY (AUTUMN SOWING),spring_barley,3301010402,summer_barley,3301010403 +211,WINTERACKERBOHNEN,WINTER FIELD BEANS,beans,3301020100,beans,3301020100 +366,LSE MEHRNUTZENHECKE,PROTECTED LANDSCAPE ELEMENT MULTI-USE HEDGE,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +180,KRESSE,CRESS,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +367,LSE AGROFORSTSTREIFEN,PROTECTED LANDSCAPE ELEMENT AGROFORESTRY STRIP,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +628,WINTERLINSEN,WINTER LENTILS,lentils,3301020500,lentils,3301020500 +304,SOMMERRÜBSEN,SUMMER BEETS,beetroot_beets,3301290200,beetroot_beets,3301290200 +363,GLÖZ NATURDENKMAL FLÄCHE,Natural Site Area,not_known_and_other,3399000000,not_known_and_other,3399000000 +158,SOMMERTRITICALE / FELDGEMÜSE,SUMMER TRITICALE / FIELD VEGETABLES,spring_triticale,3301010802,summer_triticale,3301010803 +777,GRÜNSCHNITTROGGEN / SENF,GREEN CUT RYE / MUSTARD,rye,3301010300,rye,3301010300 +182,SOMMERGERSTE (HERBSTANBAU) / FELDGEMÜSE,SUMMER BARLEY (AUTUMN SOWING) / FIELD VEGETABLES,spring_barley,3301010402,summer_barley,3301010403 +183,SOMMERGERSTE (HERBSTANBAU) / BUCHWEIZEN,SUMMER BARLEY (AUTUMN SOWING) / BUCKWHEAT,spring_barley,3301010402,summer_barley,3301010403 +139,SOMMERWEICHWEIZEN / FELDGEMÜSE,SUMMER SOFT WHEAT / FIELD VEGETABLES,spring_common_soft_wheat,3301010102,summer_common_soft_wheat,3301010103 \ No newline at end of file diff --git a/tests/data-files/convert/ch/ch.csv b/tests/data-files/convert/ch/ch.csv new file mode 100644 index 00000000..62344d1b --- /dev/null +++ b/tests/data-files/convert/ch/ch.csv @@ -0,0 +1,158 @@ +original_name,translated_name,HCAT3_name,HCAT3_code +Heuwiesen mit Zufütterung während der Sömmerung,Hay meadows with supplementary feeding during summering,pasture_meadow_grassland_grass,3302000000 +Buntbrache,Colourful fallow,fallow_land_not_crop,3301110000 +Triticale,Triticale,triticale,3301010800 +Gärtnerische Kulturen in Gewächshäusern mit festem Fundament,Horticultural crops in greenhouses with fixed foundation,fresh_vegetables,3301070000 +Winterraps zur Speiseölgewinnung,Winter rapeseed for edible oil,rapeseed_rape,3301060400 +Hanf zur Nutzung der Samen,Hemp for seed use,hemp_cannabis,3301061000 +Mohn,Poppy,poppy,3301060600 +Anderer Hanf,Other hemp,hemp_cannabis,3301061000 +Nassreis,Wet rice,rice,3301010700 +Mischel Brotgetreide,Mixed bread cereals,other_cereals,3301019900 +Beerenkulturen in geschütztem Anbau ohne festes Fundament; auf Pflanztischen oder -gestellen,Berry crops protected without fixed foundation on tables,berries_berry_species,3303020000 +"Übrige Grünfläche (Dauergrünfläche), beitragsberechtigt",Other grassland (permanent grassland) eligible,pasture_meadow_grassland_grass,3302000000 +"Übrige offene Ackerfläche, nicht beitragsberechtigt",Other open arable land not eligible,other_arable_land_crops,3301990000 +Hartweizen,Durum wheat,durum_hard_wheat,3301010200 +"Mehrjährige nachwachsende Rohstoffe (Chinaschilf, usw.)",Perennial renewable resources (miscanthus etc.),miscanthus_silvergrass,3301083000 +Hafer,Oats,oats,3301010500 +Rebflächen mit natürlicher Artenvielfalt,Vineyards with natural biodiversity,vineyards_wine_vine_rebland_grapes,3303060000 +"Übrige Flächen mit Dauerkulturen, nicht beitragsberechtigt",Other perennial crop areas not eligible,orchards_fruits,3303010000 +"Übrige offene Ackerfläche, nicht beitragsberechtigt (regionsspezifische Biodiversitätsförderfläche)",Other open arable land not eligible (region-specific biodiversity area),not_known_and_other,3399000000 +Mehrjährige Gewürz- und Medizinalpflanzen,Perennial spice and medicinal plants,other_flowers_ornamental_plants,3301089900 +"Ziersträucher, Ziergehölze und Zierstauden",Ornamental shrubs trees and perennials,flowers_ornamental_plants,3301080000 +Hopfen,Hops,hops,3301060200 +Hirse zur Nutzung ganze Pflanze,Millet/sorghum for whole plant use,millet_sorghum,3301010900 +Saatmais (Vertragsanbau),Seed/grain maize (contract),grain_maize_corn_popcorn,3301010600 +Extensiv genutzte Weiden,Extensively used pastures,pasture_meadow_grassland_grass,3302000000 +Mehrjährige Beeren,Perennial berries,berries_berry_species,3303020000 +Trüffelanlagen,Truffle plantations,other_arable_land_crops,3301990000 +"Ruderalflächen, Steinhaufen und -wälle",Ruderal areas stone piles and walls,not_known_and_other,3399000000 +Trockenmauern,Dry stone walls,not_known_and_other,3399000000 +Wintergerste,Winter barley,winter_barley,3301010401 +Kunstwiesen (ohne Weiden),Artificial meadows (without pastures),temporary_grass,3301090100 +Tabak,Tobacco,tobacco,3301060100 +Getreide siliert,Cereals for silage,plants_harvested_green,3301090000 +Silo- und Grünmais,Silage and green maize,green_silo_maize,3301090400 +Zuckerrüben,Sugar beet,sugar_beet,3301290700 +Dinkel,Spelt,spelt,3301011000 +"Übrige Flächen innerhalb der LN, beitragsberechtigt",Other areas within utilized agri area eligible,not_known_and_other,3399000000 +Übrige Spezialkulturen in Gewächshäusern mit festem Fundament,Other specialty crops in greenhouses with foundation,other_flowers_ornamental_plants,3301089900 +Soja,Soy,soy_soybeans,3301160000 +"Mischungen von Linsen mit Getreide oder Leindotter, mindestens 30 % Anteil Linsen bei der Ernte (zur Körnergewinnung)",Mixtures of lentils with cereals or camelina (>=30% lentils at harvest) grain use,legumes_dried_pulses_protein_crops,3301020000 +Gepflegte Selven (Edelkastanienbäume),Managed chestnut groves,orchards_fruits,3303010000 +"Wassergräben, Tümpel, Teiche",Ditches ponds pools,not_known_and_other,3399000000 +Waldweiden (ohne bewaldete Fläche),Forest pastures (without forested area),pasture_meadow_grassland_grass,3302000000 +"Übrige Flächen innerhalb der LN, nicht beitragsberechtigt",Other areas within utilized agri area not eligible,not_known_and_other,3399000000 +Lupinen,Lupins,sweet_lupins,3301020700 +Winterweizen (ohne Futterweizen der Sortenliste swiss granum),Winter wheat (excluding feed list),winter_common_soft_wheat,3301010101 +Sorghum zur Nutzung ganze Pflanze,Sorghum for whole plant use,millet_sorghum,3301010900 +"Übrige offene Ackerfläche, beitragsberechtigt",Other open arable land eligible,other_arable_land_crops,3301990000 +Mischel Futtergetreide,Mixed feed cereals,other_cereals,3301019900 +"Übrige Kulturen in geschütztem Anbau ohne festes Fundament, beitragsberechtigt",Other protected crops without foundation eligible,other_flowers_ornamental_plants,3301089900 +Obstanlagen (Steinobst),Orchards (stone fruit),orchards_fruits,3303010000 +Christbäume,Christmas trees,other_tree_wood_forest,3306990000 +"Unbefestigte, natürliche Wege",Unpaved natural tracks,not_known_and_other,3399000000 +Bohnen und Wicken zur Körnergewinnung (z.B. Ackerbohnen),Beans and vetches for grain (e.g. field beans),beans,3301020100 +Sommerweizen (ohne Futterweizen der Sortenliste swiss granum),Spring wheat (excluding feed list),spring_common_soft_wheat,3301010102 +Futterrüben,Fodder beets,mangelwurzel_fodder_beet,3301290400 +Übrige Spezialkulturen in geschütztem Anbau ohne festes Fundament,Other specialty crops protected without foundation,other_flowers_ornamental_plants,3301089900 +Rotationsbrache,Rotational fallow,fallow_land_not_crop,3301110000 +"Übrige Baumschulen (Rosen, Zierstauden, usw.)",Other nurseries (roses ornamentals etc.),flowers_ornamental_plants,3301080000 +Gemüsekulturen in geschütztem Anbau ohne festes Fundament; auf Pflanztischen oder -gestellen,Vegetable crops protected without foundation on benches,fresh_vegetables,3301070000 +Sommerraps als nachwachsender Rohstoff,Spring rapeseed as renewable resource,rapeseed_rape,3301060400 +Trockenreis,Dryland rice,rice,3301010700 +"Weiden (Heimweiden, übrige Weiden ohne Sömmerungsweiden)",Pastures (home pastures others without summering pastures),pasture_meadow_grassland_grass,3302000000 +Uferwiesen (ohne Weiden),Riverside meadows (without pastures),pasture_meadow_grassland_grass,3302000000 +Spargel,Asparagus,asparagus,3301200000 +Übrige Flächen ausserhalb der LN und SF,Other areas outside utilized land and special areas,not_known_and_other,3399000000 +"Übrige Grünfläche (Dauergrünflächen), nicht beitragsberechtigt",Other grassland (permanent) not eligible,pasture_meadow_grassland_grass,3302000000 +Einjährige Beeren (z.B. Erdbeeren),Annual berries (e.g. strawberries),strawberries,3301130000 +"Mischungen von Bohnen, Wicken, Erbsen, Kichererbsen und Lupinen mit Getreide oder Leindotter, mindestens 30 % Anteil Leguminosen bei der Ernte (zur Körnergewinnung)",Mixtures of beans vetches peas chickpeas lupins with cereals or camelina (>=30% legumes) grain use,legumes_dried_pulses_protein_crops,3301020000 +Sonnenblumen als nachwachsender Rohstoff,Sunflowers as renewable resource,sunflower,3301060500 +"Andere Obstanlagen (Kiwis, Holunder usw.)",Other orchards (kiwi elder etc.),orchards_fruits,3303010000 +"Hecken-, Feld- und Ufergehölze (mit Pufferstreifen)",Hedges field and riparian shrubs (with buffer strips),shrubberries_shrubs,3303080000 +Regionsspezifische Biodiversitätsförderflächen,Region-specific biodiversity areas,not_known_and_other,3399000000 +Hausgärten,Home gardens,not_known_and_other,3399000000 +Streueflächen im Sömmerungsgebiet,Litter meadows in summering area,pasture_meadow_grassland_grass,3302000000 +Gemüsekulturen in geschütztem Anbau ohne festes Fundament; im gewachsenen Boden,Vegetables protected without foundation in soil,fresh_vegetables,3301070000 +Übrige Kulturen in geschütztem Anbau mit festem Fundament,Other crops in protected cultivation with foundation,other_flowers_ornamental_plants,3301089900 +Sonnenblumen zur Speiseölgewinnung,Sunflowers for edible oil,sunflower,3301060500 +Lein,Linseed (oil flax),flax_linseed_oil,3301060702 +"Übrige Kunstwiese, beitragsberechtigt (z.B. Schweineweide, Geflügelweide)",Other artificial meadow eligible (e.g. pig poultry pasture),temporary_grass,3301090100 +Roggen,Rye,rye,3301010300 +Futterweizen gemäss Sortenliste swiss granum,Feed wheat according to swiss granum list,common_soft_wheat,3301010100 +Maulbeerbaumanlagen (Fütterung Seidenraupen),Mulberry tree plantations (silkworm feeding),orchards_fruits,3303010000 +Gemüsekulturen in Gewächshäusern mit festem Fundament,Vegetable crops in greenhouses with fixed foundation,fresh_vegetables,3301070000 +Gärtnerische Kulturen in geschütztem Anbau ohne festes Fundament,Horticultural crops protected without fixed foundation,flowers_ornamental_plants,3301080000 +Sommerraps zur Speiseölgewinnung,Spring rapeseed for edible oil,rapeseed_rape,3301060400 +"Offene Ackerfläche, beitragsberechtigt (regionsspezifische Biodiversitätsförderfläche)",Open arable land eligible (region-specific biodiversity area),not_known_and_other,3399000000 +"Einjährige nachwachsende Rohstoffe (Kenaf, usw.)",Annual renewable resources (kenaf etc.),other_arable_land_crops,3301990000 +"Einjährige Freilandgemüse, ohne Konservengemüse",Annual open-field vegetables (excl. canning),fresh_vegetables,3301070000 +"Heuwiesen im Sömmerungsgebiet, Typ wenig intensiv genutzte Wiese",Hay meadows in summering area type low intensity,pasture_meadow_grassland_grass,3302000000 +Baumschulen von Obst und Beeren,Nurseries of fruit and berries,orchards_fruits,3303010000 +"Kulturen in ganzjährig geschütztem Anbau, beitragsberechtigt aggregiert",Crops in year-round protected cultivation eligible aggregated,other_flowers_ornamental_plants,3301089900 +"Übrige Kulturen in geschütztem Anbau ohne festes Fundament, nicht beitragsberechtigt",Other protected crops without foundation not eligible,other_flowers_ornamental_plants,3301089900 +Wenig intensiv genutzte Wiesen (ohne Weiden),Low-intensity meadows (without pastures),pasture_meadow_grassland_grass,3302000000 +"Heuwiesen im Sömmerungsgebiet, Typ extensiv genutzte Wiese",Hay meadows in summering area type extensively used,pasture_meadow_grassland_grass,3302000000 +Futterleguminosen für die Samenproduktion (Vertragsanbau),Forage legumes for seed production (contract),legumes_dried_pulses_protein_crops,3301020000 +Permakultur,Permaculture,not_known_and_other,3399000000 +"Hecken-, Feld- und Ufergehölze (mit Pufferstreifen) (regionsspezifische Biodiversitätsförderfläche)",Hedges field and riparian shrubs (with buffers) region-specific biodiversity,shrubberries_shrubs,3303080000 +Leindotter,Camelina,camelina,3301061500 +Übrige Dauerwiesen (ohne Weiden),Other permanent meadows (without pastures),pasture_meadow_grassland_grass,3302000000 +"Übrige Dauerweiden, beitragsberechtigt aggregiert",Other permanent pastures eligible aggregated,pasture_meadow_grassland_grass,3302000000 +Regionsspezifische Biodiversitätsförderfläche (Grünflächen ohne Weiden),Region-specific biodiversity area (grasslands without pastures),not_known_and_other,3399000000 +Erbsen zur Körnergewinnung (z.B. Eiweisserbsen),Peas for grain (e.g. protein peas),peas,3301020600 +Kichererbsen,Chickpeas,chickpeas,3301020200 +Senf,Mustard,mustard,3301210100 +Körnermais,Grain maize,grain_maize_corn_popcorn,3301010600 +Freiland-Konservengemüse,Open-field processing vegetables,fresh_vegetables,3301070000 +"Landwirtschaftliche Produktion in Gebäuden (z. B. Champignon, Brüsseler)",Agricultural production in buildings (e.g. mushrooms Belgian endive),not_known_and_other,3399000000 +Pflanzkartoffeln (Vertragsanbau),Seed potatoes (contract),potatoes,3301030000 +Saum auf Ackerflächen,Field margin on arable land,not_known_and_other,3399000000 +Linsen,Lentils,lentils,3301020500 +Saflor,Safflower,safflower,3301083900 +Obstanlagen (Birnen),Orchards (pears),orchards_fruits,3303010000 +Streueflächen in der LN,Litter meadows in utilized agri area,pasture_meadow_grassland_grass,3302000000 +Rhabarber,Rhubarb,rhubarb,3301230000 +"Übrige unproduktive Flächen (z.B. gemulchte Flächen, stark verunkrautete Flächen, Hecken ohne Pufferstreifen)","Other unproductive areas (e.g. mulched, weedy, hedges without buffers)",not_known_and_other,3399000000 +"Flächen ohne landwirtschaftliche Hauptzweckbestimmung (erschlossenes Bauland, Spiel-, Reit-, Camping-, Golf-, Flug- und Militärplätze oder ausgemarchte Bereiche von Eisenbahnen, öffentlichen Strassen und Gewässern)",Areas without agricultural main purpose (built land etc.),not_known_and_other,3399000000 +Buchweizen,Buckwheat,buckwheat,3301150200 +"Einjährige gärtnerische Freilandkulturen (Blumen, Rollrasen usw.)",Annual horticultural open-field crops (flowers turf etc.),flowers_ornamental_plants,3301080000 +Sorghum zur Körnergewinnung,Sorghum for grain,millet_sorghum,3301010900 +Mehrjährige gärtnerische Freilandkulturen (nicht im Gewächshaus),Perennial horticultural open-field crops (not greenhouse),other_flowers_ornamental_plants,3301089900 +Pilze in geschütztem Anbau mit festem Fundament,Mushrooms in protected cultivation with foundation,not_known_and_other,3399000000 +Beerenkulturen in Gewächshäusern mit festem Fundament,Berry crops in greenhouses with fixed foundation,berries_berry_species,3303020000 +Obstanlagen (Äpfel),Orchards (apples),orchards_fruits,3303010000 +Sömmerungsweiden,Summer pastures,pasture_meadow_grassland_grass,3302000000 +"Emmer, Einkorn",Emmer einkorn,other_cereals,3301019900 +Regionsspezifische Biodiversitätsförderflächen (Weiden),Region-specific biodiversity areas (pastures),not_known_and_other,3399000000 +Pilze (Freiland),Mushrooms (open field),not_known_and_other,3399000000 +Reben,Vines/vineyards,vineyards_wine_vine_rebland_grapes,3303060000 +Quinoa,Quinoa,quinoa,3301150300 +Hirse zur Körnergewinnung,Millet/sorghum for grain,millet_sorghum,3301010900 +"Heuwiesen im Sömmerungsgebiet, Übrige Wiesen",Hay meadows in summering area other meadows,pasture_meadow_grassland_grass,3302000000 +Futtergräser für die Samenproduktion (Vertragsanbau),Forage grasses for seed production (contract),temporary_grass,3301090100 +Wurzeln der Treibzichorie,Roots of forcing chicory,chicory_chicories,3301310200 +Kartoffeln,Potatoes,potatoes,3301030000 +Baumschule von Forstpflanzen ausserhalb der Forstzone,Nursery of forest plants outside forest zone,tree_wood_forest,3306000000 +Obstanlagen aggregiert,Orchards aggregated,orchards_fruits,3303010000 +"Übrige Flächen mit Dauerkulturen, beitragsberechtigt",Other perennial crop areas eligible,orchards_fruits,3303010000 +Baumschulen von Reben,Vine nurseries,vineyards_wine_vine_rebland_grapes,3303070000 +Wald,Forest,tree_wood_forest,3306000000 +"Hecken-, Feld- und Ufergehölze (mit Krautsaum)",Hedges field and riparian shrubs (with herb strip),shrubberries_shrubs,3303080000 +Sommergerste,Spring barley,spring_barley,3301010402 +Extensiv genutzte Wiesen (ohne Weiden),Extensively used meadows (without pastures),pasture_meadow_grassland_grass,3302000000 +Ölkürbisse,Oil pumpkins,pumpkin_squash_gourd,3301140400 +Nützlingsstreifen auf offener Ackerfläche,Beneficial insect strips on arable land,not_known_and_other,3399000000 +Einjährige Gewürz- und Medizinalpflanzen,Annual spice and medicinal plants,other_arable_land_crops,3301990000 +Beerenkulturen in geschütztem Anbau ohne festes Fundament; im gewachsenen Boden,Berry crops protected without foundation in soil,berries_berry_species,3303020000 +Winterraps als nachwachsender Rohstoff,Winter rapeseed as renewable resource,rapeseed_rape,3301060400 +Hochstamm-Feldobstbäume (Punkte oder Flächen),High-stem field fruit trees (points or areas),orchards_fruits,3303010000 +Nussbäume (Punkte oder Flächen),Nut trees (points or areas),orchards_fruits,3303010000 +Edelkastanienbäume,Sweet chestnut trees,orchards_fruits,3303010000 +Einheimische standortgerechte Einzelbäume und Alleen (Punkte oder Flächen),Native site-appropriate single trees and avenues (points or areas),shrubberries_shrubs,3303080000 +Andere Bäume,Other trees,not_known_and_other,3399000000 +Andere Bäume (regionsspezifische Biodiversitätsförderfläche),Other trees (region-specific biodiversity area),not_known_and_other,3399000000 +Andere Elemente (regionsspezifische Biodiversitätsförderfläche),Other elements (region-specific biodiversity area),not_known_and_other,3399000000 +Ackerschonstreifen,Conservation strips on arable land,not_known_and_other,3399000000 +Getreide in weiter Reihen,Cereals in wide rows,other_cereals,3301019900 \ No newline at end of file diff --git a/tests/data-files/convert/cz/cz_2023.csv b/tests/data-files/convert/cz/cz_2023.csv new file mode 100644 index 00000000..f9d99419 --- /dev/null +++ b/tests/data-files/convert/cz/cz_2023.csv @@ -0,0 +1,327 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code +2,Cukrovka,sugar beet,sugar_beet,3301290700 +3,Řepa krmná,fodder beet ,mangelwurzel_fodder_beet,3301290400 +4,Topinambur,Jerusalem artichoke,topinambur_jerusalem_artichoke,3301180000 +5,Čekanka průmyslová,Industrial chicory,chicory_chicories,3301310200 +8,Bér vlašský (italský),Millet,millet_sorghum,3301010900 +9,Čirok cukrový,Sugar sorghum,millet_sorghum,3301010900 +10,Ředkev olejná,Radishes,radish,3301290600 +11,Sléz přeslenitý,Chinese mallow,other_flowers_ornamental_plants,3301089900 +12,Svazenka vratičolistá,Lacy phacelia,phacelia,3301061400 +14,Kapusta krmná,Fodder Cabbage,other_brassica_oleracea_cabbage,3301210299 +15,Šťovík,Dock and Sorrel,sorrel,3301310700 +16,Tuřín,Swedes,swede_rutabaga,3301210500 +18,Kopr vonný,Dill,anethum_dill,3301061203 +19,Koriandr setý,Coriander,coriander,3301061215 +20,Majoránka zahradní,marjoram,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +21,Andělika lékařská,Norwegian angelica,angelica,3301061204 +23,Benedikt lékařský (čubet),St. Benedict's thistle,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +25,Divizna velkokvětá,dense-flowered mullein,other_flowers_ornamental_plants,3301089900 +26,Heřmánek pravý,Chamomile,chamomile,3301061213 +28,Jablečník obecný,Common apple tree,apples,3303010200 +30,Jestřabina lékařská,galega,galega,3301081900 +31,Jitrocel kopinatý,ribwort plantain,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +33,Kozlík lékařský,Valerian ,valerian,3301061238 +34,Levandule lékařská,English lavender,lavender_lavandula,3301061219 +35,Lékořice lysá,licorice ,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +36,Libeček lékařský,Lovage,lovage_maggiplant,3301061221 +38,Máta peprná,Peppermint,mints_peppermint,3301061222 +39,Meduňka lékařská,Lemon balm,lemon_balm_melissa,3301061220 +44,Ostropestřec mariánský,Milk thistle,marian_thistles,3301061300 +46,Pelyněk kozalec (estragon),Tarragon,tarragon,3301061236 +48,Proskurník lékařský,marshmallow,other_flowers_ornamental_plants,3301089900 +49,Topolovka růžová,common hollyhock,other_flowers_ornamental_plants,3301089900 +50,Reveň dlanitá,Chinese rhubarb,other_flowers_ornamental_plants,3301089900 +53,Řepík lékařský,common agrimony,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +55,Sléz maurský,Moorish mallow,other_flowers_ornamental_plants,3301089900 +56,Šalvěj lékařská,Sage,sage_chia,3301190000 +57,Třezalka tečkovaná,St. John's wort,st_johns_wort,3301061234 +58,Tymián obecný,Thyme,thyme,3301061237 +59,Včelník moldavský,Moldavian dragonhead,moldavian_dragonhead,3301061223 +61,Yzop lékařský,Hyssop,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +63,Řebříček obecný,Common yarrow,yarrow,3301061239 +65,Cizrna beraní,chickpea,chickpeas,3301020200 +67,Čočka jedlá,Lentils,lentils,3301020500 +68,Fazol polní,Field beans,beans,3301020100 +69,Hrách polní,Field peas,peas,3301020600 +70,Peluška jarní (Hrách rolní),Spring field pea (Pea),peas,3301020600 +71,Peluška ozimá (Hrách rolní),Winter field pea (Pea),peas,3301020600 +72,Jetel nachový,crimson clover,clover,3301090303 +73,Jetel luční,red clover,clover,3301090303 +74,Jetel perský (zvrácený),Persian clover,clover,3301090303 +75,Jetel plazivý,white clover,clover,3301090303 +76,Jetel švédský (zvrhlý),alsike clover,clover,3301090303 +77,Komonice,sweet clover,melilot,3301090304 +78,Lupina bílá,Lupine white,sweet_lupins,3301020700 +80,Lupina úzkolistá,Narrow-leaved lupine,sweet_lupins,3301020700 +81,Lupina proměnlivá (andská),Lupine variable (Andean),sweet_lupins,3301020700 +82,Ptačí noha setá (seradela),serradela,serradella,3301084200 +84,Štírovník růžkatý,common bird's-foot trefoil,legumes_harvested_green,3301090300 +85,Tolice dětelová,hop clover,clover,3301090303 +86,Úročník bolhoj,kidney vetch,legumes_harvested_green,3301090300 +87,Vičenec ligrus,common sainfoin,onobrychis_sainfoins,3301061600 +88,Vikev setá,common vetch,vetches,3301090305 +89,Vikev huňatá,fodder vetch ,vetches,3301090305 +90,Vikev panonská,Hungarian vetch,vetches,3301090305 +91,Vojtěška setá,Alfafa,alfalfa_lucerne,3301090301 +92,Pískavice řecké seno,Fenugreek,fenugreek,3301020400 +93,Jetel ladní,field clover,clover,3301090303 +94,Jetel (T. nigrescens Viv.),Clover (T. nigrescens Viv.),clover,3301090303 +95,Čirok zrnový,Grain sorghum,millet_sorghum,3301010900 +96,Ječmen jarní,Spring barley,spring_barley,3301010402 +97,Ječmen ozimý dvouřadý,Two-row winter barley,winter_barley,3301010401 +98,Ječmen ozimý víceřadý,Multi-row winter barley,winter_barley,3301010401 +100,Oves nahý jarní,Naked spring oats,spring_oats,3301010502 +101,Oves pluchatý jarní,Fluffy spring oats,spring_oats,3301010502 +102,Pohanka obecná,Buckwheat,buckwheat,3301150200 +103,Proso seté,Millet,millet_sorghum,3301010900 +104,Pšenice setá jarní,Spring Wheat,spring_common_soft_wheat,3301010102 +105,Pšenice setá ozimá,Winter Wheat,winter_common_soft_wheat,3301010101 +106,Pšenice špalda ozimá,Winter Spelt,winter_spelt,3301011001 +107,Pšenice tvrdá jarní,Durum wheat spring,spring_durum_hard_wheat,3301010202 +108,Pšenice tvrdá ozimá,"Durum wheat, winter",winter_durum_hard_wheat,3301010201 +109,Tritikale jarní,Spring triticale,spring_triticale,3301010802 +110,Tritikale ozimé,Winter triticale,winter_triticale,3301010801 +111,Žito jarní,Spring rye,spring_rye,3301010302 +112,Žito ozimé,Winter rye,winter_rye,3301010301 +113,Lesknice kanárská,Canary grass ,canary_seed_canaryseed,3301011400 +114,Pšenice dvouzrnka,Emmer wheat ,emmer,3301011200 +125,Laskavec,amaranth,amaranth,3301150100 +130,Měsíček lékařský,pot marigold,calendula_marigold,3301061210 +165,Bazalka,Basil,basil,3301061207 +178,Aksamitník (afrikán),Tagetes,tagetes,3301084700 +246,Mateřídouška,Thyme,thyme,3301061237 +249,Čirok sudanská tráva,Sorghum × drummondii,millet_sorghum,3301010900 +250,Hořčice sareptská (hnědá),Sarepta mustard (brown),mustard,3301210100 +251,Řepice ozimá,Rapeseed winter,winter_rapeseed_rape,3301060401 +252,Hořčice bílá,White mustard,mustard,3301210100 +253,Kmín kořenný ozimý,winter Caraway,caraway,3301061211 +255,Len olejný,flax oil,flax_linseed_oil,3301060702 +256,Len přadný,flax,flax_linen,3301060701 +257,Lnička setá jarní,Gold of pleasure spring,camelina,3301061500 +258,Lnička setá ozimá,Gold of pleasure winter,camelina,3301061500 +260,Řepka jarní,Rapeseed Spring,spring_rapeseed_rape,3301060402 +261,Řepka ozimá,Rapeseed winter,winter_rapeseed_rape,3301060401 +262,Slunečnice roční,Sunflowers,sunflower,3301060500 +263,Sója,Soybean,soy_soybeans,3301160000 +264,Světlice barvířská,Safflower,safflower,3301083900 +276,Jahodník,Strawberry,strawberries,3301130000 +315,Bojínek hlíznatý,Phleum bertolonii (grass),poaceae_grasses,3301090200 +316,Bojínek luční,Timothy grass,timothy,3301090209 +317,Festulolium,Festulolium,festulolium,3301090204 +318,Jílek hybridní,hybrid ryegrass,lolium_ryegrass,3301090205 +319,Jílek mnohokvětý italský,Italian ryegrass,lolium_ryegrass,3301090205 +320,Jílek mnohokvětý jednoletý,ryegrass,lolium_ryegrass,3301090205 +321,Jílek vytrvalý,perennial ryegrass,lolium_ryegrass,3301090205 +322,Kostřava červená,Red fescue,festuca_fescue,3301090202 +323,Kostřava luční,Meadow fescue,festuca_fescue,3301090202 +324,Kostřava ovčí,Sheep fescue,festuca_fescue,3301090202 +325,Kostřava rákosovitá,tall fescue,festuca_fescue,3301090202 +327,Lipnice hajní,wood bluegrass,poaceae_grasses,3301090200 +328,Lipnice luční,Kentucky bluegrass,poaceae_grasses,3301090200 +331,Medyněk vlnatý,Yorkshire fog,poaceae_grasses,3301090200 +333,Ovsík vyvýšený,bulbous oat grass,poaceae_grasses,3301090200 +334,Poháňka hřebenitá,crested dog's-tail,poaceae_grasses,3301090200 +335,Psineček veliký,black bent,poaceae_grasses,3301090200 +336,Psineček tenký,common bent,poaceae_grasses,3301090200 +338,Psárka luční,meadow foxtail ,poaceae_grasses,3301090200 +339,Srha hajní (Srha Aschersonova),Dactylis polygama,cocksfoot_catgrass,3301090203 +340,Srha laločnatá (říznačka),cat grass,cocksfoot_catgrass,3301090203 +342,Trojštět žlutavý,Yellow Oatgrass,poaceae_grasses,3301090200 +343,Sveřep bezbranný,Bromus inermis,poaceae_grasses,3301090200 +344,Tomka vonná,sweet vernal grass,poaceae_grasses,3301090200 +349,Chrastice rákosovitá,reed canary grass,poaceae_grasses,3301090200 +351,Dočasný travní porost,Temporary grassland,temporary_grass,3301090100 +352,Brokolice,Broccoli,broccoli,3301210202 +353,Kedluben,Kohlrabi,kohlrabi,3301210209 +354,Celer bulvový,Celeriac,celeriac,3301250100 +356,Celer řapíkatý,Celery,celery,3301250000 +357,Cibule jarní,Spring onions,scallion,3301220500 +358,Cibule ozimá,Onions (Winter onions),onions,3301220400 +359,Čekanka salátová (hlávková),Radicchio,other_salads_lettuce_leaf_vegetables,3301319900 +361,Česnek jarní,Spring garlic,garlic,3301220200 +362,Česnek ozimý,Winter garlic,garlic,3301220200 +363,Fazol zahradní (keříčkový),Garden beans (bush),beans,3301020100 +364,Fazol zahradní (pnoucí),Garden beans (climbing),beans,3301020100 +365,Fenykl obecný,Fennel,fennel,3301170000 +366,Hrách zahradní (dřeňový),Garden peas,peas,3301020600 +367,Hrách zahradní (cukrový),Garden peas (sugar),peas,3301020600 +368,Chřest,Asparagus,asparagus,3301200000 +369,Kapusta kadeřavá (kadeřávek),Kale,kale,3301210208 +370,Kapusta hlávková jarní,Spring cabbage,brassica_oleracea_cabbage,3301210200 +371,Kapusta růžičková,Brussels sprouts,brussels_sprouts,3301210203 +373,Kukuřice cukrová,Sweet corn,grain_maize_corn_popcorn,3301010600 +374,Kukuřice pukancová,Popcorn,grain_maize_corn_popcorn,3301010600 +375,Květák,Cauliflower,cauliflower,3301210204 +376,Křen selský víceletý,Perennial horseradish,horseradish,3301210400 +377,Lilek vejcoplodý (baklažán),Eggplant (eggplant),aubergine_eggplant,3301260000 +378,Mangold,Chard,chard,3301310100 +379,Meloun cukrový,Muskmelon,melon,3301140300 +380,Meloun vodní,Watermelon,watermelon,3301140500 +381,Mrkev krmná,Fodder Carrot,carrots_daucus,3301290300 +382,Mrkev jedlá,Carrot,carrots_daucus,3301290300 +383,Okurka nakládačka,Gherkin,cucumber_pickle,3301140100 +384,Okurka salátová,salad Cucumber,cucumber_pickle,3301140100 +385,Paprika kořeninová,Allspice paprika,bell_pepper_paprika,3301300100 +386,Paprika zeleninová,Vegetable paprika,bell_pepper_paprika,3301300100 +387,Pastinák,Parsnip,parsnips,3301290500 +388,Pažitka,Chive,chives,3301220100 +389,Petržel kořenová,Petroselinum,parsley,3301061227 +390,Petržel naťová,Parsley,parsley,3301061227 +391,Pór jarní,Spring leek,leek,3301220300 +392,Rajče determinantní,Tomato determinant,tomato,3301280000 +393,Rajče indeterminantní,Tomato indeterminate,tomato,3301280000 +395,Ředkev (jinde neuvedená),Radish (not elsewhere specified),radish,3301290600 +396,Ředkvička,Garden radish,radish,3301290600 +397,Řepa salátová,Beetroot,beetroot_beets,3301290200 +398,Řeřicha zahradní,Garden cress,cress,3301210300 +399,Salát hlávkový,Butterhead lettuce,other_salads_lettuce_leaf_vegetables,3301319900 +400,Salát listový,coral lettuce,other_salads_lettuce_leaf_vegetables,3301319900 +401,Salát římský,Romaine lettuce,other_salads_lettuce_leaf_vegetables,3301319900 +406,Tykev obecná (s výjimkou tykve olejné),Field Pumpkin (except butternut squash),pumpkin_squash_gourd,3301140400 +407,Tykev olejná,oilseed pumpkin,pumpkin_squash_gourd,3301140400 +408,Tykev velkoplodá,Cucurbita maxima,pumpkin_squash_gourd,3301140400 +409,Vodnice,Turnip,turnips,3301290800 +410,Zelí čínské,Chinese cabbage Bok choy,bok_choy_pak_choi,3301210201 +411,Zelí hlávkové bílé,Cabbage white,white_cabbage,3301210212 +412,Zelí hlávkové červené,Cabbage red,red_cabbage,3301210210 +413,Zelí pekingské,Napa cabbage,chinese_cabbage,3301210205 +414,Cibule zimní (sečka),Welsh onion,scallion,3301220500 +416,Šalotka,Shallots,shallot,3301220600 +421,Brambory sadbové,Seed potatoes,potatoes,3301030000 +422,Směsky luskovin,Legume mixes,legumes_dried_pulses_protein_crops,3301020000 +423,Směsky obilovin,Cereal mixtures,cereal,3301010000 +424,Trávy s leguminozami,Grasses with legumes,pasture_meadow_grassland_grass,3302000000 +425,Pastevní směs trav,Pasture mixture of grasses,pasture_meadow_grassland_grass,3302000000 +426,Luční směs trav,Grass meadow mix,pasture_meadow_grassland_grass,3302000000 +430,Brambory průmyslové,Industrial potatoes,potatoes,3301030000 +431,Jetelotravní směs (s převahou jetelovin),Clover grass mix (clover dominated),clover,3301090303 +432,Luskoobilní směs bez podsevu,Legume mixture without undersowing,legumes_dried_pulses_protein_crops,3301020000 +433,Kukuřice na siláž,silage maize,green_silo_maize,3301090400 +434,Kukuřice na zrno,Grain maize,grain_maize_corn_popcorn,3301010600 +435,Okrasné rostliny a trvalky nerozlišené (více druhů),Ornamental plants and perennials n.e.c. (more species),flowers_ornamental_plants,3301080000 +436,Zelenina brukvovitá (více druhů),Cruciferous vegetables (multiple types),brassicaceae_cruciferae,3301210000 +437,Zelenina tykvovitá (více druhů),Squash vegetables (multiple types),pumpkin_squash_gourd,3301140400 +438,Zelenina miříkovitá (více druhů),Apiaceae vegetables (multiple types),aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +439,Léčivé rostliny (více druhů),Medicinal plants (multiple species),aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +444,Cuketa,Zucchini,zucchini_courgette,3301140600 +445,Salát ledový,Iceberg salad,iceberg,3301310400 +446,Lupina modrá,Lupine blue,sweet_lupins,3301020700 +449,Žito trsnaté (lesní),Sepp Holzer Rye,rye,3301010300 +451,Úhor bez porostu,Fallow without vegetation,fallow_land_not_crop,3301110000 +453,Ozdobnice obrovská,giant miscanthus,miscanthus_silvergrass,3301083000 +454,Mužák prorostlý,cup plant,silphium_rosinweeds,3301084400 +455,Mastňák habešský,niger,guizotia_abyssinica_nyger,3301060900 +457,Směs trav pro energetické využití,A mixture of grasses for energy use,temporary_grass,3301090100 +458,Směs krmná (čejka),Feed mixture (lark),not_known_and_other,3399000000 +459,Směs pro krmný biopás,Mixture for feed biobelt,not_known_and_other,3399000000 +460,Směs pro nektarodárný biopás,Mixture for nectar-giving biobelt,not_known_and_other,3399000000 +461,Směs pro opylovače (čejka),Mixture for pollinators,not_known_and_other,3399000000 +463,Dočasně nezpůsobilá plocha,Temporarily ineligible area,not_known_and_other,3399000000 +464,Orná bez plodiny,Arable without a crop,fallow_land_not_crop,3301110000 +465,Špenát jarní,Spring spinach,spinach,3301310800 +466,Tykev pomíchaná,Mixed pumpkin,pumpkin_squash_gourd,3301140400 +468,Vojtěškotravní směs (s převahou jetelovin),Alfalfa mixture (predominantly clovers),clover,3301090303 +469,Ostatní směsky,Other mixes,not_known_and_other,3399000000 +470,Tykev muškátová,Crookneck pumpkin,pumpkin_squash_gourd,3301140400 +471,Trávy pro ochranné pásy,Grasses for protective strips,pasture_meadow_grassland_grass,3302000000 +473,Směs pro souvrať,Headland mix,not_known_and_other,3399000000 +474,Směs pro meziplodiny,Mixture for catch crops,other_arable_land_crops,3301990000 +522,Směs pro erozní pás,Mixture for erosion belt,not_known_and_other,3399000000 +524,Batáty,Sweet potatoes,sweet_potatoes,3301040000 +527,Dobromysl obecná,Oregano,oregano,3301061226 +529,Hrachor,Lathyrus,peas,3301020600 +530,Chmel sadba,Hops seedling,hops,3301060200 +532,Kopřiva dvoudomá,Stinging nettle,nettles,3301061225 +533,Roketa setá (rukola),"Rocket, Rucola",rocket_arugula,3301310600 +535,Merlík čilský,Quinoa ,quinoa,3301150300 +536,Mák setý ozimý,Poppy sown winter,winter_poppy,3301060601 +537,Směs trav čeledi lipnicovité,A mixture of grasses of the poaceae family,poaceae_grasses,3301090200 +538,Oves hřebílkatý,lopsided oat,unspecified_season_oats,3301010599 +539,Kostřava vláskovitá,hair fescue,festuca_fescue,3301090202 +540,Kostřava drsnolistá,hard fescue,festuca_fescue,3301090202 +541,Jestřabina východní,eastern galega,galega,3301081900 +542,Jetel alexandrijský,Berseem clover,clover,3301090303 +543,Bob polní,Field Bean,beans,3301020100 +545,Sveřep sitecký,Bromus grass,poaceae_grasses,3301090200 +546,Sveřep americký,rescuegrass,poaceae_grasses,3301090200 +547,Hořčice černá,Black mustard,mustard,3301210100 +548,Vojtěška proměnlivá,Hybrid Alfafa,alfalfa_lucerne,3301090301 +550,Mák setý jarní,Poppy sown in spring,summer_poppy,3301060602 +551,Brambory konzumní rané (sklizeň do 30.6.),Potatoes for consumption early (harvest until 30.6.),potatoes,3301030000 +552,Brambory konzumní pozdní,Late Potatoes,potatoes,3301030000 +554,Pšenice jednozrnka,Einkorn wheat,einkorn,3301011300 +555,Špenát ozimý,Winter spinach,spinach,3301310800 +556,Citronová tráva,Lemon grass,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +557,Chrpa modrá,Cornflower blue,cornflowers,3301081000 +559,Kapusta hlávková ozimá,winter savoy cabbage,savoy_cabbage,3301210211 +560,Kmín kořenný jarní,spring Caraway,caraway,3301061211 +561,Máta huňatá,Bushy mint,mints_peppermint,3301061222 +562,Máta jemná,Mild mint,mints_peppermint,3301061222 +564,Máta klasnatá,Spearmint,mints_peppermint,3301061222 +565,Máta okrouhlolistá,Round-leaf mint,mints_peppermint,3301061222 +567,Pór ozimý,Winter leek,leek,3301220300 +571,Rozmarýn lékařský,Rosemary,rosemary,3301061230 +577,Směs zlepšujících netržních plodin (jinde neuvedených),Mixture of improved non-market crops (not elsewhere specified),not_known_and_other,3399000000 +582,Šťovík kyselý - krmný OK2,Common Sorrel,sorrel,3301310700 +587,Třapatka nachová (Terčovka nachová),Echinacea purpurea,echinacea_sun_hat,3301081500 +588,Třezalka skvrnitá,spotted St. Johnswort,st_johns_wort,3301061234 +590,Žito energetické,rye for biogas,rye,3301010300 +591,Ředkev setá černá,Black Spanish radish ,radish,3301290600 +592,Čičorka,crownvetch,legumes_harvested_green,3301090300 +593,Pelyněk pontický,Roman wormwood,artemisia,3301061206 +20002,Topol Maximovičův a jeho kříženci,Populus maximowiczii Henry,populus,3306070000 +20004,Topol chlupatoplodý a jeho kříženci,Populus trichocarpa,populus,3306070000 +20005,Topol vznešený a jeho kříženci,Populus ×generosa,populus,3306070000 +20007,Topol kanadský,Populus × canadensis,populus,3306070000 +20008,Topol Simonův a jeho kříženci,Populus simonii,populus,3306070000 +20010,Topol balzámový a jeho kříženci,Populus balsamifera,populus,3306070000 +20011,Topol černý,Populus nigra,populus,3306070000 +20012,Topol osika,Populus tremula,aspen,3306020000 +20013,Vrba bílá a její kříženci,White willow and its hybrids,willows_osiers,3306080000 +20014,Vrba jíva a její kříženci,goat willow and its hybrids,willows_osiers,3306080000 +20015,Vrba košíkářská a její kříženci,basket willow and its hybrids,willows_osiers,3306080000 +20017,Jasan ztepilý,European Ash,other_tree_wood_forest,3306990000 +20018,Olše lepkavá,European alder,other_tree_wood_forest,3306990000 +40005,Konopí - Bialobrzeskie ,Hemp,hemp_cannabis,3301061000 +40019,Konopí - Fedora 17,Hemp,hemp_cannabis,3301061000 +40020,Konopí - Felina 32,Hemp,hemp_cannabis,3301061000 +40026,Konopí - Finola ,Hemp,finola,3301061001 +40027,Konopí - Futura 75,Hemp,hemp_cannabis,3301061000 +40040,Konopí - Santhica 27,Hemp,hemp_cannabis,3301061000 +40041,Konopí - Santhica 70,Hemp,hemp_cannabis,3301061000 +40045,Konopí - Tiborszállási,Hemp,hemp_cannabis,3301061000 +40047,Konopí - Tygra ,Hemp,hemp_cannabis,3301061000 +40049,Konopí - Uso-31,Hemp,hemp_cannabis,3301061000 +40280,Konopí - Eletta Campana,Hemp,hemp_cannabis,3301061000 +40282,Konopí - Fibror 79,Hemp,hemp_cannabis,3301061000 +40299,Konopí - Henola,Hemp,hemp_cannabis,3301061000 +40326,Konopí - Futura 83,Hemp,hemp_cannabis,3301061000 +50015,Směs pro meziplodiny pro zlepšení struktury půdy,Mixture for catch crops to improve soil structure,other_arable_land_crops,3301990000 +50017,Jetelotravní směs (s převahou travin),Clover-grass mix (grass-dominated),temporary_grass,3301090100 +50018,Směs pro druhově bohaté pokrytí orné půdy ,Mixture for species-rich arable land coverage ,other_arable_land_crops,3301990000 +50019,Oves nahý ozimý,Naked winter oats,winter_oats,3301010501 +50020,Oves pluchatý ozimý,Fluffy winter oats,winter_oats,3301010501 +50021,Pšenice špalda jarní,Spring Spelt,spring_spelt,3301011002 +50024,Směs pro úhor s porostem,Mixture of Fallow and growth,fallow_land_not_crop,3301110000 +50025,Křen selský jednoletý,Annual horseradish,horseradish,3301210400 +50026,Směs bílkovinných plodin (PVN) jednoletých,A mixture of annual protein crops (PVN).,legumes_dried_pulses_protein_crops,3301020000 +50027,Směs bílkovinných plodin (PVN) jednoletých s ostatními plodinami do 50 %,Mixture of annual protein crops (PVN) with other crops up to 50%,legumes_dried_pulses_protein_crops,3301020000 +50028,Směs bílkovinných plodin (PVN) víceletých,Mixture of protein crops (PVN) of perennials,legumes_dried_pulses_protein_crops,3301020000 +50029,Směs bílkovinných plodin (PVN) víceletých s jednoletými plodinami do 50 %,Mixture of protein crops (PVN) perennial with annual crops up to 50%,legumes_dried_pulses_protein_crops,3301020000 +50030,Luskoobilní směs s podsevem PVN,Legume mix with PVN undersowing,legumes_dried_pulses_protein_crops,3301020000 +50031,Směs PVN neprodukčních jednoletých,VAT mixture of non-producing annuals,not_known_and_other,3399000000 +50032,Směs PVN neprodukčních jednoletých s ost. plodinami do 50 %,A mixture of PVN non-productive annuals with other crops up to 50%,not_known_and_other,3399000000 +50033,Směs PVN neprodukčních víceletých,VAT mixture of non-productive perennials,not_known_and_other,3399000000 +50034,Směs PVN neprodukčních víceletých s ost. plodinami do 50 %,A mixture of PVN non-productive perennials with other crops up to 50%,not_known_and_other,3399000000 +50035,Směs trav s PVN do 50 %,A mixture of grasses with PVN up to 50%,temporary_grass,3301090100 +50036,Vojtěškotravní směs (s převahou travin),Alfalfa grass mixture (with a predominance of grasses),temporary_grass,3301090100 +50037,Směs pro nektarodárný úhor (ekoplatba),Mixture for nectar fallow ,fallow_land_not_crop,3301110000 +50038,Směs trav s nízkovzrůstající PVN do 10 %,A mixture of grasses with low-growing PVN up to 10%,temporary_grass,3301090100 +50040,Svazenka shloučená,Blue Curls,other_flowers_ornamental_plants,3301089900 +50041,Sadební materiál lesních dřevin,Forest planting material,unspecified_tree_wood_forest,3306980000 +50043,Směs pro souvrať (prémiová),Headland Mix (Premium),not_known_and_other,3399000000 +50044,Směs pro erozní pás (prémiová),Mixture for erosion belt (premium),not_known_and_other,3399000000 +50045,Směs pro dělící pás ,Mixture for dividing strip ,not_known_and_other,3399000000 +50047,Směs pro ozeleněný kolejový řádek (prémiová),Greening Lane Mix (Premium),not_known_and_other,3399000000 +50052,Úhor pro staré ekologické zemědělství,Fallow for old organic farming,fallow_land_not_crop,3301110000 \ No newline at end of file diff --git a/tests/data-files/convert/de_bb/de.csv b/tests/data-files/convert/de_bb/de.csv new file mode 100644 index 00000000..2b469583 --- /dev/null +++ b/tests/data-files/convert/de_bb/de.csv @@ -0,0 +1,422 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code +48,Mischung Mais/Bohne,Corn / bean mixture,grain_maize_corn_popcorn,3301010600 +49,Blühmischung für Biogas,Flowering mixture for biogas,flowers_ornamental_plants,3301080000 +50,Mischkulturen mit Saatgutmischung,Mixed cultures with seed mix,arable_land_seed_seedlings,3301100000 +51,Mischkulturen in Reihenanbau,Mixed crops in rows,other_arable_land_crops,3301990000 +52,Zwischenfrucht / Gründecke ÖVF,Intercrop / groundcover,other_arable_land_crops,3301990000 +53,Untersaat ÖVF,Undersowing ÖVF,arable_land_seed_seedlings,3301100000 +54,Streifen am Waldrand (Ohne Produktion) ÖVF,Strip at the edge of the forest (Without production) ÖVF,tree_wood_forest,3306000000 +55,Ufervegetation ÖVF,Riparian vegetation,not_known_and_other,3399000000 +56,ÖVF-Streifen AL,Ecological Preferance Area strips AL,not_known_and_other,3399000000 +57,Feldrand / Pufferstreifen ÖVF DGL,Buffer strips ecological preference zones permanent grassland,pasture_meadow_grassland_grass,3302000000 +58,Feldrand / Puferstreifen ÖVF AL,Buffer strips ecological preference zones permanent grassland,pasture_meadow_grassland_grass,3302000000 +59,KuP ÖVF,Priority ecological areas short rotation plants,not_known_and_other,3399000000 +60,Leguminosen ÖVF,Legumes ÖVF,legumes_dried_pulses_protein_crops,3301020000 +61,Aufforstungsflächen ÖVF,Afforestation areas ÖVF,afforestation_reforestation,3306010000 +62,Brachen Ohne Erzeugung ÖVF,Fallow land Without production ÖVF,fallow_land_not_crop,3301110000 +63,Miscanthus,silvergrass,miscanthus_silvergrass,3301083000 +64,Durchwachsene Silphie,cup plant,silphium_rosinweeds,3301084400 +65,brachliegende Flächen öVF Honigpflanzen (pollen- und nektarreiche Arten) -einjährig,fallow land öVF honey plants (species rich in pollen and nectar) -annual,fallow_land_not_crop,3301110000 +66,brachliegende Flächen öVF Honigpflanzen (pollen- und nektarreiche Arten) -mehrjährig,fallow land öVF honey plants (species rich in pollen and nectar) -perennial,fallow_land_not_crop,3301110000 +70,Hecken Oder Knicks > 10m CC,Hedges Or Knicks > 10m CC,not_known_and_other,3399000000 +71,Baumreihe >50m CC,Tree row >50m CC,tree_wood_forest,3306000000 +72,Feldgehölze 50-2.000 rn2 CC,"Field groves 50-2,000 rn2 CC",tree_wood_forest,3306000000 +73,Feuchtgebiete < 2.000 m2 CC,"Wetlands < 2,000 m2 CC",not_known_and_other,3399000000 +74,Einzelbäume CC,Single trees CC,tree_wood_forest,3306000000 +75,Tümpel Sölle und Doline CC,Ponds sinkholes and doline CC,not_known_and_other,3399000000 +76,"Natur-, Stein- Oder Trockenmauer CC","Natural, stone or dry stone wall CC",not_known_and_other,3399000000 +77,"Fels- und Steinriegel, naturversteinte Fläche CC","Rock and stone bar, natural petrified area CC",not_known_and_other,3399000000 +78,Feldraine CC,Field margins CC,not_known_and_other,3399000000 +79,"Trocken-, Be- und Entwässerungsgräben CC","Dry, irrigation and drainage ditches CC",not_known_and_other,3399000000 +80,Terrassen CC,Terraces CC,not_known_and_other,3399000000 +112,Winterhartweizen/Durum,Winter hard wheat / durum,winter_durum_hard_wheat,3301010201 +113,Sommerhartweizen/Durum,Summer durum wheat / durum,spring_durum_hard_wheat,3301010202 +114,Winter-Dinkel,Winter spelt,winter_spelt,3301011001 +115,Winterweichweizen,Winter soft wheat,winter_common_soft_wheat,3301010101 +116,Sommerweichweizen,summer wheat/Summer soft wheat,spring_common_soft_wheat,3301010102 +118,Winter-Emmer/-Einkorn,Winter emmer / single grain/Winter emmer / Einkorn,winter_emmer,3301011201 +119,Sommer-Emmer/-Einkom,Summer emmer / single grain/Summer emmer / Einkorn,spring_emmer,3301011202 +120,Sommer-Dinkel,Summer spelt,spring_spelt,3301011002 +121,"Winterroggen, Winter-Waldstaudenroggen",Winter rye,winter_rye,3301010301 +122,"Sommerroggen, Sommer-Waldstaudenroggen",Summer rye,spring_rye,3301010302 +125,Wintermenggetreide,Winter mixed grain,winter_meslin,3301011101 +126,Wintermenggetreide ohne Weizen,Winter mixed grain without wheat,winter_meslin,3301011101 +131,Wintergerste,Winter barley,winter_barley,3301010401 +132,Sommergerste,Summer barley,spring_barley,3301010402 +142,Winterhafer,Winter oats,winter_oats,3301010501 +143,Sommerhafer,Summer oats,spring_oats,3301010502 +144,Sommermenggetreide,Summer mixed grain,spring_meslin,3301011102 +145,Sommermenggetreide ohne Weizen,Summer mixed grain without wheat,spring_meslin,3301011102 +156,Wintertriticale,Winter triticale,winter_triticale,3301010801 +157,Sommertriticale,Summer triticale,spring_triticale,3301010802 +171,Mais (ohne Silomais NC 411 ),Corn without silage maize,grain_maize_corn_popcorn,3301010600 +172,Mais (Biogas),Corn (biogas),grain_maize_corn_popcorn,3301010600 +181,Rispenhirse,Millet/Millet (Panicum),millet_sorghum,3301010900 +182,Buchweizen,Buckwheat,buckwheat,3301150200 +183,Mohren-Zuckerhirse (ohne Sudangras NC 803),Sorghum,millet_sorghum,3301010900 +184,Kolbenhirse,Millet,millet_sorghum,3301010900 +186,"Amarant, Fuchsschwanz",amaranth,amaranth,3301150100 +187,Quinoa,Quinoa,quinoa,3301150300 +190,"Getreide einer Gattung/Art, die in der aktuellen Liste nicht aufgeführt ist",Cereals of a genus/species not included in the current list,other_cereals,3301019900 +210,"Erbsen (Markerbse, Schalerbse, Zuckererbse, Futtererbse, Peluschke",Peas (fresh / table peas free range)/Peas for grain production,peas,3301020600 +211,"Gemüseerbse (Markerbse, Schalerbse, Zuckererbse)",Vegetable pea (marrow pea/Vegetable pea,peas,3301020600 +212,Platterbse,Flat pea,peas,3301020600 +220,Ackerbohne/Puffbohne/Pferdebohne/Dicke Bohne,Field bean / broad bean / horse bean / broad bean/Field / broad / horse bean,beans,3301020100 +221,"Wicken (Pannonische Wicke, Zottelwicke, Saatwicke)",Vetches,vetches,3301090305 +222,Dicke Bohne,Broad bean,beans,3301020100 +230,"Lupinen (Süßlupine, weiße Lupine, blaue/schmalblättrige Lupine, geIbe Lupine, Anden-Lupine",Lupins (sweet lupine/Lupins,sweet_lupins,3301020700 +240,Erbsen/Bohnen,Peas beans/Mixed peas / beans,legumes_dried_pulses_protein_crops,3301020000 +250,Gemenge Leguminosen/Getreide,Mixture of legumes / grain,legumes_dried_pulses_protein_crops,3301020000 +290,"Hülsenfrucht einer Gattung/Art, die in der aktuellen Liste nicht aufgeführt ist",Legume of a genus/species not included in the current list,legumes_dried_pulses_protein_crops,3301020000 +292,Linsen,Lentils,lentils,3301020500 +311,Winterraps,Winter rape,winter_rapeseed_rape,3301060401 +312,Sommerraps,Summer rape,summer_rapeseed_rape,3301060403 +315,"Winterrübsen (Rübsen, Rübsamen, Rübsaat)",Winter rape (oil radish/Winter turnip rape (also rapeseed),winter_rapeseed_rape,3301060401 +316,"Sommerrübsen (Rübsen, Rübsamen, Rübsaat)",Summer rapeseed (rapeseed/Summer turnip rape (also rapeseed),summer_rapeseed_rape,3301060403 +320,Sonnenblumen,sunflowers,sunflower,3301060500 +330,Sojabohnen,Soybeans,soy_soybeans,3301160000 +341,"Lein, Flachs",Flax/Flax (flax linseed),flax_linseed,3301060700 +390,"Ölfrucht einer Gattung/Art, die in der aktuellen Liste nicht aufgeführt ist",Oil fruit of a genus/species not included in the current list,oilseed_crops,3301060800 +392,Meerkohl/Krambe,Sea Kale/Crambe,other_brassica_oleracea_cabbage,3301210299 +393,Leindotter,Camelina,camelina,3301061500 +411,Silomais (als Hauptfutter),Silage maize (as staple feed),green_silo_maize,3301090400 +413,Futterrübe/Runkelrübe,Fodder beet / beetroot,mangelwurzel_fodder_beet,3301290400 +414,"Kohlrübe, Steckrübe",Rutabaga,swede_rutabaga,3301210500 +421,Rot-/Weiß-/Alexandriner-/lnkarnat-/Erd-/Schweden-/Persischer Klee,Clover (nitrogen-fixing plant VF)/different types of clover,clover,3301090303 +422,Kleegras,Clover grass,clover,3301090303 +423,"Luzerne, Hopfenklee/Gelbklee, Bastardluzerne/Sandluzerne",alfalfa,alfalfa_lucerne,3301090301 +424,Ackergras,Arable grass,pasture_meadow_grassland_grass,3302000000 +425,Klee-Luzerne-Gemisch,Clover and alfalfa mixture,clover,3301090303 +426,"Bockshornklee, Schabzieger Klee",Clover (not VF)/Fenugreek clover,clover,3301090303 +427,"Hornklee, Hornschotenklee",Horn clover/Horn clover horn pod clover,clover,3301090303 +428,Wechselgrünland,Alternating grassland,pasture_meadow_grassland_grass,3302000000 +429,Esparsette,Esparsette,esparsette_onobrychis,3301020300 +430,Serradella,serradella,serradella,3301084200 +431,Steinklee,Sweet clover,melilot,3301090304 +432,"Kleemischung aus NC 421, 427, 431 (stickstoffbindend)",Clover mixture from NC 421/Clover mixture (or box horn clover),clover,3301090303 +433,Luzerne-Gras,Alfalfa grass/Alfalfa-grass mixture,alfalfa_lucerne,3301090301 +441,Wiesen (Grünlandneueinsaat im Rahmen von AUKM),Meadows (grassland reseeding under AUKM),pasture_meadow_grassland_grass,3302000000 +442,Mähweiden (Grünlandneueinsaat im Rahmen von AUKM),Mowing pastures,pasture_meadow_grassland_grass,3302000000 +443,Weiden (Grünlandneueinsaat im Rahmen von AUKM),pastures,pasture_meadow_grassland_grass,3302000000 +444,DGL Neueinsaat als Ersatz für genehmigten DGL Umbruch,DGL new sowing as a replacement for approved DGL plowing,other_arable_land_crops,3301990000 +451,Wiesen,grasslands,pasture_meadow_grassland_grass,3302000000 +452,Mähweiden,Mowing pastures,pasture_meadow_grassland_grass,3302000000 +453,Weiden und Almen,Pastures and alpine pastures,pasture_meadow_grassland_grass,3302000000 +454,Hutungen,low-yield permanent grassland,pasture_meadow_grassland_grass,3302000000 +455,Almen und Alpen,Mountain pastures and alps,pasture_meadow_grassland_grass,3302000000 +458,Streuwiesen,Scattered meadows,pasture_meadow_grassland_grass,3302000000 +459,Grünland,Grassland (permanent grassland),pasture_meadow_grassland_grass,3302000000 +460,Sommerweiden für Wanderschafe,Summer pastures for wandering sheep,pasture_meadow_grassland_grass,3302000000 +462,Beweidete Sandheiden,Grazed sandy heaths,pasture_meadow_grassland_grass,3302000000 +463,Beweidete Moorheiden,Grazed bog heaths,pasture_meadow_grassland_grass,3302000000 +464,Beweidete Magerrasen,Grazed grasslands,pasture_meadow_grassland_grass,3302000000 +465,Beweidete montane Wiesen,Grazed montane meadows,pasture_meadow_grassland_grass,3302000000 +466,Gemähte Magerrasen,Mowed grasslands,pasture_meadow_grassland_grass,3302000000 +467,Gemähte montane Wiesen,Mown montane meadows,pasture_meadow_grassland_grass,3302000000 +480,Streuobstfläche mit Grünlandnutzung,Orchards with grassland use/Orchards with Permanent grassland use,orchards_fruits,3303010000 +481,Streuobstfläche ohne Grünlandnutzung,Orchards without grassland use/Orchards without Permanent grassland use,orchards_fruits,3303010000 +490,Nicht DZ-beihilfefähige Hutungen,Hute forest not eligible for DZ aid.,tree_wood_forest,3306000000 +491,Anteil an Gemeinschaftsweiden,Proportion of common pastures,pasture_meadow_grassland_grass,3302000000 +492,Dauergrünland unter etablierten lokalen Praktiken (z.B. Heide),Permanent grassland under established local practices (e.g. heather)/established local practices,pasture_meadow_grassland_grass,3302000000 +510,Goldrute (Solidago),Goldenrod (Solidago),goldenrod,3301082200 +511,Streptocarpus/Drehfrucht,Streptocarpus/Twist fruit,other_flowers_ornamental_plants,3301089900 +512,Iberischer Drachenkopf,dragonhead,other_flowers_ornamental_plants,3301089900 +513,Braunellen,prunella vulgaris,other_flowers_ornamental_plants,3301089900 +514,Hauswurz (Sempervivum),houseleeks,other_flowers_ornamental_plants,3301089900 +515,Mühlenbeckia/Drahtsträucher,wire bushes,wire_bush,3303080700 +516,Knöterich (Persicaria),Knotweed (Persicaria),other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +517,Garten-Petunie,Garden petunias,other_flowers_ornamental_plants,3301089900 +518,Polygonum,Polygonum,polygonum,3301061229 +519,Köcherblümchen (Cuphea),Caddis flower (Cuphea),other_flowers_ornamental_plants,3301089900 +520,Silberbrandschopf,Silver comb,silver_comb,3301061233 +545,Stilllegung nach FELEG/GAUALG,Decommissioning according to FELEG/GAUALG,unmaintained,3308000000 +555,"Stillgelegte Fläche gem. FLAMA, 20-jährig hier: Ökologische Stilllegung ab 1999","Set-aside area according to FLAMA, 20-year here: Ecological set-aside as of 1999",unmaintained,3308000000 +556,Aufforstung nach der Aufforstungsprämie (vor 1995),Afforestation after the afforestation premium (before 1995).,afforestation_reforestation,3306010000 +549,Stilllegung für Naturschutz und Landschaftspflege (5-Jahresprogramm) (auf AL),Set-aside for nature conservation and landscape management (5-year program) (on AL).,unmaintained,3308000000 +559,Stilllegung für Naturschutz und Landschaftspflege (5-Jahresprogramm) (auf GL),Set-aside for nature conservation and landscape management (5-year program) (on GL).,unmaintained,3308000000 +560,Brache im Rahmen einer VNS-Maßnahme,Fallow land within the framework of a VNS measure,fallow_land_not_crop,3301110000 +563,nach Art. 22 bis 24 der VO (EG) Nr. 1257/99 stillgelegte Ackerfläche,arable land set aside according to Art. 22 to 24 of Regulation (EC) No. 1257/99,unmaintained,3308000000 +564,nach VO 1257/1999 oder VO (EG) Nr. 1698/2005 oder VO 1305/2013 aufgeforstete Flächen,Areas afforested according to VO 1257/1999 or VO (EG) No. 1698/2005 or VO 1305/2013/Afforestation in rural areas,afforestation_reforestation,3306010000 +567,nach Art. 22 bis 24 der VO (EG) Nr. 1257/99 stillgelegte Dauergrünlandfläche,Longterm. o. 20 years old stilll. Permanent grass land,pasture_meadow_grassland_grass,3302000000 +568,"aufgeforstete Dauergrünlandfächen, weder nach CO 1257/99 oder VO 1698/2005 oder VO 1305/2013","afforested permanent grassland areas, neither according to CO 1257/99 or VO 1698/2005 or VO 1305/2013",afforestation_reforestation,3306010000 +572,Uferrandstrfprog.(DGL nur AUM),Bank edge control program (permanent greenland only AUM),not_known_and_other,3399000000 +573,Uferrandstreifenprogramm,Riparian fringe program/Uferrandstrfprog. (AL only AUM),not_known_and_other,3399000000 +574,Blühstreifen (MSL-Maßnahme),Flower strips (only to be used for areas with AUM-BS 1)/Flower strips (only AUM),flowers_ornamental_plants,3301080000 +575,Blühfläche (MSL-Maßnahme),Flowering area (only to be used for areas with AUM-BS 1)/Flowering area (only AUM),flowers_ornamental_plants,3301080000 +576,Schutzstreifen Erosion,Protection strip erosion (only to be used for areas with AUM-BS 7.1)/Protective strip erosion (only AUM),not_known_and_other,3399000000 +581,Grünbrache 1-jährig,Green fallow 1-year,fallow_land_not_crop,3301110000 +582,Grünbrache 2-jährig,Green fallow 2-year,fallow_land_not_crop,3301110000 +583,"Nicht landwirtschaftliche, aber nach Art. 32(2b (i)) der VO (EG) Nr.1307/2013 beihilfefähige Fläche Naturschutzfiächen die 2008",Not agricultural/Nature conservation (1307 / 2013-32-2bi),not_known_and_other,3399000000 +584,"Nicht landwirtschaftliche, aber nach Art. 32(2b (i)) der VO (EG) Nr. 1307/2013 beihilfefähige Fläche Maßnahmen aus Natura2000",Non-agricultural area eligible for aid under Article 32(2b (i)) of Regulation (EC) No 1307/2013 Natura2000 measures,not_known_and_other,3399000000 +585,"Nicht landwirtschaftliche, aber nach Art. 32(2b (i)) der VO (EG) Nr. 1307/2013 beihilfefähige Fläche Maßnahmen aus der",Non-agricultural area eligible for aid under Article 32(2b (i)) of Regulation (EC) No 1307/2013,not_known_and_other,3399000000 +586,Heckenpflanzung Erosionsschutz,Hedge planting erosion control,not_known_and_other,3399000000 +587,Heckenpflanzung Vogelschutz,Hedge planting bird protection,not_known_and_other,3399000000 +590,Brache mit Einsaat von einjährigen Blühmischungen,Fallow with annual sowing of flower mix/Fallow (Flowering Mix),fallow_land_not_crop,3301110000 +591,Ackerland aus der Erzeugung genommen iSd. Art. 4 Abs. 1 Buchst. c) ii) VO 1307/2013,Arable land withdrawn from production (self-greening)/AL taken from generation,unmaintained,3308000000 +592,Dauergrünland aus der Erzeugung genommen iSd. Art. 4 Abs. Buchst. c) ii) VO 1307/2013,Permanent grassland withdrawn from production within the meaning of Art. 4 para. 1 letter c) ii) Regulation 1307/2013/Permanent grass land suspendet from production,pasture_meadow_grassland_grass,3302000000 +593,Dauerkulturen aus der Erzeugung genommen iSd. Art. 4 Abs. Buchst. c) ii) VO 1307/2013,DK removed from production,unmaintained,3308000000 +594,Honigpflanzen genutzte brachliegende Flächen (pollen- und nektarreiche Arten) -einjährig,Honey plants used fallow land (species rich in pollen and nectar) -annual,fallow_land_not_crop,3301110000 +595,Honigpflanzen genutzte brachliegende Flächen (pollen- und nektarreiche Arten) -mehrjährig,Honey plants used fallow land (species rich in pollen and nectar) - perennial,fallow_land_not_crop,3301110000 +599,Brachefläche Vertragsnaturs.,Fallow land of contract nature conservation,fallow_land_not_crop,3301110000 +601,Stärkekartoffeln,Starch potatoes and other potatoes,potatoes,3301030000 +602,Kartoffeln (Speise),Potatoes (food)/potatoes,potatoes,3301030000 +603,Zuckerrüben,Sugar beet,sugar_beet,3301290700 +604,Topinambur,Topinambur,topinambur_jerusalem_artichoke,3301180000 +605,Süßkartoffel,sweet potato,sweet_potatoes,3301040000 +606,Pflanzkartoffeln,Seed potatoes,potatoes,3301030000 +610,Gemüse,vegetables,fresh_vegetables,3301070000 +611,Gemüse-Kreuzblütler,Vegetable cruciferous vegetables,brassicaceae_cruciferae,3301210000 +613,"Gemüsekohl (Kopfkohl, Wirsing, Rot-/Weißkohl, Spitzkohl, Grünkohl, Kohlrabi, Markstammkohl, Blumenkohl, Romanesco, Brokkoli, Rosenkohl, Zierkohl)",Vegetable cabbage (head cabbage/Vegetable cabbage (also ornamental cabbage),brassica_oleracea_cabbage,3301210200 +614,Brauner Senf/Sareptasenf,Brown mustard / Sareptasenf/Brown mustard (Sareptasenf),mustard,3301210100 +615,Echte Brunnenkresse,True watercress,cress,3301210300 +616,"Garten-Senfrauke, Rucola",Arugla/Arugula,rocket_arugula,3301310600 +617,Gartenkresse,Garden cress,cress,3301210300 +618,"Gartenrettiche (Weiße/rote Rettiche, schwarzer Winterrettich, Ölrettich, Radieschen",Garden radishes (white / red radishes/Garden radishes,radish,3301290600 +619,"Weißer Senf, Gelber Senf",White mustard,mustard,3301210100 +620,"Steckrübe, Kohlrübe (Gemüseanbau)",rutabaga (vegetable cultivation),swede_rutabaga,3301210500 +621,Gemüse-Nachtschattengewächse,Vegetable nightshade family,fresh_vegetables,3301070000 +622,Tomaten,tomatoes,tomato,3301280000 +623,Auberginen,Eggplant,aubergine_eggplant,3301260000 +624,"Paprika, Chilli, Peperoni",Spanish pepper (paprika/Paprika chilli hot peppers,capsicum,3301300000 +625,Schwarze Tollkirsche,Black belladonna,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +626,Gemüse-Kürbisgewächse,Vegetable cucurbits,cucurbits,3301140000 +627,"Gurke (Salatgurke, Einlegegurke)",Cucumber (cucumber/Cucumber (also pickled cucumber),cucumber_pickle,3301140100 +628,Zuckermelone,Sugar melon (cucumis melo),melon,3301140300 +629,"Riesenkürbis (Riesenkürbis, Hokkaidokürbis)",Giant Pumpkin (Giant Pumpkin/Giant pumpkin (also Hokkaido),pumpkin_squash_gourd,3301140400 +630,"Gartenkürbis (Gartenkürbis, Steirischer Kürbis, Zucchini, Spaghettikürbis, Zierkürbis)",Garden pumpkin (garden pumpkin/Pumpkin (Zucchini,pumpkin_squash_gourd,3301140400 +631,Melone (Wassermelone),Melon (watermelon),watermelon,3301140500 +632,Andere Gemüsearten,Other vegetables,fresh_vegetables,3301070000 +633,"Lauch (Speise-Zwiebel, Schalotte, Lauch, Knoblauch, Schnittlauch, Winterheckenzwiebel, Bärlauch)",Allium / leek (onion/Onions / leeks,alliums,3301220000 +634,"Möhre (Möhre/Karotte, Futtermöhre)",Carrot (carrot / carrot/Carrot (also feeding carrot),carrots_daucus,3301290300 +635,"Gartenbohne (Gartenbohne/Buschbohne/Stangenbohne, Feuerbohne/Prunkbohne",Garden bean (French bean / French bean / runner bean/Kidney bean,beans,3301020100 +636,Feldsalat/Ackersalat/ Rapunzel,Lamb's lettuce / field lettuce / Rapunzel/Lamb's lettuce (also Rapunzel),lambs_lettuce_rapunzel,3301310500 +637,"Lattich (Garten-Salat/Lattich, Lollo Rosso, Romana-Salat/Römischer Salat",Lattich (garden lettuce / lettuce/Lettuce (garden,salads_lettuce_leaf_vegetables,3301310000 +638,Spinat,spinach,spinach,3301310800 +639,"Mangold, Rote Beete/Rote Rübe","Swiss chard, beetroot",beetroot_beets,3301290200 +640,Melde (Garten-Melde),Garden orache,other_salads_lettuce_leaf_vegetables,3301319900 +641,"Sellerie (Knollen-Sellerie, Bleich-Sellerie, Stangen-Sellerie)",Celery (celery root/Celery (tuber / pale / stick),celery,3301250000 +642,Ampfer (Wiesen-Sauerampfer),Dock (meadow sorrel),sorrel,3301310700 +643,Pastinaken,Parsnips,parsnips,3301290500 +644,"Zichorien/Wegwarten (Chicoree, Radiccio, krausblättrige Endivie, ganzblättrige Endive, Zichorie)",Chicory / Wegwarten (chicory/Chicories,chicory_chicories,3301310200 +645,Kichererbsen,Chickpeas,chickpeas,3301020200 +646,Meerettich,horseradish,horseradish,3301210400 +647,Schwarzwurzeln,Salsify,salsify,3301084000 +648,"Fenchel (Gemüsefenchel, Körnerfenchel)",Fennel (vegetable fennel/Fennel (vegetables / grains),fennel,3301170000 +649,"Gemüserübsen (Stoppelrübe, Weiße Rübe, Bayerische Rübe, Mairübe, Chinakohl, Pak-Choi, Teltower Rübchen, Stielmus, Herbstrübe)",Vegetable turnip (stubble turnip/Vegetable turnips,brassicaceae_cruciferae,3301210000 +650,Küchenkräuter/Heil-und Gewürzpflanzen,Kitchen herbs / medicinal and aromatic plants,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +651,"Dill, Gurkenkraut",Dill/Anethum (dill,anethum_dill,3301061203 +652,"Kerbel (Kerbel/echter Kerbel, Wiesenkerbel)",Chervil (real chervil)/Chervil (also meadow chervil),chervil,3301061214 +653,Anis,anise/Bibernels (aniseed),anise_aniseed,3301061205 +654,Kümmel,Caraway seed/Caraway (real caraway),caraway,3301061211 +655,Kreuzkümmel,cumin,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +656,"Schwarzkümmel (Echter Schwarzkümmel, Jungfer im Grünen)",Black cumin (real black cumin/Black cumin,black_cumin,3301061208 +657,Koriander,coriander,coriander,3301061215 +658,Liebstöckel/Maggikraut,Lovage / Maggi herb,lovage_maggiplant,3301061221 +659,Petersilie,parsley/Petroselinum (parsley),parsley,3301061227 +660,Basilikum,basil,basil,3301061207 +661,Rosmarin,rosemary,rosemary,3301061230 +662,"Salbei (Küchen-/Heilsalbei, Buntschopf-Salbei)",Sage,sage_chia,3301190000 +663,Borretsch,Borage,borage,3301061209 +664,"Oregano (Echter Majoran, Oregano/Dost/Wilder Majoran)",Oregano (real marjoram/Oregano (marjoram dost),oregano,3301061226 +665,Bohnenkraut,Savory/Savory herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +666,Ysop/Eisenkraut,Hyssop / Verbena,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +667,Verbenen (Echtes Eisenkraut),Verbena,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +668,"Lavendel (Echter Lavendel, Speik-Lavendel, Hybrid-Lavendel)",Lavender (real lavender/lavender,lavender_lavandula,3301061219 +669,Thymian,thyme/Thyme (also garden thyme),thyme,3301061237 +670,Melisse (Zitronenmelisse),Balm (lemon balm)/Melissa (lemon balm),lemon_balm_melissa,3301061220 +671,Enzian,gentian/Gentians,gentians,3301082000 +672,"Minzen (Pfefferminze, Grüne Minze)",Mints (peppermint/Mints (pepperm. Green m.),mints_peppermint,3301061222 +673,"Wermut, Estragon, Beifuß",Artemisia (Wer. Estr. Beif.),artemisia,3301061206 +674,Ringelblumen (Garten-Ringelblume),Marigolds (garden marigold)/Marigolds (garden r.),calendula_marigold,3301061210 +675,"Sonnenhut (Schmalblättriger Sonnenhut, Purpur-Sonnenhut)",Sun hat (narrow purple),echinacea_sun_hat,3301081500 +676,Wegerich (Spitzwegerich),Plantain (ribwort),other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +677,Kamillen (Echte Kamille),Chamomiles (real chamomile),chamomile,3301061213 +678,Schafgarben (Gelbe Schafgarbe),Yarrow (yellow yarrow),yarrow,3301061239 +679,Baldrian (Echter Baldrian),Valerian (real valerian),valerian,3301061238 +680,Echtes Johanniskraut/Hyperikum,Real St. Johns wort / hypericum/St. John's Herbs (Genuine J.),st_johns_wort,3301061234 +681,Frauenmantel,Lady's mantle,alchemilla_ladys_mantle,3301061202 +682,Mariendisteln,Milk thistles/Marian thistles,marian_thistles,3301061300 +683,Geißraute,goat's rue,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +684,Löwenzahn,dandelion,dandelions,3301081400 +685,"Engelwurzen (Arznei-Engelwurz, Echter Engelwurz)",Angelica,angelica,3301061204 +686,Malven (Wilde Malve),Mallow (Wild Mallow),other_flowers_ornamental_plants,3301089900 +687,echte Arnika (Arnica montana),real arnica (Arnica montana),other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +701,Hanf,Industrial hemp/hemp,hemp_cannabis,3301061000 +702,"Rollrasen, Vegetationsmappen für Dachbegrünung",rolled turf/Turf,sod_turf,3301090207 +703,Färber-Waid,Dyer's woad,isatis_tinctoria_woad,3301082400 +704,Kanariensaat/Echtes Glanzgras,Glossy grass,canary_seed_canaryseed,3301011400 +705,Virginischer Tabak,Virginian tobacco,tobacco,3301060100 +706,"Mohn (Schlafmohn, Backmohn)",Poppy seeds,poppy,3301060600 +707,Erdbeeren,Strawberries,strawberries,3301130000 +708,Färberdisteln,Safflower,safflower,3301083900 +709,Brennnesseln (Große Brennnessel),Nettles (great nettle)/Nettles (large nettles),nettles,3301061225 +710,Färberkrapp (Rubia tinctorum),Common madder (Rubia tinctorum),rubia_tinctorum_common_madder,3301061231 +720,Zierpflanzen,Ornamental plants (including flowers to pick yourself),flowers_ornamental_plants,3301080000 +721,Goldlack,wallflower,other_flowers_ornamental_plants,3301089900 +722,Einjähriges Silberblatt,Annual silver leaf,lunaria_honesty_silver,3301082700 +723,Garten-/Sommerlevkoje,hoary stock,other_flowers_ornamental_plants,3301089900 +724,Kugelamarant (Echter Kugelamarant),globe amaranth,other_flowers_ornamental_plants,3301089900 +725,Taglilien (Essbare Taglilie),daylily,other_flowers_ornamental_plants,3301089900 +726,Lilien (Türkenbund),Lilies (king lily/Lilies (turkish union),lilies,3301082500 +727,Narzissen / Osterglocken,Daffodils / daffodils,narcissus_daffodil,3301083300 +728,Bischofskraut,Cartilage carrots (bishop's herb),carrots_daucus,3301290300 +729,Hasenohren (rundblättriges Hasenohr),thorow-wax,other_flowers_ornamental_plants,3301089900 +730,Seidenpflanzen (Indianer-Seidenpflanze),Silk plants (Indian-S.),other_flowers_ornamental_plants,3301089900 +731,Hyazinthe (Garten-Hyazinthe),hyacinth,other_flowers_ornamental_plants,3301089900 +732,Milchstern,Milky Star (Cape Milky Star),milk_star,3301082900 +733,Astern (Sommeraster),Asters,asters,3301080300 +734,"Chrysanthemen (Garten-Chrysantheme, Winteraster)",Chrysanthemum winter aster,chrysanthemum,3301080900 +735,Strohblumen,Everlasting flowers/Everlasting flowers (garden),other_flowers_ornamental_plants,3301089900 +736,Edelweiß,Edelweiss (Alpine Edelweiss),edelweiss,3301081600 +737,Margeriten,Daisies,daisy_daisies,3301081300 +738,"Rudbeckien (Schwarzäugige Rudbeckie/Sonnenhut, Leuchtender Sonnenhut Schlitzblättriger Sonnenhut)",Rudbeckia (coneflower),rudbeckia_coneflowers,3301083800 +739,Tagetes/Studentenblume,Tagetes / marigold/Tagetes,tagetes,3301084700 +740,Wucherblumen (Mutterkraut),tansies,other_flowers_ornamental_plants,3301089900 +741,Strandflieder (Geflügelter Strandflieder),Limonium,other_flowers_ornamental_plants,3301089900 +742,Spreublumen (Einjährige Papierblume),Chaff Flowers (Annual Paper Flower),other_flowers_ornamental_plants,3301089900 +743,Zinnien,Zinnias,zinnias,3301085200 +744,Taubnesseln (Weiße Taubnessel),Dead Nettle (White Dead Nettle),nettles,3301061225 +745,Gladiolen,Gladioli/Gladioli (garden gladiolus),gladiolus_gladioli,3301082100 +746,Tulpen,Tulips/Tulips (garden tulip),tulips,3301084900 +747,Trauben-Silberkerze,Christopher herbs,actaea_baneberry_christopher_herbs,3301061201 +748,Rittersporn,Field Knight Spurs,other_flowers_ornamental_plants,3301089900 +749,Skabiosen,pincushion flower,other_flowers_ornamental_plants,3301089900 +750,Dahlien,Dahlias/Dahlias (garden dahlia),dahlia,3301081200 +751,Rosenwurz,Rhodiola (rose root),other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +752,"Krokusse (Safran, Garten-Krokus)",Crocuses (saffron,saffron_crocus_sativus,3301061232 +753,Hibiskus (Chinesischer Roseneibisch),hibiscus,hibiscus,3301061218 +754,Strauch-/Bechermalven (Bechermalve),annual mallow,other_flowers_ornamental_plants,3301089900 +755,Wolfsmilch,spurge,other_flowers_ornamental_plants,3301089900 +756,Löwenmäulchen (Großes Löwenmaul),Snapdragons,snapdragons,3301084500 +757,Montbretien,Garden Montbretie,other_flowers_ornamental_plants,3301089900 +758,Halskräuter (Blaues Halskraut),blue throatwort,other_flowers_ornamental_plants,3301089900 +759,Gipskräuter (Schleierkraut),Gypsophila,other_flowers_ornamental_plants,3301089900 +760,Pampasgräser (Amerikanisches Pampasgras),pampas grass,poaceae_grasses,3301090200 +761,Kosmeen (Gemeines Schmuckkörbchen),cosmos,other_flowers_ornamental_plants,3301089900 +762,Nachtkerzen (Diptam),evening primroses,primrose,3301083500 +763,Nachtkerzen (Oenothera),evening primroses,primrose,3301083500 +764,Königskerzen (Großblütige Königskerze),dense-flowered mullein,other_flowers_ornamental_plants,3301089900 +765,Kapuzinerkresse,Capuchin cresses,cress,3301210300 +766,"Pfingstrosen/Päonien (Gemeine Pfingstrose, Strauch-Pfingstrose)",Peonies / Peonies (Common Peony/Peonies (also shrub),peony_peonies,3301083400 +767,Schwertlilien (Deutsche Schwertlilie),Irises (German iris),iris,3301082300 +768,"Wiesenknopf (Kleiner Wiesenknopf, Pimpinelle)",Burnet,burnet,3301080700 +769,"Zieste (Deutscher Ziest, Knollen-Ziest)",Hedgenettles (German Knollen),stachys_hedgenettle_chinese_artichoke,3301061235 +770,Vergissmeinnicht (Wald-Vergissmeinnicht),Forget-Me-Not (Forest Forg.),other_flowers_ornamental_plants,3301089900 +771,Portulak,Purslane,purslane,3301240000 +772,"Nelken (Bartnelke, Land-Edelnelke)",Carnations (sweet carnation/Carnations (Bard / noble carnation),carnation,3301080800 +773,Gewöhnlicher Leberbalsam (Ageratum),Ageratum (weight liver balm),other_flowers_ornamental_plants,3301089900 +774,Gelber Leberbalsam (Lonas),yellow ageratum,other_flowers_ornamental_plants,3301089900 +775,Kornblumen,Cornflowers,cornflowers,3301081000 +776,"Veilchen (Horn-Veilchen, Garten-Stiefmütterchen, Wildes Stiefmütterchen",Violets and pansies,violets_pansies,3301085100 +777,Phacelia (als Hauptkultur z.B. Saatgutvermehrung),Phacelia (as main culture e.g. seed propagation)/Phacelia,phacelia,3301061400 +778,Alpendistel,Alpine thistle,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +779,Amacrinum,Amarcrinum,other_flowers_ornamental_plants,3301089900 +780,Begonien,Begonias,begonias,3301080400 +781,Calla/Drachenwurz,calla,other_flowers_ornamental_plants,3301089900 +782,Glockenblumen (Campanula),Bluebells (campanula),bluebells,3301080500 +783,Schildblume (Chelone),Shield flower (chelone),other_flowers_ornamental_plants,3301089900 +784,"Christrose-/Schnee-/Weihnachtsrose, Korischer Nieswurz",Corsican hellebore,corsican_hellebore,3301081100 +785,Eukalyptus,eucalyptus,eucalyptus,3306050000 +786,Fingerhut,thimble,thimbles,3301084800 +787,Fuchsien,Fuchsias,fuchsias,3301081800 +788,Geranien,geraniums,other_flowers_ornamental_plants,3301089900 +789,Veronica/Hebe/Ehrenpreis,Veronica,other_flowers_ornamental_plants,3301089900 +790,"Anemonen (Herbstanemone, Japanische Anemone)",Anemones,anemones_windflowers,3301080200 +791,Knollenbegonien,Tuberous begonias,begonias,3301080400 +792,Kornrade,corncockle,other_flowers_ornamental_plants,3301089900 +793,Leimkraut/Taubenkropf-Leimkraut,Pigeon goiter / catchfly,silene_catchfly,3301084300 +794,Orchideen,orchids,other_flowers_ornamental_plants,3301089900 +795,Pelargonien,geraniums,other_flowers_ornamental_plants,3301089900 +796,"Fetthenne, Mauerpfeffer (Sedum)",Stonecrop/stonecrop,stonecrop,3301084600 +797,Rhizinus,Castor,ricinus_castor,3303080600 +798,Ramtillkraut,Ramtill herb,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299 +799,Husarenknopf (Sanvitalia),Hussar button (Sanvitalia),sanvitalia_procumbens,3301084100 +801,"Energiepflanze einer Gattung/Art, die in der aktuellen Liste nicht aufgeführt ist",Energy crop of a genus/species not included in the current list,energy_crops,3304010000 +802,"Silphium (Durchwachsene Silphie, Becherpflanze)",Silphium (streaky Silphie/Silphium (through-growth,silphium_rosinweeds,3301084400 +803,Sudangras,Sudan grass,millet_sorghum,3301010900 +804,Virginiamalve,Sida (Virginia Mallow),sida_virginia_mallow,3304030000 +805,"Staudenknöterich, Igniscum",Knotweed,igniscum_candy,3304020100 +806,Rutenhirse/Switchgras,Switchgrass / switchgrass,switchgrass,3301090208 +821,Kern- und Steinobst,Pome and stone fruit,orchards_fruits,3303010000 +822,Streuobst (Ohne Wiesennutzung),Orchards as crop cultivation (without meadow use)/Orchards (without meadow use),orchards_fruits,3303010000 +823,Birnen (Ertragsanlagen),Pears (crop plants),pears,3303011200 +824,"sonst. Obstanlagen in Vollanbau (Ohne Äpfel, Birnen, Pfirsiche)",otherwise. Fully cultivated orchards (without apples,orchards_fruits,3303010000 +825,"Kernobst z.B. Åpfel, Birnen",Pome fruit e.g. apples,orchards_fruits,3303010000 +826,"Steinobst, z. B. Kirschen, Pflaumen",Stone fruit,orchards_fruits,3303010000 +827,"Beerenobst, z.B. Johannis-, Stachel-, Himbeeren",Berries,berries_berry_species,3303020000 +828,Sanddom,Sea buckthorn,hippophae_sea_buckthorns_seaberry,3303020800 +829,"Sonstige Obstanlagen z.B. Holunder, Aronia, Maulbeeren",Other orchards e.g. elderberries/Other orchards,orchards_fruits,3303010000 +830,Pfirsiche in Vollanbau,Peaches in full cultivation,peach,3303011100 +831,Kirschen (Ertragsanlagen),Cherries (crop plants),cherry_cherries,3303010400 +832,Pflaumen (Ertragsanlagen),Plums (crop plants),plums,3303011300 +833,Haselnüsse,Hazelnuts,hazelnuts_hazel,3303030200 +834,Walnüsse,Walnuts,walnuts,3303030600 +835,sonstige Schalenfrüchte,other nuts,nuts,3303030000 +836,Äpfel in Vollanbau,Apples in full cultivation,apples,3303010200 +837,"sonst. Steinobst (Ohne Kirschen, Pflaumen)",otherwise stone fruit (without cherries,orchards_fruits,3303010000 +838,"Baumschulen, nicht für Beerenobst",Nurseries/Tree nurseries (excluding soft fruit),nurseries_nursery,3303070000 +839,Beerenobst zur Vermehrung (in Baumschulen),Berries for propagation (in tree nurseries)/berries for propagation,berries_berry_species,3303020000 +840,Korbweiden,Osier,willows_osiers,3306080000 +841,KUP lt. Direktzahlungendurchführungsverordnung,Short rotation plantations/Short rotation coppice,other_permanent_crops_plantations,3303990000 +842,Rebland,Vineyard,vineyards_wine_vine_rebland_grapes,3303060000 +843,Bestockte Rebfläche,Planted vineyards (underplanting),vineyards_wine_vine_rebland_grapes,3303060000 +844,Unbestockte Rebfläche,Unplanted vineyard (no underplanting),vineyards_wine_vine_rebland_grapes,3303060000 +845,Rebschulfläche,Vine nursery area,vineyards_wine_vine_rebland_grapes,3303060000 +846,Unterlagsrebfläche,vineyard,vineyards_wine_vine_rebland_grapes,3303060000 +847,Steillagenweinbau,Steep slope viticulture,vineyards_wine_vine_rebland_grapes,3303060000 +848,Tafeltrauben,table grapes,vineyards_wine_vine_rebland_grapes,3303060000 +849,Weinbergbrache,Vineyard fallow,vineyards_wine_vine_rebland_grapes,3303060000 +850,Sonstige Dauerkulturen,Other permanent crops,other_permanent_crops_plantations,3303990000 +851,Rhabarber,rhubarb,rhubarb,3301230000 +852,Chinaschilf/Miscanthus,Chinese reed / Miscanthus,miscanthus_silvergrass,3301083000 +853,Riesenweizengras/Szarvasi-Gras/Hirschgras,Giant wheat grass / Szarvasi grass / deer grass/Giant wheat grass / Szarvasi grass,poaceae_grasses,3301090200 +854,Rohrglanzgras,Reed grass,poaceae_grasses,3301090200 +855,"Dauerkultur einer Gattung/Art, die in der aktuellen Liste nicht aufgeführt ist",Permanent culture of a genus/species not included in the current list,other_permanent_crops_plantations,3303990000 +856,Hopfen,Hops,hops,3301060200 +857,Aromahopfen,aromatic hops,hops,3301060200 +858,Bitterhopfen,bitter hops,hops,3301060200 +859,Hopfen vorübergehend stillgelegt (Gerüst steht noch),Hops temporarily shut down (scaffolding still in place),hops,3301060200 +860,Spargel,asparagus,aspargus,3301200000 +861,Artischocke,artichoke,artichoke,3301270000 +862,Heidekraut,Heather,ericaceae_heather,3301061216 +863,"Rosen (Baumschulen), Schnittrosen",Roses (nurseries)/Roses cut roses,roses,3301083700 +864,Rhododendron,rhododendron,rhododendron,3301083600 +865,Trüffel,truffle,truffle,3304040000 +866,Pflanzenmischung mit Hanf,Plant mixture with hemp,hemp_cannabis,3301061000 +907,Höhere Gewalt (Zuweisung),Force majeure (assignment),not_known_and_other,3399000000 +910,Wildäsungsfläche,Deforestation area/Wild field on agricultural area,fallow_land_not_crop,3301110000 +911,(Beta-)Rübensamenvermehrung,(Beta) beet seed multiplication,beetroot_beets,3301290200 +912,Grassamenvermehrung,Grass seed propagation,temporary_grass,3301090100 +914,Versuchsflächen mit mehreren beihilfefähigen Kulturarten,Trial plots with several eligible crops/Trial areas (only direct payment possible),other_arable_land_crops,3301990000 +915,Ackerrandstreifen und Blühflächen,other flower strips or flower areas,flowers_ornamental_plants,3301080000 +918,Mehrjährige Blühstreifen und Blühflächen,Perennial flower strips or flower areas (only to be used for areas with AUM-BS 2),flowers_ornamental_plants,3301080000 +920,Haus- und Nutzgärten,House and kitchen gardens,kitchen_gardens,3301120000 +923,Grünland ohne landwirtschaftliche Nutzung,Grassland without agricultural use,pasture_meadow_grassland_grass,3302000000 +924,Biotope ohne landwirtschaftliche Nutzung (AUKM),Contract nature conservation without direct payment,not_known_and_other,3399000000 +925,Biotope mit landwirtschaftlicher Nutzung,Biotopes with agricultural use,other_arable_land_crops,3301990000 +927,Flächen mit LPR-Pflegevertrag,Areas with LPR maintenance contract,not_known_and_other,3399000000 +928,Gewässer- und Erosionsschutzstreifen,Water and erosion protection strips,not_known_and_other,3399000000 +930,Bewirtschaftete Gewässer/Teichflächen,Managed water bodies/pond areas,not_known_and_other,3399000000 +940,Unbewirtschaftes Gewässer,Unmanaged water,not_known_and_other,3399000000 +941,Gründüngung im Hauptfruchtanbau,Green manure in main crop cultivation,fallow_land_not_crop,3301110000 +952,Aufforstung n. d. Aufforstungsprämie '91 bis '92,Reforestation n. d. Reforestation premium '91 to '92,afforestation_reforestation,3306010000 +955,Erstaufforstung ldw. Flächen gem. VO (EG) Nr. 1257/1999 (Ödland),Initial afforestation of agricultural land according to Regulation (EC) No. 1257/1999 (wasteland),afforestation_reforestation,3306010000 +956,Aufforstung nach der Einkommensverlustprämie ab 2007,Afforestation,afforestation_reforestation,3306010000 +958,Streuwiesen (Hauptzweck Naturschutz),Litter meadows (main purpose nature conservation),pasture_meadow_grassland_grass,3302000000 +960,Dämme und Deiche,Dams and dikes,not_known_and_other,3399000000 +961,Pflege aufgegebener Flächen im Rahmen einer VNS-Maßnahme,Maintenance of abandoned areas within the framework of a SSF measure.,not_known_and_other,3399000000 +965,Unkultivierte Moorfläche,Uncultivated peatland,unmaintained,3308000000 +966,Unkultivierte Heidefläche,Uncultivated heath,unmaintained,3308000000 +972,Grünland (nicht DZ fähig),Run for livestock keeping (not DZ capable)/NFF: permanent grassland use,pasture_meadow_grassland_grass,3302000000 +980,Pilzbeet- und Gemüseflächen in Gebäuden (nicht im Gewächshaus),Mushroom beds and vegetable plots indoors (not in greenhouses).,other_mushrooms_energy_crops_genetically_modified_crops,3304990000 +981,Pilze unter Glas,Mushrooms under glass,greenhouse_foil_film,3305000000 +982,Sonstige KUP,Other short rotation plantations (rotation time over 20 years),other_permanent_crops_plantations,3303990000 +983,Weihnachtsbäume,Christmas trees,other_tree_wood_forest,3306990000 +990,Alle anderen Flächen (keine LF),All other surfaces (no LF),not_known_and_other,3399000000 +991,"Nicht landwirt. Flächen in der Verfügungsgewalt des Antragstellers, die gemäß § 15 Absatz 1 des Direktzahlungen-",Not farmer. Areas within the control of the applicant that have been designated as environmentally sensitive permanent grassland in accordance with Section 15 (1) of the Direct Payments Implementation Act,not_known_and_other,3399000000 +992,Nicht landwirt. Flächen in folge Genehmigung DGL Umwandlung,Not farmer. Areas following approval DGL conversion,not_known_and_other,3399000000 +994,"Vorübergehende, unbefestigte Mieten, Stroh-, Futter- oder Dunglagerplätze auf DGL","Temporary, unpaved windrows, straw, fodder or manure storage areas on DG",not_known_and_other,3399000000 +995,Forstflächen (Waldbodenflächen),Forest areas (forest floor areas)/Forest areas,tree_wood_forest,3306000000 +996,"Vorübergehende, unbefestigte Mieten, Stroh-, Futter oder Dunglagerplätze auf AL",Unsecured Rent/unfixed rents AL,not_known_and_other,3399000000 +998,"Abbau-, Öd-, Un-, Geringstland, Sukzessionsflächen - dauerhaft aus der Erzeugung genommen",Mining,not_known_and_other,3399000000 +999,"Ackerkultur einer Gattung/Art, die in der aktuellen Liste nicht aufgeführt ist",Arable crops of a genus / species not included in the current list/Genus / species (not in list),other_arable_land_crops,3301990000 \ No newline at end of file diff --git a/tests/data-files/convert/ec_lv/lv_2021.csv b/tests/data-files/convert/ec_lv/lv_2021.csv new file mode 100644 index 00000000..f8e870f6 --- /dev/null +++ b/tests/data-files/convert/ec_lv/lv_2021.csv @@ -0,0 +1,139 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +111,"Kvieši, vasaras",Wheat summer,spring_common_soft_wheat,3301010102,summer_common_soft_wheat,3301010103 +112,"Kvieši, ziemas",Wheat winter,winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +113,Kvieši vasaras ar stiebrzāļu vai tauriņziežu pasēju,Wheat in summer with grass or legume sowing,spring_common_soft_wheat,3301010102,summer_common_soft_wheat,3301010103 +115,"Speltas kvieši, vasaras",Spelt summer,spring_spelt,3301011002,summer_spelt,3301011003 +116,"Speltas kvieši, ziemas","Spelt wheat, winter",winter_spelt,3301011001,winter_spelt,3301011001 +121,Rudzi,Rye,rye,3301010300,rye,3301010300 +122,"Rudzi, Kaupo šķirnes",Rye Kaupo varieties,rye,3301010300,rye,3301010300 +123,"Rudzu populācijas šķirnes, izņemot ‘Kaupo’",Rye population varieties other than ,rye,3301010300,rye,3301010300 +131,"Mieži, vasaras",Barley summer,spring_barley,3301010402,summer_barley,3301010403 +132,"Mieži, ziemas",Barley winter,winter_barley,3301010401,winter_barley,3301010401 +133,Mieži vasaras ar stiebrzāļu vai tauriņziežu pasēju,Barley summer with grass or legume sowing,spring_barley,3301010402,summer_barley,3301010403 +140,Auzas,Oats,oats,3301010500,oats,3301010500 +141,Auzas ar stiebrzāļu vai tauriņziežu pasēju,Oats with grass or legume paste,oats,3301010500,oats,3301010500 +150,"Tritikāle, vasaras",Triticale summer,spring_triticale,3301010802,summer_triticale,3301010803 +151,"Tritikāle, ziemas",Triticale winter,winter_triticale,3301010801,winter_triticale,3301010801 +152,Tritikāle vasaras ar stiebrzāļu vai tauriņziežu pasēju,Summer triticale with grass or legume paste,spring_triticale,3301010802,summer_triticale,3301010803 +160,Griķi,Buckwheat,buckwheat,3301150200,buckwheat,3301150200 +161,Griķi ar tauriņziežu pasēju,Buckwheat with legume seed,buckwheat,3301150200,buckwheat,3301150200 +170,Kaņepes,Cannabis,hemp_cannabis,3301061000,hemp_cannabis,3301061000 +211,"Rapsis, vasaras",Rape summer,summer_rapeseed_rape,3301060403,summer_rapeseed_rape,3301060403 +212,"Rapsis, ziemas",Rape winter,winter_rapeseed_rape,3301060401,winter_rapeseed_rape,3301060401 +213,"Ripsis, vasaras","Rapeseed, summer",summer_rapeseed_rape,3301060403,summer_rapeseed_rape,3301060403 +214,"Ripsis, ziemas",Rapeseed winter,winter_rapeseed_rape,3301060401,winter_rapeseed_rape,3301060401 +215,Sinepe,Mustard,mustard,3301210100,mustard,3301210100 +216,Sinepes ar tauriņziežu pasēju,Mustard with legume paste,mustard,3301210100,mustard,3301210100 +310,"Lini, šķiedras",Linen fibers,flax_linen,3301060701,flax_linen,3301060701 +330,"Lini, eļļas",Flax oils,flax_linseed_oil,3301060702,flax_linseed_oil,3301060702 +410,Lauka pupas,Field beans,beans,3301020100,beans,3301020100 +420,Zirņi,Peas,peas,3301020600,peas,3301020600 +430,"Lupīna (saldā jeb dzeltenā, baltā, šaurlapu)",Lupine (sweet or yellow white narrow leaf),sweet_lupins,3301020700,sweet_lupins,3301020700 +441,"Vīķi, vasaras",Vetches summer,vetches,3301090305,vetches,3301090305 +442,"Vīķi, ziemas",Vetches winter,vetches,3301090305,vetches,3301090305 +443,Soja,Soybeans,soy_soybeans,3301160000,soy_soybeans,3301160000 +445,"Graudaugu un zirņu vai vīķu maisījums, kur proteīnaugi >50%",Mixture of cereals and peas or vetches with protein crops> 50%,legumes_dried_pulses_protein_crops,3301020000,unspecified_cereals,3301011500 +446,"Graudaugu un zirņu vai vīķu maisījums ar stiebrzāļu vai tauriņziežu pasēju, kur proteīnaugi >50%",Mixture of cereals and peas or vetches with grass or legume sowing where protein crops> 50%,legumes_dried_pulses_protein_crops,3301020000,unspecified_cereals,3301011500 +610,Papuve,Idle land,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +620,"LIZ, par kuru kārtējā gadā nevar saņemt atbalstu",UAA for which no support is available in the current year,not_known_and_other,3399000000,currants,3303020600 +640,Kokaugu stādaudzētavas lauksaimniecības zemē,Tree nurseries on agricultural land,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +641,Miežabrālis,Reed canary grass,poaceae_grasses,3301090200,canary_seed_canaryseed,3301011400 +642,Klūdziņprosa,Switch grass,switchgrass,3301090208,switchgrass,3301090208 +644,Apse,Apsen,aspen,3306020000,aspen,3306020000 +645,Kārkls,Willow,willows_osiers,3306080000,willows_osiers,3306080000 +646,Baltalksnis,White alder,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +710,Ilggadīgie zālāji,Permanent grasslands,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +713,Citur neminētas stiebrzāles,Grasses not elsewhere specified or included,poaceae_grasses,3301090200,pasture_meadow_grassland_grass,3302000000 +714,Esparsete,Esparsete,esparsette_onobrychis,3301020300,esparsette_onobrychis,3301020300 +715,Facēlija,Phacelia,phacelia,3301061400,phacelia,3301061400 +716,Facēlija ar tauriņziežu pasēju,Phacelia with legume sowing,phacelia,3301061400,phacelia,3301061400 +720,Aramzemē sētu stiebrzāļu vai lopbarības zālaugu maisījums,Mixture of grasses or fodder grasses sown on arable land,poaceae_grasses,3301090200,pasture_meadow_grassland_grass,3302000000 +723,Sarkanais āboliņš,Red clover,clover,3301090303,clover,3301090303 +724,Baltais āboliņš,White clover,clover,3301090303,clover,3301090303 +725,Bastarda āboliņš,Bastard clover,clover,3301090303,clover,3301090303 +726,Lucerna,Lucerne,alfalfa_lucerne,3301090301,alfalfa_lucerne,3301090301 +727,Austrumu galega,Eastern galega,galega,3301081900,galega,3301081900 +728,Ragainais vanagnadziņš,Birds foot trefoil,legumes_harvested_green,3301090300,lotus,3301082600 +729,Amoliņš,Sweet clover,melilot,3301090304,clover,3301090303 +731,"Pļavas timotiņš, sēklas ieguvei",Meadow timothy for seed production,timothy,3301090209,timothy,3301090209 +732,"Pļavas auzene, sēklas ieguvei",Meadow fescue for seed production,festuca_fescue,3301090202,festuca_fescue,3301090202 +733,"Hibrīdā airene, sēklas ieguvei",Hybrid ryegrass Folium boucheanum for seed production,lolium_ryegrass,3301090205,rye,3301010300 +734,Daudzziedu viengadīgā airene sēklas ieguvei,Italian ryegrass for seed production,lolium_ryegrass,3301090205,lolium_ryegrass,3301090205 +735,"Sarkanā auzene, sēklas ieguvei",Red fescue for seed production,festuca_fescue,3301090202,festuca_fescue,3301090202 +736,"Ganību airene, sēklas ieguvei",Perennial ryegrass for seed production,lolium_ryegrass,3301090205,lolium_ryegrass,3301090205 +737,"Niedru auzene, sēklas ieguvei",Tall fescue for seed production,festuca_fescue,3301090202,festuca_fescue,3301090202 +738,"Pļavas skarene, sēklas ieguvei",Kentucky bluegrass for seed production,poaceae_grasses,3301090200,festuca_fescue,3301090202 +739,"Kamolzāle, sēklas ieguvei",Cocksfoot for seed production,cocksfoot_catgrass,3301090203,chamomile,3301061213 +741,Citur neminēta kukurūza,Maize not elsewhere specified,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +760,"Aramzemē sētu stiebrzāļu vai tauriņziežu maisījums, kur tauriņzieži >50%",Mixture of grasses or legumes sown on arable land with legumes> 50%,plants_harvested_green,3301090000,pasture_meadow_grassland_grass,3302000000 +761,Auzeņairene sēklas ieguvei,Festulolium for seed production,festulolium,3301090204,festuca_fescue,3301090202 +791,Kukurūza biogāzes ieguvei,Maize for biogas production,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +792,"Platība, kurā dabiski iesējušos augu īpatsvars pārsniedz 40%, dārzeņu kultūraugu skaits vienā kvadrātmetrā ir mazāks par šo noteikumu 2.2 pielikumā noteikto skaitu un nav īstenoti nezāļu ierobežošanas agrotehniskie pasākumi vismaz t",Area where the proportion of naturally sown plants exceeds 40% the number of vegetable crops per square meter is less than the number specified in Annex 2.2 to these Regulations and no weed control agronomic measures have been implemented for at least t,not_known_and_other,3399000000,not_known_and_other,3399000000 +811,"Dažādi kultūraugi nelielā aramzemes platībā vai vairāki kultūraugi, audzēti vienlaidu laukā, ja katrs no kultūraugiem attiecīgajā laukā aizņem mazāk par 0,3 ha, vai platības, ko izmanto ziedu audzēšanai","Different crops on a small area of arable land or several crops grown in a continuous field, where each of the crops occupies less than 0.3 ha in the field concerned, or areas used for the production of flowers",arable_crops,3301000000,other_arable_land_crops,3301990000 +820,"Kartupeļi, kas citur nav minēti",Potatoes not elsewhere specified or included,potatoes,3301030000,potatoes,3301030000 +821,Sēklas kartupeļi,Seed potatoes,potatoes,3301030000,potatoes,3301030000 +825,Cietes kartupeļi,Starch potatoes,potatoes,3301030000,potatoes,3301030000 +826,Tomāti,Tomatoes,tomato,3301280000,tomato,3301280000 +831,"Lopbarības bietes, cukurbietes",Fodder beet sugar beet,mangelwurzel_fodder_beet,3301290400,mangelwurzel_fodder_beet,3301290400 +842,Ziedkāposti,Cauliflower,cauliflower,3301210204,cauliflower,3301210204 +843,Burkāni,Carrots,carrots_daucus,3301290300,carrots_daucus,3301290300 +844,"Galda bietes, mangolds (lapu bietes)",Table beets chard (leaf beets),beetroot_beets,3301290200,beetroot_beets,3301290200 +845,Gurķi un kornišoni,Cucumbers and gherkins,cucumber_pickle,3301140100,cucumber_pickle,3301140100 +846,"Sīpoli, šalotes sīpoli, maurloki, lielloku sīpoli un batūni",Onions shallots chives shallots and trampolines,alliums,3301220000,alliums,3301220000 +847,Ķiploki,Garlic,garlic,3301220200,garlic,3301220200 +848,"Garšaugi un kultivēti ārstniecības augi (fenhelis, baziliks, timiāns, estragons, anīss, majorāns, oregano, salvija, izops, piparmētra, pupumētra, vērmele, lofants, naktssvece, deviņvīru spēks, ābolmētra, citronmelisa, tauksakne, ehinācija",Herbs and cultivated medicinal plants (fennel basil thyme tarragon anise marjoram oregano sage hyssop mint peppermint wormwood lofant night candle nine-man force apple mint lemon balm fatty root echinacea,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +849,Puravi,leek,leek,3301220300,leek,3301220300 +851,"Galda rāceņi, turnepši",Turnips,turnips,3301290800,turnips,3301290800 +852,Selerijas,Celery,celery,3301250000,celery,3301250000 +853,Redīsi un melnie rutki,Radish and black radish,radish,3301290600,radish,3301290600 +854,Pētersīļi,Parsley,parsly,3301061227,parsly,3301061227 +855,Pastinaks,Parsnips,parsnips,3301290500,parsnips,3301290500 +856,Galda kāļi,rutabaga,swede_rutabaga,3301210500,swede_rutabaga,3301210500 +857,"Dārza ķirbji, cukīni, kabači, patisoni",Garden pumpkins zucchini courgettes squash,cucurbits,3301140000,cucurbits,3301140000 +858,"Vīģlapu, lielaugļu, muskata ķirbji","Fig-leaf, large-fruit, and nutmeg pumpkins",pumpkin_squash_gourd,3301140400,fig,3303010600 +859,Parastās jeb dārza pupiņas,Ordinary or garden beans,beans,3301020100,beans,3301020100 +860,Skābenes,Sorrel,sorrel,3301310700,sorrel,3301310700 +861,Rabarberi,Rhubarb,rhubarb,3301230000,rhubarb,3301230000 +862,Spināti,Spinach,spinach,3301310800,spinach,3301310800 +863,Mārrutki,Horseradish,horseradish,3301210400,horseradish,3301210400 +864,Salāti,Salads,salads_lettuce_leaf_vegetables,3301310000,salads_lettuce_leaf_vegetables,3301310000 +865,Topinambūri,Jerusalem artichokes,topinambur_jerusalem_artichoke,3301180000,topinambur_jerusalem_artichoke,3301180000 +867,Paprika,Paprika,bell_pepper_paprika,3301300100,bell_pepper_paprika,3301300100 +869,Sparģeļi,Asparagus,asparagus,3301200000,aspargus,3301200000 +870,"Citur neminēti kāposti (baltie vai sarkanie galviņkāposti, rožu jeb Briseles kāposti, galda kolrābji, sparģeļkāposti, virziņkāposti jeb Savojas kāposti, lapu kāposti, brokoļi, Pekinas kāposti), izņemot lopbarības kāpostus",Cabbages not elsewhere specified or included (white or red headed cabbages Brussels sprouts kohlrabi kale savoy cabbages kale broccoli Beijing cabbages) other than fodder cabbages,brassica_oleracea_cabbage,3301210200,brassica_oleracea_cabbage,3301210200 +871,"Dārzeņi, ja vienlaidu platībā augošas BSA atbalsttiesīgās dārzeņu kultūraugu sugas katra aizņem mazāk par 0,3 ha un kopējā saimniecības aramzemes platība nav lielāka par 10 ha",Vegetables if the BSA eligible vegetable crop species growing in a continuous area each occupy less than 0.3 ha and the total arable land area of ??the holding does not exceed 10 ha,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +872,"Pārējie kultūraugi, sēti tīrsējā aramzemē",Other crops sown on pure arable land,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +873,"Pārējie citur neminētie kultūraugi, sēti kā kultūraugu maisījums aramzemē",Other crops not elsewhere specified sown as a mixture of crops on arable land,arable_crops,3301000000,other_arable_land_crops,3301990000 +874,Parastās dilles,Common dill,anethum_dill,3301061203,anethum_dill,3301061203 +877,Sējas koriandrs jeb kinza,Sowing coriander or kinza,coriander,3301061215,coriander,3301061215 +878,Ķimene,Cumin,caraway,3301061211,caraway,3301061211 +879,Mārdadzis,Marian Thistles,marian_thistles,3301061300,marian_thistles,3301061300 +881,Kumelīte,Chamomile,chamomile,3301061213,chamomile,3301061213 +882,Kliņģerīte,Marigold,calendula_marigold,3301061210,calendula_marigold,3301061210 +883,Cigoriņš,Chicory,chicory_chicories,3301310200,chicory_chicories,3301310200 +884,Ārstniecības gurķene,Borage,borage,3301061209,borage,3301061209 +885,Lavanda,Lavender,lavender_lavandula,3301061219,lavender_lavandula,3301061219 +911,Ābeles,Apples,apples,3303010200,apples,3303010200 +912,Bumbieres,Pears,pears,3303011200,pears,3303011200 +914,Plūmes,Plums,plums,3303011300,plums,3303011300 +915,Plūškoks,Elderberry,elder_elderberry,3303080400,elder_elderberry,3303080400 +918,Aronijas,Chokeberry,aronia_chokeberries,3303020100,aronia_chokeberries,3303020100 +919,Smiltsērkšķis,Sea buckthorn,hippophae_sea_buckthorns_seaberry,3303020800,hippophae_sea_buckthorns_seaberry,3303020800 +921,Avenes,Raspberries,raspberry_raspberries,3303021000,raspberry_raspberries,3303021000 +922,Upenes,Blackcurrant,blackcurrant_cassis,3303020300,blackcurrant_cassis,3303020300 +924,Krūmmellenes (zilenes),Blueberries (blueberries),blueberry,3303020400,blueberry,3303020400 +926,Zemenes,Strawberry,strawberries,3301130000,strawberries,3301130000 +927,Ērkšķogas,Gooseberries,gooseberry_gooseberries_cranberries,3303020700,gooseberry_gooseberries_cranberries,3303020700 +928,Krūmcidonijas,Quince,chaenomeles_cathayensis,3303080200,quinces,3303011500 +929,Kazenes,Blackberries,blackberry,3303020200,blackberry,3303020200 +930,"Citi kultivēti nektāraugi (ežziede, biškrēsliņš, pūķgalve, melisa, daglītis, dedestiņa, kaķumētra, rudzupuķe)",Other cultivated nectar plants (hedgehog periwinkle dragon's head lemon balm buttercup blackthorn catnip cornflower),aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +931,Dārza pīlādži,Garden rowans or garden mountain-ashes,rowan_rowanberries,3303021300,rowan_rowanberries,3303021300 +932,Saldie un skābie ķirši,Sweet and sour cherries,cherry_cherries,3303010400,cherry_cherries,3303010400 +933,Sarkanās un baltās jāņogas,Red and white currants,currants,3303020600,currants,3303020600 +934,Lielogu dzērvenes,Large cranberries,cranberry,3303020500,cranberry,3303020500 +935,Vīnogas,Grapes,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +937,Arbūzi un melones,Watermelons and melons,cucurbits,3301140000,melon,3301140300 +938,Sausserdis,Honeysuckle,honeysuckle,3303080500,honeysuckle,3303080500 +939,Irbene,Guilder rose,shrubberries_shrubs,3303080000,not_known_and_other,3399000000 +950,"Augļu koki un ogulāji (izņemot zemenes), ja vienlaidus platībā augošas BSA sugas katra <0,3 ha",Fruit trees and berry bushes (excluding strawberries) if BSA species growing in a continuous area each <0.3 ha,orchards_fruits,3303010000,berries_berry_species,3303020000 +952,Citur neminēti ilggadīgie stādījumi,Perennial crops not mentioned elsewhere,permanent_crops_perennial,3303000000,permanent_crops_perennial,3303000000 \ No newline at end of file diff --git a/tests/data-files/convert/ec_si/si_2021.csv b/tests/data-files/convert/ec_si/si_2021.csv new file mode 100644 index 00000000..42cb17b5 --- /dev/null +++ b/tests/data-files/convert/ec_si/si_2021.csv @@ -0,0 +1,158 @@ +original_code,original_name,latin_name,translated_name,HCAT3_name,HCAT3_code +000,ni v uporabi,Not known,Not known,not_known_and_other,3399000000 +001,p?enica (jara),Triticum aestivum L.,Common wheat spring,spring_common_soft_wheat,3301010102 +002,r? (jara),Secale cereale L.,Rye (spring),spring_rye,3301010302 +003,pira (jara),Triticum spelta L.,Spring Spelt,spring_spelt,3301011002 +004,ajda,Fagopyrum esculentum Moench,Buckwheat,buckwheat,3301150200 +005,koruza za zrnje,Zea mays L.,Grain maize,grain_maize_corn_popcorn,3301010600 +006,koruza za sila?o,Zea mays L.,Silo Maize,green_silo_maize,3301090400 +007,tritikala (jara),X Triticosecale Wittmack (Triticum x Secale),Spring Triticale,spring_triticale,3301010802 +008,oves (jari),Avena sativa L.,Oats (spring),spring_oats,3301010502 +009,je?men (jari),"Hordeum vulgare L., spring barley",spring barley,spring_barley,3301010402 +010,proso,Panicum miliaceum L.,Proso millet,millet_sorghum,3301010900 +011,me?anice ?it (jara),Mixture of cereals (spring),Mixture of cereals (spring),spring_unspecified_cereals,3301011502 +012,son?nice,Helianthus annus L.,Common Sunflower,sunflower,3301060500 +013,oljna bu?a,Cucurbita pepo var.pepo,Winter squash pumkin,pumpkin_squash_gourd,3301140400 +014,oljna ogr??ica (jara),Brassica napus var.napus,Rapeseed spring,spring_rapeseed_rape,3301060402 +017,krmni bob,Vicia faba L. var. minor Harz,Field bean,beans,3301020100 +019,sladkorna pesa,Beta vulgaris L. subsp. vulgaris var. Altissima,Sugar beet,sugar_beet,3301290700 +020,krompir (pozni),Solanum tuberosum L.,potatoes (late),potatoes,3301030000 +022,krompir (zgodnji),Solanum tuberosum L.,potatoes (early),potatoes,3301030000 +024,sirek,Sorghum bicolor (L.) Moench,sorghum,millet_sorghum,3301010900 +025,trda p?enica (jara),Triticum durum Desf.,Durum wheat spring,spring_durum_hard_wheat,3301010202 +026,praha,Fallow land,Fallow land,fallow_land_not_crop,3301110000 +027,konoplja,Canabis sativa var.sativa,Cannabis,hemp_cannabis,3301061000 +028,lan,Linum usitatissimum L.,Flax,flax_linen,3301060701 +029,ukoreni??e hmeljnih sadik,The rooting of hop seedlings,The rooting of hop seedlings,hops,3301060200 +030,soja,Glycine max (L.) Merr.,Soybean,soy_soybeans,3301160000 +031,vrtni mak (jari),Papaver somniferum L. subsp. somniferum,Spring Poppy,summer_poppy,3301060602 +033,krmni grah (jari),Pisum sativum L.,Pea spring,peas,3301020600 +035,p?enica horasan (jara),Triticum turanicum Jakubz.,Khorasan wheat spring,other_cereals,3301019900 +036,ri?ek,Camelina sativa L. Crantz,Camelina,camelina,3301061500 +037,amarant,Amaranthus caudatus L.,Pendant amaranth,amaranth,3301150100 +038,repa,Brassica rapa L. var. rapa (L.) Thell.,Turnip,turnips,3301290800 +049,sladka koruza,Zea mays L. convar. saccharata Koerm.,Sweet corn,grain_maize_corn_popcorn,3301010600 +052,me?anica medonosnih rastlin,A mixture of honey plants,A mixture of honey plants,fallow_land_not_crop,3301110000 +053,me?anica medonosnih rastlin z drugimi kmetijskimi rastlinami,A mixture of honey plants with other agricultural plants,A mixture of honey plants with other agricultural plants,fallow_land_not_crop,3301110000 +054,bela gorju?ica - medonosna praha,Sinapis alba L.,White mustard ,mustard,3301210100 +055,oljna redkev - medonosna praha,Raphanus sativus L. var. oleiformis Pers.,Oilseed radish,radish,3301290600 +056,facelija - medonosna praha,Phacelia tanacetifolia,Phacelia,phacelia,3301061400 +057,ajda - medonosna praha,Fagopyrum esculentum Moench,Buckwheat,buckwheat,3301150200 +058,son?nice - medonosna praha,Helianthus annus L.,Sunflower,sunflower,3301060500 +100,vinska trta,Vitis vinifera L.,Common grape vine,vineyards_wine_vine_rebland_grapes,3303060000 +101,krmna pesa,Beta vulgaris spp.vulgaris,Beet fodder,mangelwurzel_fodder_beet,3301290400 +102,krmna repa,Brassica rapa L. var. rapa (L.) Thell,Turnip,turnips,3301290800 +103,oljna repica,Brassica rapa L. subsp. campestris,Oilseed rape,rapeseed_rape,3301060400 +104,krmna repica (jara),Brassica rapa L. ssp. sylvestris f. autumnalis,Oilseed rape spring,spring_rapeseed_rape,3301060402 +105,krmni ohrovt,"Brassica oleracea L., convar.: acephala var. medullosa Thell.",Fodder cabbage,other_brassica_oleracea_cabbage,3301210299 +106,krmni radi?,Cichorium intybus L. var. sativum DC. Bischoff,Common Chicory,chicory_chicories,3301310200 +107,krmno korenje,Daucus carota,Daucus,carrots_daucus,3301290300 +108,podzemna koleraba,Brassica napus L. var. napobrassica (L.) Rchb.,Swede,swede_rutabaga,3301210500 +109,krmni sirek,Sorghum bicolor (L.) Moench.,Sorghum,millet_sorghum,3301010900 +110,gra?ica (jara),Vicia sativa L.,Common peas spring,peas,3301020600 +111,bela gorju?ica,Sinapis alba L.,White mustard,mustard,3301210100 +112,krmna ogr??ica (jara),Brassica napus L. var. napus f. biennis,Spring oilseed rape,spring_rapeseed_rape,3301060402 +113,oljna redkev,Raphanus sativus L. var. oleiformis Pers.,Fodder Radish,radish,3301290600 +114,druge rastline za krmo na njivah,Other fodder crops on arable land,Other fodder crops on arable land,other_arable_land_crops,3301990000 +116,sudanska trava,Sorghum sudannense P.,Sudan grass,millet_sorghum,3301010900 +129,"druge me?anice z rastlinami, ki ve?ejo du?ik",Other mixtures with nitrogen-fixing crops,Other mixtures with nitrogen-fixing crops,not_known_and_other,3399000000 +200,trave za pridelavo semena,Grass for seed production,Grass for seed production,temporary_grass,3301090100 +201,trave,Grass on arable land,Grass on arable land,temporary_grass,3301090100 +202,travna ru?a (travni tepih),Green cover (grass turf),Green cover (grass turf),sod_turf,3301090207 +203,travnodeteljne me?anice,Grass clover mixture,Grass clover mixture,temporary_grass,3301090100 +204,trajno travinje,Permanent grassland,Permanent grassland,pasture_meadow_grassland_grass,3302000000 +206,deteljnotravne me?anice,Clover grass mixture,Clover grass mixture,clover,3301090303 +207,detelja,Trifolium pratense L.,Red clover,clover,3301090303 +208,lucerna,Medicago sativa L.,Alfalfa,alfalfa_lucerne,3301090301 +210,vol?ji bob,Lupinus albus L.,White lupin,sweet_lupins,3301020700 +219,facelija,Phacelia tanacetifolia,Lacy phacelia,phacelia,3301061400 +221,perzijska detelja,Trifolium resupinatum L.,Reversed clover,clover,3301090303 +222,inkarnatka,Trifolium incarnatum L.,Crimson clover,clover,3301090303 +333,tehni?no ali drugo sredstvo,Not mantained because of technical obstacle,Not mantained because of technical obstacle,unmaintained,3308000000 +402,zelenjadnice,Mixed vegetables,Mixed vegetables,fresh_vegetables,3301070000 +403,razli?na trajna zeli??a,Mixed permanent herbs,Mixed permanent herbs,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +404,enoletna in dvoletna njivska zeli??a,One-year and two-year herbs on fields,One-year herbs on fields,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +405,"me?ana raba (zelenjadnice, polj??ine, di?avnice in zdravilna zeli??a)","Mixed use (vegetables, crops, aromatic plants and medicinal herbs)","Mixed use (vegetables, crops, aromatic plants and medicinal herbs)",arable_crops,3301000000 +409,"me?ane zelenjadnice pod 0,1 ha","Mixed vegetables on field under 0,1 ha size",Mixed vegetables on field under 0,fresh_vegetables,3301070000 +444,pridelava ni v tleh,Production not in the soil,Production not in the soil,not_known_and_other,3399000000 +611,jablana,Malus domestica Borkh.,Apple,apples,3303010200 +612,hru?ka,Pyrus communis L.,European pear,pears,3303011200 +613,kutina,Cydonia oblonga Mill.,Quince,quinces,3303011500 +614,nashi,Pyrus pyrifolia (Burm.f.) Nakai,Asian pear,pears,3303011200 +615,granatno jabolko,Punica granatum L.,Pomegranate,pomegranate,3303011400 +616,ne?plja,Mespilus germanica L.,Common medlar,medlar_loquat,3303010800 +618,?i?ula,Ziziphus sativa Gaert.,Jujube red date,orchards_fruits,3303010000 +619,feioja,Feijoa sellowiana,Feijoa,feijoa,3303010500 +621,breskev,Prunus persica BATSCH,Peach,peach,3303011100 +622,nektarina,Prunus persica (L.) Batsch. var. nucipersica (Suckow) Schneid.,Nectarine,nectarine,3303010900 +623,sliva/?e?plja,Prunus domestica L.,Common plum,plums,3303011300 +624,marelica,Prunus armeniaca L.,Armenian plum,plums,3303011300 +625,?e?nja,Prunus avium L.,Sweet cherry,cherry_cherries,3303010400 +626,vi?nja,Prunus cerasus L. (Cerasus vulgaris Mill.),Sour cherry,cherry_cherries,3303010400 +631,oreh,Juglans regia,English walnut,walnuts,3303030600 +632,leska,Corylus avellana,Common hazel,hazelnuts_hazel,3303030200 +633,mandelj,Prunus dulcis (Mill.) D. A. Webb,Almond,almond,3303030100 +634,pekan oreh,Carya illinoensis,Pecan,pecan,3303030300 +642,kivi,Actinidia chinensis Planch. (Actinidia deliciosa),Golden kiwifruit,kiwi,3303010700 +643,kaki,Diospyros kaki L.,Japanese Persimmon,orchards_fruits,3303010000 +644,kostanj,Castanea sativa,Chestnut,sweet_chestnuts,3303030500 +646,bezeg,Sambucus L.,Elderberry,elder_elderberry,3303080400 +647,smokva (figa),Ficus carica L.,Fig,fig,3303010600 +648,asimina,Asimina triloba,Pawpaw,pawpaw,3303011000 +649,rakitovec,Hippophae rhamnoides L.,Seaberry,hippophae_sea_buckthorns_seaberry,3303020800 +651,jagoda,Fragaria L.,Strawberries,strawberries,3301130000 +652,ameri?ka borovnica,Vaccinium corymbosum L.,American blueberry,blueberry,3303020400 +653,malina,Rubus idaeus L.,Red raspberry,raspberry_raspberries,3303021000 +654,robida,Rubus fruticosus L.,Blackberry,blackberry,3303020200 +655,rde?i ribez,Ribes rubrum L.,Redcurrant,redcurrant,3303021100 +656,?rni ribez,Ribes nigrum L.,Blackcurrant,blackcurrant_cassis,3303020300 +657,aronija,Aronia melanocarpa,Black chokeberry,aronia_chokeberries,3303020100 +658,murva,Morus sp.,White mulberry,berries_berry_species,3303020000 +659,goji jagoda,Lycium barbarum L.,Goji berry,shrubberries_shrubs,3303080000 +660,?rni ribez x kosmulja,Ribes nidigrolaria,Jostaberry,jostaberry,3303020900 +661,namizno grozdje,Vitis vinifera,Common grape vine,vineyards_wine_vine_rebland_grapes,3303060000 +662,robida x malina,Rubus fruticosus x Rubus idaeus,Tayberry,tayberry,3303021400 +671,limonovec,Citrus limon (L.) Burm. f. lemon,Lemon,citrus_plantations,3303040000 +674,mandarinovec,Citrus reticulata Blanco,Mandarin orange,citrus_plantations,3303040000 +675,dren,Cornus mas L.,Cornelian cherry,dogwood_cornus,3306040000 +676,kosmulja,Ribes uva-crispa L.,European gooseberry,gooseberry_gooseberries_cranberries,3303020700 +677,skor?,Sorbus domestica L.,Sorb tree,other_tree_wood_forest,3306990000 +678,u?itno modro kosteni?je,Lonicera caerulea var. Kamtschatica,Kamchatka honeysuckle,honeysuckle,3303080500 +680,?ipek,Rosa canina L.,Rosa canina,roses,3301083700 +682,?marna hru?ica,Amelanchier spp.,Serviceberry,amelanchier_serviceberry,3303010100 +698,oreh in kostanj,Castanea sativa,Sweet chestnut,sweet_chestnuts,3303030500 +699,me?ane sadne vrste,Mixed fruit plants,Mixed fruit plants,orchards_fruits,3303010000 +702,drevesnice,Nurseries,Nurseries,nurseries_nursery,3303070000 +703,?parglji,Asparagus officinalis,Garden asparagus,asparagus,3301200000 +704,trsnice,Vine nurseries,Vine nurseries,vineyards_wine_vine_rebland_grapes,3303070000 +705,"me?ane trajne rastline pod 0,1 ha","Mixed permanent crops on field under 0,1 ha size",Mixed permanent crops on field under 01 ha size,permanent_crops_perennial,3303000000 +706,"trta za drugo rabo, ki ni vino ali namizno grozdje",Vines for other uses than for wine or fruits,Vines for other uses than for wine or fruits,vineyards_wine_vine_rebland_grapes,3303060000 +707,mati?njak,Root-stock nursery,Root-stock nursery,nurseries_nursery,3303070000 +710,me?ane rastline za rejo pol?ev,Mixed plants for snail farming,Mixed plants for snail farming,other_arable_land_crops,3301990000 +720,"hitro rasto?i panjevec (vrba, topol)","Short rotation coppice (willow tree, poplar)","Short rotation coppice (willow tree, poplar)",tree_wood_forest,3306000000 +721,drugi hitro rasto?i panjevci,Other short rotation coppices,Other short rotation coppices,tree_wood_forest,3306000000 +722,miskant,Miscanthus,Silvergrass,miscanthus_silvergrass,3301083000 +733,arti?oka,Cynara cardunculus var. Scolymus L.,Globe artichocke,artichoke,3301270000 +734,rabarbara,Rheum rhabarbarum L.,Garden rhubarb,rhubarb,3301230000 +735,okrasne rastline,Ornamental plants,Ornamental plants,flowers_ornamental_plants,3301080000 +736,vrtnice,Roses,Roses,roses,3301083700 +737,sivka,Lavandula spica L.,Lavandula,lavender_lavandula,3301061219 +738,ameri?ki slamnik,Echinacea purpurea (L.) Moench Echinacea angustifolia,Purple coneflower,echinacea_sun_hat,3301081500 +777,povr?ina v odstopu,Crop not known,Crop not known,not_known_and_other,3399000000 +800,oljka,Olea europaea L. v. europaea,Olive,olive_plantations,3303050000 +801,p?enica (ozimna),Triticum L.,Common wheat winter,winter_common_soft_wheat,3301010101 +802,r? (ozimna),Secale cereale L.,Rye winter,winter_rye,3301010301 +803,pira (ozimna),Triticum spelta L.,Spelt winter,winter_spelt,3301011001 +804,krmna repica (ozimna),Brassica rapa L. var. silvestris (Lam.) Briggs,Turnip rape winter,winter_rapeseed_rape,3301060401 +807,tritikala (ozimna),X Triticosecale Wittmack (Triticum x Secale),Triticale winter,winter_triticale,3301010801 +808,oves (ozimni),Avena sativa L.,Winter oats,winter_oats,3301010501 +809,je?men (ozimni),Hordeum L.,Barley winter,winter_barley,3301010401 +811,me?anice ?it (ozimna),Mixture of cereals (winter),Mixture of cereals (winter),winter_unspecified_cereals,3301011501 +812,krmna ogr??ica (ozimna),Brassica napus L. var. napus f. biennis,Fodder rape (winter),winter_rapeseed_rape,3301060401 +814,oljna ogr??ica (ozimna),Brassica napus L. ssp. oleifera (Metzg.) Sinsk.,Winter rape,winter_rapeseed_rape,3301060401 +821,sor?ica (ozimna),Meslin (winter),Meslin (winter),winter_meslin,3301011101 +825,trda p?enica (ozimna),Triticum durum Desf.,Durum winter,winter_durum_hard_wheat,3301010201 +831,vrtni mak (ozimni),Papaver somniferum L. subsp. somniferum,Opium poppy winter,winter_poppy,3301060601 +833,krmni grah (ozimni),Pisum sativum L. (partim),Peas winter,peas,3301020600 +835,p?enica horasan (ozimna),Triticum turanicum Jakubz.,Khorasan wheat (winter),other_cereals,3301019900 +900,hmelj,Humulus lupulus L.,Common hop,hops,3301060200 \ No newline at end of file diff --git a/tests/data-files/convert/fi/fi_2023.csv b/tests/data-files/convert/fi/fi_2023.csv new file mode 100644 index 00000000..228534c5 --- /dev/null +++ b/tests/data-files/convert/fi/fi_2023.csv @@ -0,0 +1,243 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code +1110,Syysvehnä,Winter wheat,winter_common_soft_wheat,3301010101 +1120,Kevätvehnä,Spring wheat,spring_common_soft_wheat,3301010102 +1130,Durumvehnä,Durum wheat,unspecified_season_durum_hard_wheat,3301010299 +1141,Syysspelttivehnä,Autumn Spelt,winter_spelt,3301011001 +1142,Kevätspelttivehnä,Spring Spelt,spring_spelt,3301011002 +1210,Syysruisvehnä,Winter Triticale,winter_triticale,3301010801 +1211,Kevätruisvehnä,Spring Triticale,spring_triticale,3301010802 +1220,Kevätruis,Spring rye,spring_rye,3301010302 +1230,Syysruis,Autumn rye,winter_rye,3301010301 +1310,Rehuohra,Feed Barley,barley,3301010400 +1320,Mallasohra,Malt (barley),barley,3301010400 +1330,Syysohra,Winter barley,winter_barley,3301010401 +1400,Kaura,Oats,oats,3301010500 +1410,Syyskaura,Autumn oats,winter_oats,3301010501 +1545,Seoskasvusto (viljat),Mixed crops (cereals),cereal,3301010000 +1555,Seoskasvusto (vilja+öljykasvit),Mixed crops (cereals+oil crops),cereal,3301010000 +1601,Vihantavilja (ohra),Green fodder crops (barley),barley,3301010400 +1602,Vihantavilja (kaura),Green fodder grain (oats),oats,3301010500 +1603,Vihantavilja (vehnä),Green fodder grain (wheat),common_soft_wheat,3301010100 +1604,Vihantavilja (ruis),Green fodder grain (Rye),rye,3301010300 +1605,Vihantavilja (viljaseos),Green fodder grain (mixed cereals),cereal,3301010000 +1700,Tattari,Buckwheat,buckwheat,3301150200 +1710,Hirssi,Millet,millet_sorghum,3301010900 +1750,Kvinoa (kinua),Quinoa (quinoa),quinoa,3301150300 +1800,Maissi,Corn,grain_maize_corn_popcorn,3301010600 +1810,Sokerimaissi,Sweet corn,grain_maize_corn_popcorn,3301010600 +2110,Ruokaherne,Field Pea,peas,3301020600 +2120,Rehuherne,Fodder pea,peas,3301020600 +2170,Seoskasvusto (valkuaiskasvit+öljykasvit),Mixed crops (protein crops+oil crops),legumes_dried_pulses_protein_crops,3301020000 +2175,Seos (herne/härkäpapu/makea lupiini/öljykasvit),Mixture (Pea/Farm Bean/Sweet Lupine/Oil Plants),legumes_dried_pulses_protein_crops,3301020000 +2180,Seos herne/härkäpapu/makea lupiini yli 50 %+viljaa,Mixture of pea/broad bean/sweet lupine over 50%+cereal,legumes_dried_pulses_protein_crops,3301020000 +2185,Seoskasvusto (valkuaiskasvit+vilja),Mixed crops (protein crops + cereals),legumes_dried_pulses_protein_crops,3301020000 +2195,Seoskasvusto (valkuaiskasvit),Mixed crops (protein crops),legumes_dried_pulses_protein_crops,3301020000 +2196,Seoskasvusto (typensitojakasvia yli 50 %),Mixed crops (nitrogen-fixing plants over 50%),legumes_harvested_green,3301090300 +2197,Seoskasvusto (apila yli 50 % + nurmiheinä),Mixed crops (over 50% clover + forage grass),clover,3301090303 +2200,Härkäpapu,Broad bean,beans,3301020100 +2300,Soijapapu,Soybean,soy_soybeans,3301160000 +2400,Virna,Vetch,vetches,3301090305 +2410,Mesikkä,sweet yellow clover,melilot,3301090304 +2420,Mailanen,Lucerne,alfalfa_lucerne,3301090301 +2430,Apila,Clover,clover,3301090303 +2440,Vuohenherne,galega,galega,3301081900 +2450,Linssi,Lentil,lentils,3301020500 +2500,Makealupiini,Sweet lupin,sweet_lupins,3301020700 +2510,Muut lupiinit,Other lupins,sweet_lupins,3301020700 +3110,Ruokaperuna,Potatoes,potatoes,3301030000 +3120,Ruokateollisuusperuna,Industrial potatoes,potatoes,3301030000 +3130,Tärkkelysperuna,Starch potato,potatoes,3301030000 +3150,Varhaisperuna (katteenalainen),Early potato (under cover),potatoes,3301030000 +3160,Siemenperuna (sertifioidun siemenen tuotantoon),Seed potatoes,potatoes,3301030000 +3190,Tärkkelysperunan oma siemenlisäys,Seed potatoes ,potatoes,3301030000 +3210,"Sokerijuurikas, sokerintuotantoon",Sugar beet for sugar production,sugar_beet,3301290700 +3230,"Sokerijuurikas, energiantuotantoon","Sugar beet, for energy production",sugar_beet,3301290700 +3330,Rehujuurikasvit,Fodder root vegetables,fodder_roots,3301050000 +3340,Rehukaali,Fodder cabbage,other_brassica_oleracea_cabbage,3301210299 +4020,Kuitunokkonen,Stinging nettle,nettles,3301061225 +4030,"Ruistankio (Camelina, Kitupellava)",Camelina,camelina,3301061500 +4110,Kevätrypsi,Spring rape,spring_rapeseed_rape,3301060402 +4120,Syysrypsi,Winter Rape,winter_rapeseed_rape,3301060401 +4210,Kevätrapsi,Spring rape,spring_rapeseed_rape,3301060402 +4220,Syysrapsi,Winter Rape,winter_rapeseed_rape,3301060401 +4300,Auringonkukka,Sunflower,sunflower,3301060500 +4390,Seoskasvusto (öljykasvit),Mixed cropping (oil crops),oilseed_crops,3301060800 +4400,Humala,Hops,hops,3301060200 +4525,Kuituhamppu,Hemp,hemp_cannabis,3301061000 +4530,Öljyhamppu,Oil hemp,hemp_cannabis,3301061000 +4610,Öljypellava,Oil flax,flax_linseed_oil,3301060702 +4620,Kuitupellava,Flax,flax_linseed,3301060700 +4810,"Siemenmausteet ja lääkekasvit (pl kumina, sinappi)",Seed spices and medicinal plants (not caraway/mustard),aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +4904,Ruokohelpi (kuivike/rehu),reed canary grass (feed),poaceae_grasses,3301090200 +4905,Ruokohelpi (energia),reed canary grass (energy),poaceae_grasses,3301090200 +4913,"Energiapuu, lyhytkiertoinen (haapa ja paju)","Energy wood, short rotation (aspen and willow)",tree_wood_forest,3306000000 +4914,"Energiapuu, lyhytkiert. (hybriidihaapa ja poppeli)","Energy trees, short cycle time (hybrid aspen and poplar)",tree_wood_forest,3306000000 +4930,"Ahdekaunokki, energiantuotantoon",Brown knapweed for energy production,energy_crops,3304010000 +5101,Tarhaherne,Garden pea,peas,3301020600 +5102,Pensaspapu,Bush bean,beans,3301020100 +5103,Valko- eli keräkaali,White cabbage/head cabbage,white_cabbage,3301210212 +5104,Kiinankaali,Chinese cabbage,chinese_cabbage,3301210205 +5105,Kukkakaali,Cauliflower,cauliflower,3301210204 +5106,Porkkana,Carrot,carrots_daucus,3301290300 +5108,Lanttu,Rutabaga Swede,swede_rutabaga,3301210500 +5109,Nauris,Turnip,turnips,3301290800 +5110,Mukulaselleri,Celeriac,celeriac,3301250100 +5111,Palsternakka,Parsnip,parsnips,3301290500 +5113,Sipulin pikkuistukkaat,Onion seedlings,onions,3301220400 +5114,Purjo,Leeks,leek,3301220300 +5115,Avomaankurkku,Open field cucumber,cucumber_pickle,3301140100 +5116,Kurpitsa,Pumpkin,pumpkin_squash_gourd,3301140400 +5117,Pinaatti,Spinach,spinach,3301310800 +5119,Raparperi,Rhubarb,rhubarb,3301230000 +5120,Punajuurikas ja keltajuurikas,Beetroot and yellow beetroot,beetroot_beets,3301290200 +5121,Ruokasipuli (sis. punasipuli ja jättisipuli),Onion (incl. red onion and giant onion),onions,3301220400 +5124,Punakaali,Red cabbage,red_cabbage,3301210210 +5125,Savoijinkaali (kurttukaali),Savoy cabbage,savoy_cabbage,3301210211 +5127,Ruusukaali,Brussels sprouts,brussels_sprouts,3301210203 +5128,Parsakaali,Broccoli,broccoli,3301210202 +5129,Kyssäkaali,Kohlrabi,kohlrabi,3301210209 +5131,Lehtiselleri,Leaf celery,leaf_celery,3301250200 +5133,Lamopinaatti,New Zealand spinach,other_salads_lettuce_leaf_vegetables,3301319900 +5134,Retiisi,Radish,radish,3301290600 +5140,Salaatti (Lactuca-suku),Lettuce,salads_lettuce_leaf_vegetables,3301310000 +5141,Salaattisikurit (Cichorium-suku),Radicchio,chicory_chicories,3301310200 +5142,Salaattifenkoli,Fennel,fennel,3301170000 +5143,Kesäkurpitsa,Zucchini,zucchini_courgette,3301140600 +5148,Lehtikaali,Kale,kale,3301210208 +5149,Tilli,Dill,anethum_dill,3301061203 +5150,Persilja,Parsley,parsly,3301061227 +5157,Valkosipuli,Garlic,garlic,3301220200 +5158,Piparjuuri,Horseradish,horseradish,3301210400 +5165,Maa-artisokka,Jerusalem artichoke,topinambur_jerusalem_artichoke,3301180000 +5173,Meloni,Melon,melon,3301140300 +5175,Lehtimangoldi,Swiss chard,chard,3301310100 +5176,Latva-artisokka,Artichoke,artichoke,3301270000 +5198,Muut vihannekset,Other vegetables,fresh_vegetables,3301070000 +5210,Omena,Apple,apples,3303010200 +5212,Pihlaja (marjantuotanto),Rowan (berry production),rowan_rowanberries,3303021300 +5213,Päärynä,Pear,pears,3303011200 +5220,Muut hedelmät,Other fruits,orchards_fruits,3303010000 +5221,Luumu,Plum,plums,3303011300 +5222,Viinirypäle,Grapevine,vineyards_wine_vine_rebland_grapes,3303060000 +5301,Mustaherukka,Blackcurrant,blackcurrant_cassis,3303020300 +5302,Punaherukka,Redcurrants,redcurrant,3303021100 +5303,Valkoherukka,White currant,currants,3303020600 +5305,Karviainen,Gooseberry,gooseberry_gooseberries_cranberries,3303020700 +5310,Vadelma ja mesivadelma,Raspberries and field raspberries,raspberry_raspberries,3303021000 +5311,Mansikka,Strawberry,strawberries,3301130000 +5312,Mesimarja,Arctic raspberries,raspberry_raspberries,3303021000 +5313,Pensasmustikka,Bush blueberry,blueberry,3303020400 +5314,Marja-aronia,Aronia berry,aronia_chokeberries,3303020100 +5318,Saskatoon (Marjatuomipihlaja),Serviceberry,amelanchier_serviceberry,3303010100 +5319,Muut marjakasvit,Other berry plants,unspecified_berries_berry_species,3303029800 +5410,"Koristekasvit, alle 5 v.","Ornamental plants, under 5 years",flowers_ornamental_plants,3301080000 +5420,"Koristekasvit, 5 v. ja yli, jatkuva sato avomaalta","Ornamentals, 5 years -, continuous outdoor harvest",flowers_ornamental_plants,3301080000 +5440,"Leikkovihreä ja leikkohavu, alle 5 v. kasveista","Ornamental leaves and conifers, under 5 years old",other_tree_wood_forest,3306990000 +5441,"Leikkovihreä ja leikkohavu, väh. 5 v. kasveista","Cut greens and conifers for decoration, at least 5 years old",other_tree_wood_forest,3306990000 +5442,"Koristepaju punontatarkoitukseen, alle 5 v.","Ornamental willow for braiding, under 5 years",willows_osiers,3306080000 +5443,"Koristepaju punontatarkoitukseen, 5 - 20 v.","Ornamental willow for braiding, 5 - 20 years",willows_osiers,3306080000 +5451,Tyrni,Sea buckthorn,hippophae_sea_buckthorns_seaberry,3303020800 +5452,Kirsikka,Cherry,cherry_cherries,3303010400 +5512,"Taimitarhat, alle 5 v. marja-, hedelmä-, koristek.","Nursery (berries, fruits, ornamental plants under 5 years)",nurseries_nursery,3303070000 +5534,Metsäpuiden taimitarhat pellolla,Nursery for forest trees in fields,nurseries_nursery,3303070000 +5537,"Taimitarhat, väh. 5 v. marja-, hedelmä-, koristek.","Nursery (berries, fruits, ornamental plants at least 5 years old)",nurseries_nursery,3303070000 +5806,Kumina,Caraway,caraway,3301061211 +5816,Parsa,Asparagus,asparagus,3301200000 +5824,Sinappi,Mustard,mustard,3301210100 +5831,Korianteri,Coriander,coriander,3301061215 +5845,Ratamot,Plantaginaceae,flowers_ornamental_plants,3301080000 +5846,Yrttikasvit alle 5 v. (ei tilli eikä persilja),Herbs less than 5 years old (no dill or parsley),aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +5847,Yrttikasvit väh. 5 v.,Herbs at least 5 years,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +5850,Saneerauskasvi (valkosinappi),white mustard,mustard,3301210100 +5851,Saneerauskasvi (öljyretikka),oil radish,radish,3301290600 +5852,Saneerauskasvi (samettikukka),Tagetes,tagetes,3301084700 +5853,Saneerauskasviseos,Remediation crop (mixture),fallow_land_not_crop,3301110000 +5860,Pienet vierekkäiset alat,Small adjacent fields,not_known_and_other,3399000000 +6050,Viherlannoitusnurmi,Green manure lawn,fallow_land_not_crop,3301110000 +6051,Viherlannoitusnurmi (ei ympäristösitoumusta),Green manure (no environmental commitment),fallow_land_not_crop,3301110000 +6060,Siirtonurmi,Turf,sod_turf,3301090207 +6111,"1-vuot. kuivaheinä-, säilörehu-, tuorerehunurmet","1 years. dry hay, silage, fresh fodder grasses",temporary_grass,3301090100 +6112,1-vuotiset laidunnurmet,Annual pasture,temporary_grass,3301090100 +6113,1-vuotiset siemennurmet,1-year seed grasses,temporary_grass,3301090100 +6115,"1-vuotinen siemennurmi, yksilajinen","1-year seed grass, single species",temporary_grass,3301090100 +6121,"Moniv. kuivaheinä-, säilörehu- ja tuorerehunurmet","Perennial dry hay, silage and fresh fodder crops",pasture_meadow_grassland_grass,3302000000 +6122,Monivuotiset laidunnurmet,Perennial pasture,pasture_meadow_grassland_grass,3302000000 +6123,Monivuotiset siemennurmet,Perennial seed grasses,pasture_meadow_grassland_grass,3302000000 +6125,"Monivuotinen siemennurmi, yksilajinen","Perennial seed grasses, single species",pasture_meadow_grassland_grass,3302000000 +6210,"Pysyvä kuivah.,säilör., tuorer. (väh 5, alle10 v)","Perm. dry hay, silage, fresh forage (5-10 years)",pasture_meadow_grassland_grass,3302000000 +6220,"Pysyvä laidunnurmi (väh 5, alle 10 v)","Permanent pasture (at least 5, less than 10 years)",pasture_meadow_grassland_grass,3302000000 +6300,Luonnonlaidun ja -niitty,Natural pasture and meadow,pasture_meadow_grassland_grass,3302000000 +6303,Luonnonlaidun jolla korkea luontoarvo (Ahvenanmaa),Natural pasture with high nature values (Åland),pasture_meadow_grassland_grass,3302000000 +6304,Luonnonlaidun jolla kohd. toim.pit. (Ahvenanmaa),Natural pasture with targeted interventions (Åland),pasture_meadow_grassland_grass,3302000000 +6305,Luonnonlaidun kulttuurialueella (Ahvenanmaa),Cultivated Pasture,pasture_meadow_grassland_grass,3302000000 +6307,Muu luonnonlaidun (Ahvenanmaa),Other natural pasture (Åland),pasture_meadow_grassland_grass,3302000000 +6401,Muut rehukasvit,Other fodder plants,other_arable_land_crops,3301990000 +6402,Rehurapsi,Fodder rape,rapeseed_rape,3301060400 +6545,"Englannin raiheinän siemen, valvottu tuotanto",English ryegrass seed,lolium_ryegrass,3301090205 +6546,"Italianraiheinän (westerw.) siemen, valv. tuotanto",Ryegrass,lolium_ryegrass,3301090205 +6550,"Ruokonadan siemen, valvottu tuotanto",Tall fescue seed,festuca_fescue,3301090202 +6561,"Apilan siemen, valvottu tuotanto","Clover seed, controlled production",clover,3301090303 +6562,"Timotein siemen, valvottu tuotanto","Timothy seed, controlled production",timothy,3301090209 +6565,"Nurminadan siemen, valvottu tuotanto",Meadow fescue seed,festuca_fescue,3301090202 +6600,Metsälaidun,Silvopasture,tree_wood_forest,3306000000 +6710,"Hakamaa, avoin",Grazing land open,pasture_meadow_grassland_grass,3302000000 +6720,"Hakamaa, puustoinen",Grazing land wooded,pasture_meadow_grassland_grass,3302000000 +9060,Hunajantuotantoon tarkoitettu kasvusto,Plants for honey production,fallow_land_not_crop,3301110000 +9061,Aitohunajakukka,Lacy phacelia,phacelia,3301061400 +9101,"20 v. erityistukisopimus, pelto","20-year special support agreement, field",not_known_and_other,3399000000 +9102,"20 v. erityistukisopimus, muu ala","20-year special support agreement, other sector",not_known_and_other,3399000000 +9403,Sänkikesanto,Stump fallow,fallow_land_not_crop,3301110000 +9404,Avokesanto,Open fallow,fallow_land_not_crop,3301110000 +9405,"Luonnonhoitopelto (nurmikasvit, väh. 2 v.)","Natural management field (grasses and legumes, min. 2 yrs)",temporary_grass,3301090100 +9412,Viherkesanto (nurmi ja niitty),Grassland (grassland and meadow),pasture_meadow_grassland_grass,3302000000 +9413,Viherkesanto (riista ja maisema),Green cover (wildlife and landscape),pasture_meadow_grassland_grass,3302000000 +9414,Viherkesanto (mesikasvit),honey plants,fallow_land_not_crop,3301110000 +9422,"Monimuotoisuuspelto, riista",Diversity field for wild animals,not_known_and_other,3399000000 +9423,"Monimuotoisuuspelto, maisema","Diversity field, landscape",not_known_and_other,3399000000 +9424,"Monimuotoisuuspelto, niitty 1. ja 2. vuosi","Diversity field, meadow 1st and 2nd year",pasture_meadow_grassland_grass,3302000000 +9620,Tilapäisesti viljelemätön ala,Temporarily uncultivated area,fallow_land_not_crop,3301110000 +9621,Tilapäisesti viljelemätön luonnonlaidun ja -niitty,Temporary uncultivated natural pastures and meadows,pasture_meadow_grassland_grass,3302000000 +9630,Kasvimaa,Garden plot,kitchen_gardens,3301120000 +9700,Pysyvästi viljelemätön,Permanently uncultivated,unmaintained,3308000000 +9801,"Erityistukisopimusala, pysyvä laidun","Contract area for special aid, permanent pasture",pasture_meadow_grassland_grass,3302000000 +9802,"Erityistukisopimusala, muu ala","Contract area for special aid, other area",not_known_and_other,3399000000 +9803,"Erityistukisopimusala, pelto","Contract area for special payments, arable land",other_arable_land_crops,3301990000 +9804,"Erityistukisopimusala, metsämaa","Contract area for special aid, woodland",tree_wood_forest,3306000000 +9805,"Ympäristösopimusala, pysyvä nurmi","Environmental agreement area, permanent grassland",pasture_meadow_grassland_grass,3302000000 +9806,"Kurki-, hanhi- ja joutsenpelto (sopimus)","Fields for cranes, geese and swans (agreement)",not_known_and_other,3399000000 +9807,"Ympäristösopimusala, muu ala","Environmental agreement area, other area",not_known_and_other,3399000000 +9808,"Ympäristösopimusala, metsämaa","Environmental agreement area, woodland",tree_wood_forest,3306000000 +9810,Suojavyöhykenurmi (sopimukset ennen 2015),Protection zone embankment (agreement before 2015),not_known_and_other,3399000000 +9811,Suojavyöhyke (sitoumus alkaen 2015),Protection zone (commitment from 2015),not_known_and_other,3399000000 +9812,Monivuotinen ympäristönurmi,Perennial environmental grass,pasture_meadow_grassland_grass,3302000000 +9820,Suojakaista,Protective strip,not_known_and_other,3399000000 +9830,Maisemapiirre,Landscape feature,not_known_and_other,3399000000 +9409,Luonnonhoitonurmi,Natural management grass,pasture_meadow_grassland_grass,3302000000 +6130,Rehunurmi,Forage grass,pasture_meadow_grassland_grass,3302000000 +5855,Öljyretikka,Oil nettle,nettles,3301061225 +9809,Suojavyöhyke,Protection zone,not_known_and_other,3399000000 +9427,"Monimuotoisuuskasvit, riista","Diversity plants, game",not_known_and_other,3399000000 +9425,"Monimuotoisuuskasvit, niitty","Diversity plants, meadow",not_known_and_other,3399000000 +9710,Viljelemätön,Uncultivated,unmaintained,3308000000 +9428,"Monimuotoisuuskasvit, pölyttäjä ja maisema","Diversity plants, pollinator and landscape",not_known_and_other,3399000000 +6151,"Siemennurmi, yksilajinen","Seed grass, single species",poaceae_grasses,3301090200 +2181,Seos (herne/härkäpapu/makea lupiini/linssi/vilja),Mixture (pea/broad bean/sweet lupine/lentil/cereal),cereal,3301010000 +6140,Laidunnurmi,Pasture grass,pasture_meadow_grassland_grass,3302000000 +5854,Maanparannus- ja saneerauskasviseos,Land improvement and renovation plant mixture,not_known_and_other,3399000000 +6150,Siemennurmi,Seed grass,poaceae_grasses,3301090200 +9415,"Viherkesanto (riista, maisema ja pölyttäjä)","Green cover (game, landscape and pollinator)",not_known_and_other,3399000000 +9651,Muu luomuvalvonnan ala (ei maatalousmaata),Other organic control area (not agricultural land),not_known_and_other,3399000000 +9999,Tuen peruminen,Support cancellation,not_known_and_other,3399000000 +9814,"Ympäristösopimusala, kosteikko","Environmental agreement area, wetland",not_known_and_other,3399000000 +9426,"Monimuotoisuuskasvit, peltolintu","Diversity plants, field bird",not_known_and_other,3399000000 +5122,"Tarhaherne, tuoretuotanto","Pea, fresh production",peas,3301020600 +9813,"Ympäristösopimusala, puustoinen tai muu ala","Environmental agreement area, woody or other area",not_known_and_other,3399000000 +5123,"Tarhaherne, teollisuuden sopimustuotanto","Pea, cultivation production",peas,3301020600 +5856,Samettikukka,Velvet flower,flowers_ornamental_plants,3301080000 +6308,Luonnonlaidun sopimusala (Ahvenanmaa),Natural pasture agreement area (Åland),pasture_meadow_grassland_grass,3302000000 +9652,Maatalousmaan säilyttäminen (pysyvät kasvit),Agricultural land conservation (permanent crops),not_known_and_other,3399000000 +9062,Mesikasvit (Ahvenanmaa),Melliferous plants (Åland),pasture_meadow_grassland_grass,3302000000 +5838,Ruisvirna,Vesperma,not_known_and_other,3399000000 +5859,Sikuri,Chicory,chicory_chicories,3301310200 +5858,Morsinko,Morsinko,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +4010,Tupakka,Tobacco,tobacco,3301060100 \ No newline at end of file diff --git a/tests/data-files/convert/hr/hr_2020.csv b/tests/data-files/convert/hr/hr_2020.csv new file mode 100644 index 00000000..1a02c28b --- /dev/null +++ b/tests/data-files/convert/hr/hr_2020.csv @@ -0,0 +1,16 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +200,Staklenici na oranici,Arable land,arable_crops,3301000000,arable_crops,3301000000 +210,Staklenici na oranici,Greenhouses on arable land,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +310,Livada,Meadow,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +320,Pašnjak,Pasture,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +321,Krški pašnjak,Karstic pasture,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +410,Vinograd,Vineyard,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +411,Iskrčeni vinograd,Grubbed-up vineyard,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +421,Maslinik,Olive grove,olive_plantations,3303050000,olive_plantations,3303050000 +422,Voćnjak,Orchard,orchards_fruits,3303010000,orchards_fruits,3303010000 +430,Kulture kratke ophodnje,Short rotation coppice,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +450,Rasadnik,Nursery,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +451,Matičnjak loznih podloga i plemki,Mother block of vines,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +490,Mješoviti višegodišnji nasadi,Mixed perennial crops,permanent_crops_perennial,3303000000,permanent_crops_perennial,3303000000 +900,Ostale vrste uporabe zemljišta,Other agricultural land,not_known_and_other,3399000000,not_known_and_other,3399000000 +910,Privremeno neodržavana parcela,Temporary non-cultivated land,unmaintained,3308000000,unmaintained,3308000000 \ No newline at end of file diff --git a/tests/data-files/convert/ie/ie.csv b/tests/data-files/convert/ie/ie.csv new file mode 100644 index 00000000..048aa5bf --- /dev/null +++ b/tests/data-files/convert/ie/ie.csv @@ -0,0 +1,186 @@ +original_name,HCAT3_name,HCAT3_code +Access Road / Roadways,not_known_and_other,3399000000 +Agroforestry (<400 trees per hectare),tree_wood_forest,3306000000 +Alfalfa,alfalfa_lucerne,3301090301 +Apples,apples,3303010200 +Arable Habitat,arable_crops,3301000000 +Arable Silage (Grass),pasture_meadow_grassland_grass,3302000000 +Arable Silage (No Grass),other_arable_land_crops,3301990000 +Artichoke,artichoke,3301270000 +Asparagus,asparagus,3301200000 +Baby Leaf Spinach,spinach,3301310800 +Barley - Spring,spring_barley,3301010402 +Barley - Winter,winter_barley,3301010401 +Basil,basil,3301061207 +Beans - Spring,beans,3301020100 +Beans - Winter,beans,3301020100 +Beetroot,beetroot_beets,3301290200 +Blackberries,blackberry,3303020200 +Blackcurrants,blackcurrant_cassis,3303020300 +Blueberries,blueberry,3303020400 +Bog,not_known_and_other,3399000000 +Borage,borage,3301061209 +Broccoli - Spring,broccoli,3301210202 +Broccoli - Winter,broccoli,3301210202 +Brussel sprouts,brussels_sprouts,3301210203 +Building,not_known_and_other,3399000000 +Cabbage - Spring,brassica_oleracea_cabbage,3301210200 +Cabbage - Winter,brassica_oleracea_cabbage,3301210200 +Calabrese,broccoli,3301210202 +Camelina,camelina,3301061500 +Carrots,carrots_daucus,3301290300 +Cauliflowers - Spring,cauliflower,3301210204 +Cauliflowers - Winter,cauliflower,3301210204 +Celeriac,celeriac,3301250100 +Celery - Spring,celery,3301250000 +Celery - Winter,celery,3301250000 +Cherries,cherry_cherries,3303010400 +Christmas Trees,other_tree_wood_forest,3306990000 +Clover,clover,3301090303 +Copse,tree_wood_forest,3306000000 +Coriander,coriander,3301061215 +Courgettes,zucchini_courgette,3301140600 +Cucumbers,cucumber_pickle,3301140100 +Daffodils,narcissus_daffodil,3301083300 +Designated Habitat,not_known_and_other,3399000000 +Environmental Management Of Arable Fallow,fallow_land_not_crop,3301110000 +Extensively Grazed Pasture,pasture_meadow_grassland_grass,3302000000 +Fallow,fallow_land_not_crop,3301110000 +Farm Road,not_known_and_other,3399000000 +Farmyard,not_known_and_other,3399000000 +Fennel,fennel,3301170000 +Flax,flax_linseed,3301060700 +Fodder Beet,mangelwurzel_fodder_beet,3301290400 +Foliage,tree_wood_forest,3306000000 +Forage Rape,rapeseed_rape,3301060400 +Forestry,tree_wood_forest,3306000000 +Forestry 2022,tree_wood_forest,3306000000 +Forestry 2023,tree_wood_forest,3306000000 +Forestry Eligible,tree_wood_forest,3306000000 +Forestry ESB Corridor,tree_wood_forest,3306000000 +Forestry ESB Corridor Eligible,tree_wood_forest,3306000000 +Forestry ESB Corridor Ineligible,tree_wood_forest,3306000000 +Forestry Ineligible,tree_wood_forest,3306000000 +Forestry Pre-2009,tree_wood_forest,3306000000 +Forestry Setaside,tree_wood_forest,3306000000 +Gardens,kitchen_gardens,3301120000 +Garlic,garlic,3301220200 +Glasshouse,greenhouse_foil_film,3305000000 +Gooseberries,gooseberry_gooseberries_cranberries,3303020700 +Grapes,vineyards_wine_vine_rebland_grapes,3303060000 +Grass Seed,temporary_grass,3301090100 +Grass Year 1,temporary_grass,3301090100 +Grass Year 1 (MSS Eco-Scheme),temporary_grass,3301090100 +Grass Year 1 (MSS Measure),temporary_grass,3301090100 +Grass Year 1 (MSS),temporary_grass,3301090100 +Grass Year 2,temporary_grass,3301090100 +Grass Year 2 (MSS),temporary_grass,3301090100 +Grass Year 3,temporary_grass,3301090100 +Grass Year 3 (MSS),temporary_grass,3301090100 +Grass Year 4,temporary_grass,3301090100 +Grass Year 4 (MSS),temporary_grass,3301090100 +Grass Year 5,temporary_grass,3301090100 +Grass Year 5 (MSS),temporary_grass,3301090100 +Grassmeal,temporary_grass,3301090100 +Habitat,not_known_and_other,3399000000 +Hemp for Food Use,hemp_cannabis,3301061000 +Hemp for Industrial Use,hemp_cannabis,3301061000 +Inactive,not_known_and_other,3399000000 +Invalid Crop,not_known_and_other,3399000000 +Kale,kale,3301210208 +Lake / Waterway / Pond,not_known_and_other,3399000000 +Leeks - Spring,leek,3301220300 +Leeks - Winter,leek,3301220300 +Lettuce,salads_lettuce_leaf_vegetables,3301310000 +Linnet Habitat,not_known_and_other,3399000000 +Linseed,flax_linseed,3301060700 +Loganberries,berries_berry_species,3303020000 +Low Input Grassland,pasture_meadow_grassland_grass,3302000000 +Low Input Peat Grassland,pasture_meadow_grassland_grass,3302000000 +Low Input Permanent Pasture,pasture_meadow_grassland_grass,3302000000 +Lucerne,alfalfa_lucerne,3301090301 +Lupins,sweet_lupins,3301020700 +Maize,grain_maize_corn_popcorn,3301010600 +Management of Environmental Fallow (horticulture),fallow_land_not_crop,3301110000 +Management of intensive grassland next to a watercourse,pasture_meadow_grassland_grass,3302000000 +Millet,millet_sorghum,3301010900 +Mint,mints_peppermint,3301061222 +Miscanthus Sinensis,miscanthus_silvergrass,3301083000 +Mixed Cropping,arable_crops,3301000000 +Mustard,mustard,3301210100 +Nursery,nurseries_nursery,3303070000 +Oats - Spring,spring_oats,3301010502 +Oats - Winter,winter_oats,3301010501 +Oilseed Rape - Spring,spring_rapeseed_rape,3301060402 +Oilseed Rape - Winter,winter_rapeseed_rape,3301060401 +Onions,onions,3301220400 +Orchard,orchards_fruits,3303010000 +Other cut flower / bulb crops,unspecified_flowers_ornamental_plants,3301089800 +Pak Choi,bok_choy_pak_choi,3301210201 +Parsley,parsly,3301061227 +Parsnips,parsnips,3301290500 +Pears,pears,3303011200 +Peas,peas,3301020600 +Peppers,capsicum,3301300000 +Permanent Pasture,pasture_meadow_grassland_grass,3302000000 +Permanent Pasture (MSS Eco-Scheme 2023),pasture_meadow_grassland_grass,3302000000 +Permanent Pasture (MSS Measure),pasture_meadow_grassland_grass,3302000000 +Permanent Pasture (MSS),pasture_meadow_grassland_grass,3302000000 +Perpetual Spinach,spinach,3301310800 +Planted Buffer Zone,not_known_and_other,3399000000 +Plums,plums,3303011300 +Potatoes - Early,potatoes,3301030000 +Potatoes - Maincrop,potatoes,3301030000 +Potatoes - Seed,potatoes,3301030000 +Protein/Cereal Mix 50/50,legumes_dried_pulses_protein_crops,3301020000 +Pumpkins,pumpkin_squash_gourd,3301140400 +Quarry,not_known_and_other,3399000000 +Quinoa,quinoa,3301150300 +Raspberries,raspberry_raspberries,3303021000 +Recreational Area,not_known_and_other,3399000000 +Red Clover,clover,3301090303 +Redcurrants,redcurrant,3303021100 +Reed Canary Grass,poaceae_grasses,3301090200 +Rhubarb,rhubarb,3301230000 +Riparian Buffer Zone - Arable,not_known_and_other,3399000000 +Riparian Buffer Zone - Grassland,pasture_meadow_grassland_grass,3302000000 +Riparian Zone,not_known_and_other,3399000000 +Rocket,rocket_arugula,3301310600 +Rocky Outcrop,not_known_and_other,3399000000 +Rosemary,rosemary,3301061230 +Rye,rye,3301010300 +Scallions,scallion,3301220500 +Scrub,shrubberries_shrubs,3303080000 +Shallot,shallot,3301220600 +Short Rotation Coppice,tree_wood_forest,3306000000 +Soya Bean,soy_soybeans,3301160000 +Squash,pumpkin_squash_gourd,3301140400 +Strawberries,strawberries,3301130000 +Sugar Beet,sugar_beet,3301290700 +Sunflower,sunflower,3301060500 +Swede,swede_rutabaga,3301210500 +Sweetcorn,grain_maize_corn_popcorn,3301010600 +Thyme,thyme,3301061237 +Tomatoes,tomato,3301280000 +Traditional Hay Meadow,pasture_meadow_grassland_grass,3302000000 +Tree belts for ammonia capture from farmyards,tree_wood_forest,3306000000 +Triticale - Spring,spring_triticale,3301010802 +Triticale - Winter,winter_triticale,3301010801 +Tulips,tulips,3301084900 +Turnips,turnips,3301290800 +Unknown,not_known_and_other,3399000000 +Vetch,vetches,3301090305 +Wheat - Spring,spring_common_soft_wheat,3301010102 +Wheat - Winter,winter_common_soft_wheat,3301010101 +Wild Bird Cover,not_known_and_other,3399000000 +Willow,willows_osiers,3306080000 +Winter Bird Food Plot,not_known_and_other,3399000000 +Woodland,tree_wood_forest,3306000000 +Red Clover Silage (RCS Measure),clover,3301090303 +Native Tree Area,tree_wood_forest,3306000000 +Grass Year 2 (MSS Measure),pasture_meadow_grassland_grass,3302000000 +Mixed Cropping (Horticulture),arable_crops,3301000000 +Permanent Pasture (MSS Eco-Scheme 2024),pasture_meadow_grassland_grass,3302000000 +Grass Year 2 (MSS Eco-Scheme),pasture_meadow_grassland_grass,3302000000 +Radish,radish,3301290600 +100% Protein,legumes_dried_pulses_protein_crops,3301020000 \ No newline at end of file diff --git a/tests/data-files/convert/it_1/iti1.csv b/tests/data-files/convert/it_1/iti1.csv new file mode 100644 index 00000000..988f1cb0 --- /dev/null +++ b/tests/data-files/convert/it_1/iti1.csv @@ -0,0 +1,370 @@ +nuts,original_code,original_name,translated_name,hcat4_code,hcat4_name,HCAT3_name,HCAT3_code,usage_code,usage_name +iti1,1001,Avena,Oats,3310105000,oats,oats,3301010500,0,No specified use +iti1,1002,Frumento duro,Durum wheat,3310102000,durum_hard_wheat,durum_hard_wheat,3301010200,0,No specified use +iti1,1003,Frumento tenero,Common soft wheat,3310101000,common_soft_wheat,common_soft_wheat,3301010100,0,No specified use +iti1,1005,Mais,Corn,3310106000,maize_corn_popcorn,grain_maize_corn_popcorn,3301010600,0,No specified use +iti1,1007,Orzo,Barley,3310104000,barley,barley,3301010400,0,No specified use +iti1,101,PAPAVERO,POPPY,3310606000,poppy,poppy,3301060600,0,No specified use +iti1,1012,Sorgo,Sorghum,3310116000,sorghum,millet_sorghum,3301010900,0,No specified use +iti1,1013,Triticale,Triticale,3310108000,triticale,triticale,3301010800,6,Harvested as Grains +iti1,1014,Farro,Spelt/emmer/einkorn,3310110000,spelt,spelt,3301011000,0,No specified use +iti1,1095,Bosco,Woodland,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,1098,Vivaio specie ornamentali,Ornamental species nursery,3310800000,flowers_ornamental_plants,flowers_ornamental_plants,3301080000,0,No specified use +iti1,11,STEVIA REBAUDIANA,STEVIA REBAUDIANA,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,1102,Cece,Chickpea,3310201040,chickpeas,chickpeas,3301020200,0,No specified use +iti1,112,CANNA CINESE (MISCANTHUS SINENSIS),CHINESE silver grass (MISCANTHUS SINENSIS),3310617030,miscanthus_silvergrass,miscanthus_silvergrass,3301083000,0,No specified use +iti1,113,AGLIO,GARLIC,3310708020,garlic,garlic,3301220200,0,No specified use +iti1,114,AGRETTO,opposite leaved saltwort (soda ash/ eaten as vegetables),3310717990,other_salads_lettuce_leaf_vegetables,other_salads_lettuce_leaf_vegetables,3301319900,0,No specified use +iti1,1148,Hedysarum coronarium L. (Sulla),Sweet Vetch Hedysarum coronarium L.,3310202050,vetches,vetches,3301090305,0,No specified use +iti1,1167,Medicago sativa L. (Erba medica),Medicago sativa L. (Alfalfa),3310202010,alfalfa_lucerne,alfalfa_lucerne,3301090301,0,No specified use +iti1,117,BROCCOLETTO DI RAPA,Rapini,3310707990,other_brassicaceae_cruciferae,brassicaceae_cruciferae,3301210000,0,No specified use +iti1,1170,Onobrichis viciifolia Scop. (Lupinella),Onobrichis viciifolia Scop. (Sainfoin),3310202060,onobrychis_sainfoins,onobrychis_sainfoins,3301061600,0,No specified use +iti1,1172,PASCOLO ARBORATO - CESPUGLIATO TARA 20%,Wooded Pasture - Shrubby Tare 20%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,1173,PASCOLO ARBORATO - TARA 50%,Wooded Pasture - Tare 50%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,118,CAVOLFIORE,CAULIFLOWER,3310707050,cauliflower,cauliflower,3301210204,0,No specified use +iti1,121,FAGIOLINO,green bean,3310201010,beans,beans,3301020100,0,No specified use +iti1,122,FAGIOLO,bean,3310201010,beans,beans,3301020100,0,No specified use +iti1,127,LATTUGA LATTUGHINO,LETTUCE LETTUCE,3310717000,salads_lettuce_leaf_vegetables,salads_lettuce_leaf_vegetables,3301310000,0,No specified use +iti1,129,MELANZANA,AUBERGINE,3310712000,aubergine_eggplant,aubergine_eggplant,3301260000,0,No specified use +iti1,13,CALLA,CALLA lily,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,130,MELONE,MELON,3310702030,melon,melon,3301140300,0,No specified use +iti1,1305,Tabacco,Tobacco,3310601000,tobacco,tobacco,3301060100,0,No specified use +iti1,1308,Pomodoro,Tomato,3310714000,tomato,tomato,3301280000,0,No specified use +iti1,131,ORTIVE A PIENO CAMPO,FIELD VEGETABLES,3310700000,fresh_vegetables,fresh_vegetables,3301070000,0,No specified use +iti1,135,PORRO,Leek,3310708030,leek,leek,3301220300,0,No specified use +iti1,145,SEDANO,wild celery,3310711000,celery,celery,3301250000,0,No specified use +iti1,1451,Trifoglio,Clover,3310202030,clover,clover,3301090303,0,No specified use +iti1,1464,ERBAIO,forage grass less than a year,3320201000,poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,1482,Fava,Fava bean,3310201010,beans,beans,3301020100,0,No specified use +iti1,149,SESAMO,SESAME,3310604990,other_oilseed_crops,oilseed_crops,3301060800,0,No specified use +iti1,150,TARTUFO DI PRATO,MEADOW TRUFFLE,3340200000,truffle,truffle,3304040000,0,No specified use +iti1,155,STATICE,STATICE (limonium),3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,156,USO NON AGRICOLO - ALTRO,NON-AGRICULTURAL USE - OTHER,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,16,LENTICCHIE,LENTILS,3310201070,lentils,lentils,3301020500,0,No specified use +iti1,162,INDIVIA o SCAROLA,ENDIVE or ESCAROLE,3310717020,endive,endive,3301310300,0,No specified use +iti1,167,RUCOLA,ROCKET rucola,3310717050,rocket_arugula,rocket_arugula,3301310600,0,No specified use +iti1,1700,Terreni ritirati dalla produzione,Land withdrawn from production,3370000000,unmaintained,unmaintained,3308000000,0,No specified use +iti1,171,CORBEZZOLO,STRAWBERRY TREE,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,176,SORBO,Sorbus trees,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,177,GELSO,MULBERRY,3330299000,other_berries_berry_species,berries_berry_species,3303020000,0,No specified use +iti1,178,ACERO,MAPLE,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,179,ONTANO,ALDER,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,181,CARPINO,HORNBEAM,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,183,OLMO,ELM,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,188,PAULOWNIA TOMENTOSA,PAULOWNIA TOMENTOSA Princess tree,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,19,RISONE,RICE,3310107000,rice,rice,3301010700,0,No specified use +iti1,1904,Girasole,Sunflower,3310604020,sunflower,sunflower,3301060500,0,No specified use +iti1,1905,PASCOLO POLIFITA CON ROCCIA AFFIORANTE TARA 20%,Multifloral Pasture with Exposed Rock - Tare 20%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,1906,PASCOLO POLIFITA CON ROCCIA AFFIORANTE TARA 50%,Multifloral Pasture with Exposed Rock TARE 50%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,191,PLATANO,cooking bananas,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,192,FARNIA,English Oak,3360600000,oak,oak,3306060000,0,No specified use +iti1,193,ROVERELLA,Downy oak,3360600000,oak,oak,3306060000,0,No specified use +iti1,194,TIGLIO,LINDEN,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,195,ABETE,FIR,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,197,DOUGLASIA,douglas fir,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,198,PINO MARITTIMO,MARITIME PINE,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,199,CIPRESSO,CYPRESS,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,20,PISELLO,PEA,3310201080,peas,peas,3301020600,0,No specified use +iti1,201,ARANCIO,ORANGE,3330402000,orange,citrus_plantations,3303040000,0,No specified use +iti1,202,MANDARINO,MANDARIN,3330403000,mandarin,citrus_plantations,3303040000,0,No specified use +iti1,204,LIMONE,LEMON,3330404000,lemons,citrus_plantations,3303040000,0,No specified use +iti1,208,PINO,PINE,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,209,PRATO IN ROTOLO (TAPPETO ERBOSO),LAWN IN ROLL (TURF),3320201070,sod_turf,sod_turf,3301090207,0,No specified use +iti1,2101,Albicocco,Apricot,3330103000,apricots,apricots,3303010300,0,No specified use +iti1,2103,Ciliegio,Cherry,3330104000,cherry_cherries,cherry_cherries,3303010400,0,No specified use +iti1,2107,Pesco,Peach,3330111000,peach,peach,3303011100,0,No specified use +iti1,2109,SUSINO,PLUM TREE,3330113000,plums,plums,3303011300,0,No specified use +iti1,213,LYCIUM BARBARUM (GOJI),LYCIUM BARBARUM (GOJI),3330299000,other_berries_berry_species,berries_berry_species,3303020000,0,No specified use +iti1,216,LIQUIRIZIA,LICORICE,3310299000,other_legumes,legumes_dried_pulses_protein_crops,3301020000,0,No specified use +iti1,217,FAGIOLO D'EGITTO,Hyacinth bean,3310201010,beans,beans,3301020100,0,No specified use +iti1,218,PASCOLO CON PRATICHE TRADIZIONALI TARA 50%,PASTURE WITH TRADITIONAL PRACTICES TARE 50%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,219,CIPOLLETTA CIPOLLA D'INVERNO,SPRING ONION,3310708050,scallion,scallion,3301220500,0,No specified use +iti1,2201,Melo,APPLE TREE,3330102000,apples,apples,3303010200,0,No specified use +iti1,2202,Pero,PEAR TREE,3330112000,pears,pears,3303011200,0,No specified use +iti1,221,ANETO,DILL,3310612030,anethum_dill,anethum_dill,3301061203,0,No specified use +iti1,226,CORIANDOLO,CORIADER,3310612150,coriander,coriander,3301061215,0,No specified use +iti1,228,GIUGGIOLO,JUJUBE,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,23,ORTENSIA,HYDRANGEA,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,2302,VITE,GRAPE,3330600000,vineyards_wine_vine_rebland_grapes,vineyards_wine_vine_rebland_grapes,3303060000,0,No specified use +iti1,24,"BIETOLA (Compresa la CHELTENHAM BEET, BARBABIETOLA ROSSA/BIETOLA DA COSTA)","CHARD (Including CHELTENHAM BEET, RED BEET/CHARD)",3310719000,beta_vulgaris,chard,3301310100,0,No specified use +iti1,240,BARBABIETOLA - RAPA ROSSA/BIETOLA DA COSTA,BEET - beetroot/CHARD,3310719000,beta_vulgaris,chard,3301310100,0,No specified use +iti1,2402,Oliva da trasformazione,Olive for processing,3330500000,olive,olive_plantations,3303050000,2,Industry/for processing +iti1,244,TRITORDEUM,TRITORDEUM,3310199000,other_cereals,other_cereals,3301019900,0,No specified use +iti1,245,MENTUCCIA,Lesser calamint,3310612220,mints_peppermint,mints_peppermint,3301061222,0,No specified use +iti1,261,AVENA ALTISSIMA,false oat-grass,3320209900,other_poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,265,FAGIOLO DI SPAGNA,SPANISH BEAN,3310201010,beans,beans,3301020100,0,No specified use +iti1,27,CAROTA,CARROT,3310715020,carrots_daucus,carrots_daucus,3301290300,0,No specified use +iti1,270,CAVOLO CINESE,CHINESE CABBAGE,3310707060,chinese_cabbage,chinese_cabbage,3301210205,0,No specified use +iti1,2706,Tare,non agricultural area,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,279,MENTA DOLCE,SWEET MINT,3310612220,mints_peppermint,mints_peppermint,3301061222,0,No specified use +iti1,28,CAVOLO,CABBAGE,3310707000,brassicaceae_cruciferae,brassicaceae_cruciferae,3301210000,0,No specified use +iti1,280,MENTA PIPERITA,PEPPERMINT,3310612220,mints_peppermint,mints_peppermint,3301061222,0,No specified use +iti1,286,ALTEA,Marshmallow,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,289,ANICE COMUNE,COMMON ANISE,3310612050,anise_aniseed,anise_aniseed,3301061205,0,No specified use +iti1,29,CICERCHIA,CHICKENING PEA,3310201080,peas,peas,3301020600,0,No specified use +iti1,291,ARNICA,ARNICA,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,292,ARONIA NERA,BLACK ARONIA,3330201000,aronia_chokeberries,aronia_chokeberries,3303020100,0,No specified use +iti1,293,CUMINO - CUMINO ROMANO,CUMIN - CUMIN ROMAN,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,295,ARTEMISIA,ARTEMISIA,3310612060,artemisia,artemisia,3301061206,0,No specified use +iti1,296,ASSENZIO,Artemisia absinthium,3310612060,artemisia,artemisia,3301061206,0,No specified use +iti1,297,BARDANA,BURDOCK,3310715010,arctium_burdock,arctium_burdock,3301290100,0,No specified use +iti1,298,BETULLA,BIRCH,3360300000,birch,birch,3306030000,0,No specified use +iti1,3,COLZA,RAPE,3310604010,rapeseed_rape,rapeseed_rape,3301060400,0,No specified use +iti1,300,PIOPPO BIANCO,WHITE POPLAR,3360700000,populus,populus,3306070000,0,No specified use +iti1,301,PIOPPO NERO,BLACK POPLAR,3360700000,populus,populus,3306070000,0,No specified use +iti1,302,PIOPPO TREMULO,aspen,3360200000,aspen,aspen,3306020000,0,No specified use +iti1,303,FAGGIO,BEECH,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,304,BIANCOSPINO,HAWTHORN,3330803000,crataegus_hawthorn,crataegus_hawthorn,3303080300,0,No specified use +iti1,305,BIRICOCCOLO SUSINCOCCO,BIRICOCCOLO Apricot,3330103000,apricots,apricots,3303010300,0,No specified use +iti1,306,BORRAGINE,BORAGE,3310612090,borage,borage,3301061209,0,No specified use +iti1,3080,Noce,WALNUT,3330306000,walnuts,walnuts,3303030600,0,No specified use +iti1,309,FRASSINO OSSIFILLO O MERIDIONALE,ASH narrow-leaved OR SOUTHERN,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,31,PEONIA,PEONY,3310834000,peony_peonies,peony_peonies,3301083400,0,No specified use +iti1,310,GINKGO BILOBA,GINKGO BILOBA,3361000000,gingko,other_tree_wood_forest,3306990000,0,No specified use +iti1,311,LECCIO,HOLM OAK,3360600000,oak,oak,3306060000,0,No specified use +iti1,314,SUGHERA QUERCIA DA SUGHERO,CORK OAK,3360600000,oak,oak,3306060000,0,No specified use +iti1,315,SAMBUCO,elderberry,3330804000,elder_elderberry,elder_elderberry,3303080400,0,No specified use +iti1,316,OLIVELLO O OLIVELLO SPINOSO,SEA BUCKTHORN,3330208000,hippophae_sea_buckthorns_seaberry,hippophae_sea_buckthorns_seaberry,3303020800,0,No specified use +iti1,317,GINEPRO,JUNIPER,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,318,MIRTO,MYRTLE,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,319,RUSCO PUNGITOPO,BUTCHER'S BROOM,3330899000,other_shrubs,shrubberries_shrubs,3303080000,0,No specified use +iti1,32,ERBA MAZZOLINA,cat grass,3320201030,cocksfoot_catgrass,cocksfoot_catgrass,3301090203,0,No specified use +iti1,320,CISTO BIANCO,grey-leaved cistus,3330899000,other_shrubs,shrubberries_shrubs,3303080000,0,No specified use +iti1,321,ACCA SELLOWIANA O FEJIOIA SELLOWIANA,Feijoa,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,322,LIPPIA CITRIODORA O CEDRINA O LIMONCINA (ALOYSIA CITRODORA),Lemon Verbena,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,324,CAMOMILLA,CHAMOMILE,3310612130,chamomile,chamomile,3301061213,0,No specified use +iti1,325,CAMOMILLA ROMANA,ROMAN CHAMOMILE,3310612130,chamomile,chamomile,3301061213,0,No specified use +iti1,328,ELICRISO,HELICHRYSUM,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,329,ENULA,inula,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,33,PHILODENDRO,PHILODENDRON,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,331,ESCOLZIA,Poppy,3310606000,poppy,poppy,3301060600,0,No specified use +iti1,332,FACELIA,phacelia,3311300000,phacelia,phacelia,3301061400,0,No specified use +iti1,334,"FILIPENDULA,ULMARIA",Meadowsweet,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,335,FIORDALISO,CORNFLOWER,3310810000,cornflowers,cornflowers,3301081000,0,No specified use +iti1,336,PRATO POLIFITA,Multifloral Meadow,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,0,No specified use +iti1,337,GALEGA O CAPRAGGINE,GALEGA,3310202070,galega,galega,3301081900,0,No specified use +iti1,338,GIAGGIOLO (IRIS) PALLIDA,Iris,3310823000,iris,iris,3301082300,0,No specified use +iti1,340,GIAGGIOLO O GIGLIO BIANCO IRIS FIORENTINA,White Iris,3310823000,iris,iris,3301082300,0,No specified use +iti1,342,GRINDELIA,GRINDELIA,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,344,IPERICO,st johns wort,3310612340,st_johns_wort,st_johns_wort,3301061234,0,No specified use +iti1,345,ISSOPO,HYSSOP,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,346,GRANO TURANICUM O FRUMENTO ORIENTALE O GRANO KHORASAN,KHORASAN WHEAT,3310199000,other_cereals,other_cereals,3301019900,0,No specified use +iti1,348,MALVA,MALLOW,3310828000,malva,malva,3301082800,0,No specified use +iti1,35,ROSA,ROSE,3310837000,roses,roses,3301083700,0,No specified use +iti1,359,FESTUCA (SP. FESTUCA ARUNDINACEA SCHREB.),Tall fescue,3320201020,festuca_fescue,festuca_fescue,3301090202,0,No specified use +iti1,361,FESTUCA (SP. FESTUCA PRATENSIS HUDS.),Meadow Fescue,3320201020,festuca_fescue,festuca_fescue,3301090202,0,No specified use +iti1,369,LOIETTO (SP. LOLIUM X BOUCHEANUM KUNT.),Ryegrass (Lolium x boucheanum),3320201050,lolium_ryegrass,lolium_ryegrass,3301090205,0,No specified use +iti1,375,POA (SP. POA PRATENSIS L.),Kentucky Bluegrass,3320209900,other_poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,379,TRIFOGLIO (SP. TRIFOLIUM ALEXANDRINUM L.),Clover (Alexandrian Clover),3310202030,clover,clover,3301090303,0,No specified use +iti1,380,PRATI PERMANENTI NATURALI CON VINCOLI AMBIENTALI - TARA 20%,Natural Permanent Grasslands with Environmental Constraints - Tare 20%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,10,Eco-schemes for environment protection +iti1,381,TRIFOGLIO (SP. TRIFOLIUM HYBRIDUM L.),Clover (specifically Hybrid Clover),3310202030,clover,clover,3301090303,0,No specified use +iti1,382,PRATI PERMANENTI NATURALI CON VINCOLI AMBIENTALI - TARA 50%,Natural Permanent Grasslands with Environmental Constraints - Tare 50%,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,10,Eco-schemes for environment protection +iti1,383,TRIFOGLIO (SP. TRIFOLIUM INCARNATUM L.),Clover (specifically Crimson Clover),3310202030,clover,clover,3301090303,0,No specified use +iti1,384,TRIFOGLIO (SP. TRIFOLIUM PRATENSE L.),Clover (specifically Red Clover),3310202030,clover,clover,3301090303,0,No specified use +iti1,385,TRIFOGLIO (SP. TRIFOLIUM REPENS L.),Clover (specifically White Clover),3310202030,clover,clover,3301090303,0,No specified use +iti1,386,MARGINI DEI CAMPI SEMINABILI,Edges of Arable Fields,3310000000,arable_crops,arable_crops,3301000000,0,No specified use +iti1,388,TRIFOGLIO (SP. TRIFOLIUM RESUPINATUM L.),Clover (specifically TRIFOLIUM RESUPINATUM),3310202030,clover,clover,3301090303,0,No specified use +iti1,389,VECCIA SATIVA,Common Vetch,3310202050,vetches,vetches,3301090305,0,No specified use +iti1,39,GINESTRA,Genisteae,3310299000,other_legumes,legumes_dried_pulses_protein_crops,3301020000,0,No specified use +iti1,390,VECCIA VILLOSA,Hairy Vetch,3310202050,vetches,vetches,3301090305,0,No specified use +iti1,391,PRATI PERMANENTI NATURALI CON VINCOLI AMBIENTALI,Natural Permanent Grasslands with Environmental Constraints,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,10,Eco-schemes for environment protection +iti1,392,ORTICA,Nettle,3310612250,nettles,nettles,3301061225,0,No specified use +iti1,393,PARTENIO,feverfew,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,394,PASSIFLORA,passionflower,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,395,PASTINACA,Parsnip,3310715030,parsnips,parsnips,3301290500,0,No specified use +iti1,396,PIANTAGGINE LANCEOLATA O LINGUA DI CANE,Plantain (ribwort plantain),3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,398,PSILLO O PLANTAGO OVATA,Plantain (blond plantain),3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,399,PILOSELLA,Pilosella,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,4,SOIA,Soybean,3310201030,soy_soybeans,soy_soybeans,3301160000,0,No specified use +iti1,405,AVOCADO,AVOCADO,3330116000,avocado,avocado,3303100000,0,No specified use +iti1,407,FICODINDIA,Prickly Pear,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,408,MELOGRANO,Pomegranate,3330114000,pomegranate,pomegranate,3303011400,0,No specified use +iti1,411,ROSA CANINA,dog rose,3310837000,roses,roses,3301083700,0,No specified use +iti1,412,ROVEJA PISELLO SELVATICO,Wild Pea,3310201080,peas,peas,3301020600,0,No specified use +iti1,413,SANTOREGGIA MONTANA,Winter savory,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,414,SCORZONERA,Black Salsify,3310715090,salsify,salsify,3301084000,0,No specified use +iti1,415,TARASSACO,Dandelion,3310814000,dandelions,dandelions,3301081400,0,No specified use +iti1,416,VALERIANA,VALERIAN,3310612380,valerian,valerian,3301061238,0,No specified use +iti1,417,VERBENA OFFICINALE,Common Verbena,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,418,VERGA D'ORO (SOLIDAGO VIRGA AUREA L.),Goldenrod,3310822000,goldenrod,goldenrod,3301082200,0,No specified use +iti1,419,VIOLA TRICOLOR,wild pansy,3310850000,viola,viola,3301085000,0,No specified use +iti1,422,BAMBU,Bamboo,3320209900,other_poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,424,BAMBU GIGANTE,Giant Bamboo,3320209900,other_poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,428,PINO NERO,Black Pine,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,429,RAFANO,wild radish,3310715040,radish,radish,3301290600,0,No specified use +iti1,43,RUSCUS,Butcher's Broom,3330899000,other_shrubs,shrubberries_shrubs,3303080000,0,No specified use +iti1,430,AGRUMI,Citrus Fruits,3330400000,citrus,citrus_plantations,3303040000,0,No specified use +iti1,432,BERGAMOTTO,BERGAMOT,3330499000,other_citrus,citrus_plantations,3303040000,0,No specified use +iti1,435,LIMETTE,LIMES,3330499000,other_citrus,citrus_plantations,3303040000,0,No specified use +iti1,436,SENAPE BRUNA,BROWN MUSTARD,3310707010,mustard,mustard,3301210100,0,No specified use +iti1,438,SENAPE NERA,BLACK MUSTARD,3310707010,mustard,mustard,3301210100,0,No specified use +iti1,445,LAVANDA,LAVENDER,3310612190,lavender_lavandula,lavender_lavandula,3301061219,0,No specified use +iti1,446,POA ANNUA,Annual bluegrass,3320209900,other_poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,447,FUNGHI,MUSHROOMS,3349900000,other_mushrooms,mushrooms_energy_genetically_modified_crops,3304000000,0,No specified use +iti1,45,SPELTA,SPELT,3310110000,spelt,spelt,3301011000,0,No specified use +iti1,453,FAGIOLO DALL'OCCHIO,cow pea,3310201010,beans,beans,3301020100,0,No specified use +iti1,456,ALOE,ALOE,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,457,ECHINACEA PALLIDA,ECHINACEA PALLIDA,3310815000,echinacea,echinacea_sun_hat,3301081500,0,No specified use +iti1,458,ECHINACEA ANGUSTIFOLIA,ECHINACEA ANGUSTIFOLIA,3310815000,echinacea,echinacea_sun_hat,3301081500,0,No specified use +iti1,46,LOIETTO LOGLIO,PERENNIAL RYEGRASS,3320201050,lolium_ryegrass,lolium_ryegrass,3301090205,0,No specified use +iti1,460,PRATI ARIDI - FORMAZIONI ERBOSE CON ORCHIDEE,Arid Grasslands - Herbaceous Formations with Orchids,3320000000,grassland_grass,pasture_meadow_grassland_grass,3302000000,0,No specified use +iti1,463,PINO MUGO,Mountain Pine,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,466,UVA URSINA,BEARBERRY,3330299000,other_berries_berry_species,berries_berry_species,3303020000,0,No specified use +iti1,467,ACHILLEA,Yarrow,3310612390,yarrow,yarrow,3301061239,0,No specified use +iti1,468,"CILIEGIO ACIDO (MARASCA,VISCIOLA,AMARENA)","SOUR CHERRY (MARASCA, SOUR CHERRY, BLACK CHERRY)",3330104000,cherry_cherries,cherry_cherries,3303010400,0,No specified use +iti1,469,OKRA o OCRA o GOMBO,Okra,3310799000,other_fresh_vegetables,fresh_vegetables,3301070000,0,No specified use +iti1,47,LOIETTO LOGLIO PERENNE/LOIETTO INGLESE,RYEGRASS PERENNIAL,3320201050,lolium_ryegrass,lolium_ryegrass,3301090205,0,No specified use +iti1,471,LOTO (KAKI),Persimmon,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,472,FICO,FIG,3330106000,fig,fig,3303010600,0,No specified use +iti1,473,LAMPONE,RASPBERRY,3330202020,raspberry_raspberries,raspberry_raspberries,3303021000,0,No specified use +iti1,474,MORE,BLACKBERRIES,3330202010,blackberry,blackberry,3303020200,0,No specified use +iti1,475,"MIRTILLI ROSSI, MIRTILLI NERI ED ALTRI FRUTTI DEL GENERE VACCINIUM","CRANBERRIES, Blue berries AND OTHER FRUITS OF THE VACCINIUM GENUS",3330299000,other_berries_berry_species,berries_berry_species,3303020000,0,No specified use +iti1,476,NESPOLO,MEDLAR,3330108000,medlar_loquat,medlar_loquat,3303010800,0,No specified use +iti1,477,RIBES NERO,BLACK CURRANT,3330206010,blackcurrant_cassis,blackcurrant_cassis,3303020300,0,No specified use +iti1,479,VISCIOLE,Sour cherry,3330104000,cherry_cherries,cherry_cherries,3303010400,0,No specified use +iti1,48,VIBURNO,VIBURNUM,3330899000,other_shrubs,shrubberries_shrubs,3303080000,0,No specified use +iti1,480,RIBES BIANCO (UVA SPINA),WHITE CURRANTS (GOOSEBERRIES),3330206990,other_ribes,currants,3303020600,0,No specified use +iti1,481,RIBES ROSSO,RED CURRANT,3330206020,redcurrant,redcurrant,3303021100,0,No specified use +iti1,483,CENTELLA,CENTELLA,3310717990,other_salads_lettuce_leaf_vegetables,other_salads_lettuce_leaf_vegetables,3301319900,0,No specified use +iti1,484,CRESCIONE DEI GIARDINI O CRESCIONE INGLESE,GARDEN CRESSE OR ENGLISH CRESSE,3311200000,kitchen_gardens,kitchen_gardens,3301120000,0,No specified use +iti1,485,ALLORO,Bay Laurel,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,488,ECHINACEA PURPUREA,ECHINACEA PURPUREA,3310815000,echinacea,echinacea_sun_hat,3301081500,0,No specified use +iti1,490,FRUTTA A GUSCIO,NUTS,3330300000,nuts,nuts,3303030000,0,No specified use +iti1,491,CARRUBO,CAROB,3330901000,carob,carob,3303110100,0,No specified use +iti1,492,CASTAGNO,CHESTNUT,3330305000,sweet_chestnuts,sweet_chestnuts,3303030500,0,No specified use +iti1,493,MANDORLO,ALMOND TREE,3330301000,almond,almond,3303030100,0,No specified use +iti1,494,NOCCIOLO,HAZELNUT,3330302000,hazelnuts_hazel,hazelnuts_hazel,3303030200,0,No specified use +iti1,496,AMARANTO,AMARANTH,3310703010,amaranth,amaranth,3301150100,0,No specified use +iti1,497,PISTACCHIO,PISTACHIO,3330304000,pistachio,pistachio,3303030400,0,No specified use +iti1,498,NASTURZIO,NASTURTIUM,3310612240,nasturtiums,nasturtiums,3301061224,0,No specified use +iti1,499,PRUGNOLO,blackthorn,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,500,ARBORICOLTURA,ARBORICULTURE,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,501,TARTUFO,TRUFFLE,3340200000,truffle,truffle,3304040000,0,No specified use +iti1,503,VIVAI ORTICOLI,HORTICULTURAL NURSERY,3330700000,nurseries_nursery,nurseries_nursery,3303070000,0,No specified use +iti1,505,CAVE DA PIETRA,STONE QUARRIES,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,507,PIANTE AROMATICHE E MEDICINALI E SPEZIE,AROMATIC AND MEDICINAL PLANTS AND SPICES,3310612000,aromatic_medicinal_culinary_plants_spices_herbs,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,0,No specified use +iti1,508,PORTULACA O PORCELLANA COMUNE,COMMON PURSLANE,3310710000,purslane,purslane,3301240000,0,No specified use +iti1,51,LUPOLINA,Black Medic,3310202010,alfalfa_lucerne,alfalfa_lucerne,3301090301,0,No specified use +iti1,524,CAMELIA,CAMELLIA,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,53,PANICO,foxtail millet,3310109000,millet,millet_sorghum,3301010900,0,No specified use +iti1,535,GERANIO,GERANIUM,3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,539,CARTAMO,SAFFLOWER,3310839000,safflower,safflower,3301083900,0,No specified use +iti1,541,MIZUNA O BRASSICA RAPA,MIZUNA OR BRASSICA RAPA,3310707000,brassicaceae_cruciferae,brassicaceae_cruciferae,3301210000,0,No specified use +iti1,543,CARDIACA,Motherwort,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,545,CHENOPODIUM QUINOA,QUINOA,3310703030,quinoa,quinoa,3301150300,0,No specified use +iti1,546,ERISMO,hedge mustard,3310612000,aromatic_medicinal_culinary_plants_spices_herbs,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,0,No specified use +iti1,55,LINO,LINEN,3310607000,flax_linseed,flax_linseed,3301060700,7,Fiber +iti1,552,VIVAI FRUTTICOLI,FRUIT NURSERY,3330700000,nurseries_nursery,nurseries_nursery,3303070000,0,No specified use +iti1,553,VIVAI VITICOLI,VINEYARD NURSERY,3330600000,vineyards_wine_vine_rebland_grapes,vineyards_wine_vine_rebland_grapes,3303060000,0,No specified use +iti1,554,VIVAI OLIVICOLI,OLIVE NURSERY,3330500000,olive,olive_plantations,3303050000,0,No specified use +iti1,555,VIVAI FORESTALI,FORESTRY NURSERY,3330700000,nurseries_nursery,nurseries_nursery,3303070000,0,No specified use +iti1,556,VIVAI - ALTRI,NURSERY - OTHERS,3330700000,nurseries_nursery,nurseries_nursery,3303070000,0,No specified use +iti1,557,SERRE,GREENHOUSES,3350000000,greenhouse_foil_film,greenhouse_foil_film,3305000000,0,No specified use +iti1,56,CANAPA,HEMP,3310610000,hemp_cannabis,hemp_cannabis,3301061000,0,No specified use +iti1,562,ERBA MEDICA,ALFAFA,3310202010,alfalfa_lucerne,alfalfa_lucerne,3301090301,0,No specified use +iti1,58,RAVIZZONE,RAPESEED,3310604010,rapeseed_rape,rapeseed_rape,3301060400,0,No specified use +iti1,581,GINESTRINO,bird's-foot trefoil,3310202080,birdsfoot_treifoil,legumes_harvested_green,3301090300,0,No specified use +iti1,597,GRANO SARACENO,BUCKWHEAT,3310703020,buckwheat,buckwheat,3301150200,0,No specified use +iti1,60,CEDRO,CEDAR,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,607,LEGUMINOSE DA GRANELLA,GRAIN LEGUMES,3310201010,beans,beans,3301020100,6,Harvested as Grains +iti1,615,LUPINO,LUPIN,3310201090,lupins,sweet_lupins,3301020700,0,No specified use +iti1,619,COLTIVAZIONI ARBOREE PERMANENTI SOGGETTE A DIVIETO DI FERTILIZZAZIONE E DI TRATTAMENTO FITOSANITARIO LUNGO I CORSI D?ACQUA,PERMANENT TREE CROPS SUBJECT TO A BAN ON FERTILIZATION AND PHYTOSANITARY TREATMENT ALONG WATER COURSES,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,62,PEPERONCINO PEPERETTA,CHILI PEPPER,3310716020,chili_pepper,chili_pepper,3301300200,0,No specified use +iti1,622,MELILOTO,sweet yellow clover,3310202030,clover,clover,3301090303,0,No specified use +iti1,624,MIGLIO,Proso MILLET,3310109000,millet,millet_sorghum,3301010900,0,No specified use +iti1,629,ORTI FAMILIARI,Family Gardens,3311200000,kitchen_gardens,kitchen_gardens,3301120000,0,No specified use +iti1,630,ARACHIDE,PEANUT,3310201020,peanuts,arachis,3301090302,0,No specified use +iti1,646,PIANTE ORNAMENTALI,ORNAMENTAL PLANTS,3310800000,flowers_ornamental_plants,flowers_ornamental_plants,3301080000,0,No specified use +iti1,65,PASCOLO POLIFITA,Multifloral Pasture,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,651,COLTIVAZIONI ARBOREE SPECIALIZZATE,SPECIALIZED TREE CROPS,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,653,ARUNDO DONAX,Giant Reed,3320209900,other_poaceae_grasses,poaceae_grasses,3301090200,0,No specified use +iti1,655,ARBORETO CONSOCIABILE (CON COLTIVAZIONI ERBACEE),COMBINABLE ARBORETUM (WITH HERBACEOUS CROPS),3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,656,POMODORINO,CHERRY TOMATO,3310714000,tomato,tomato,3301280000,0,No specified use +iti1,66,CALENDULA,CALENDULA,3310612100,calendula_marigold,calendula_marigold,3301061210,0,No specified use +iti1,660,MANUFATTI,non agricultural area,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,661,COTOGNO,QUINCE,3330115000,quinces,quinces,3303011500,0,No specified use +iti1,666,SEMINATIVI,ARABLE LAND,3310000000,arable_crops,arable_crops,3301000000,0,No specified use +iti1,667,SALICE,WILLOW,3360800000,willows_osiers,willows_osiers,3306080000,0,No specified use +iti1,668,EUCALIPTO,EUCALYPTUS,3360500000,eucalyptus,eucalyptus,3306050000,0,No specified use +iti1,669,ROBINIA,ROBINIA,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,675,SCAGLIOLA,CANARY GRASS,3320201100,canary_seed_canaryseed,canary_seed_canaryseed,3301011400,0,No specified use +iti1,676,TRIFOGLIO (SP. TRIFOLIUM SQUARROSUM L.),CLOVER (SP. TRIFOLIUM SQUARROSUM L.),3310202030,clover,clover,3301090303,0,No specified use +iti1,679,FRUTTETI FAMILIARI,FAMILY ORCHARDS,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,680,SCALOGNO,SHALLOT,3310708060,shallot,shallot,3301220600,0,No specified use +iti1,682,TEF o TEFF,TEFF,3310115000,teff,teff,3301010904,0,No specified use +iti1,684,SEGALA,RYE,3310103000,rye,rye,3301010300,0,No specified use +iti1,685,COLTIVAZIONI ARBOREE PROMISCUE (PIU' SPECIE ARBOREE),Mixed Tree Cultivations (Multiple Tree Species),3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,688,CACO MELA,Oriental PERSIMMON,3330199000,other_orchards_fruits,orchards_fruits,3303010000,0,No specified use +iti1,69,PIOPPO,POPLAR,3360700000,populus,populus,3306070000,0,No specified use +iti1,692,SILENE o SIRENE o STRIGOLI,SILENE,3310843000,silene_catchfly,silene_catchfly,3301084300,0,No specified use +iti1,7,ARALIA,ARALIA,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,7001,Fabbricati,Buildings,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,710,PATATA,POTATO,3310300000,potatoes,potatoes,3301030000,0,No specified use +iti1,719,LUFFA,LUFFA,3310702990,other_cucurbits,cucurbits,3301140000,0,No specified use +iti1,722,MISCUGLIO DI AZOTOFISSATRICI,Mixture of Nitrogen-Fixing Plants,3310201020,peanuts,arachis,3301090302,11,Nitrogen fixing +iti1,732,SPIRULINA,SPIRULINA -cyanobacteria,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,734,FIORI EDULI,Edible Flowers,3310800000,flowers_ornamental_plants,flowers_ornamental_plants,3301080000,0,No specified use +iti1,736,SILFIO (PIANTA DI COPPO) Silphium perfoliatum,SILPHUM Silphium perfoliatum,3310844000,silphium_rosinweeds,silphium_rosinweeds,3301084400,0,No specified use +iti1,76,LUPPOLO,HOPS,3310602000,hops,hops,3301060200,0,No specified use +iti1,77,SENAPE,MUSTARD,3310707010,mustard,mustard,3301210100,0,No specified use +iti1,770,USO NON AGRICOLO - AREE NON COLTIVABILI,NON-AGRICULTURAL USE - NON-CULTIVABLE AREAS,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,783,ALBERI IN FILARE,TREES IN ROWS,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,784,"MACERI, STAGNI E LAGHETTI","Marshes, Ponds, and Small Lakes",3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,785,GRUPPI DI ALBERI E BOSCHETTI,GROUPS OF TREE AND SMALL GROVES,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,786,FOSSATI E CANALI,DITCHES AND CANALS,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,788,SIEPI E FASCE ALBERATE,HEDGES AND TREE-LINED STRIPS,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,789,MARGINI DEI CAMPI,FIELD MARGINS,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,79,VECCE,Vetches,3310202050,vetches,vetches,3301090305,0,No specified use +iti1,790,TERRAZZAMENTI,TERRACES,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,791,FASCE TAMPONE RIPARIALI,RIPARIAN BUFFER STRIPS,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,792,FASCE TAMPONE NON RIPARIALI,NON-RIPARIAN BUFFER STRIPS,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use +iti1,793,ALBERI ISOLATI,TREES ISOLATED,3360000000,tree_wood_forest,tree_wood_forest,3306000000,0,No specified use +iti1,80,CRISANTEMO,CHRYSANTHEMUM,3310809000,chrysanthemum,chrysanthemum,3301080900,0,No specified use +iti1,804,PESCO NETTARINA,PEACH TREE NECTARINE,3330109000,nectarine,nectarine,3303010900,0,No specified use +iti1,826,PINO DOMESTICO,DOMESTIC PINE,3360900000,conifers,tree_wood_forest,3306000000,0,No specified use +iti1,83,TOPINAMBUR,JERUSALEM ARTICHOKE,3310713000,artichoke,artichoke,3301270000,0,No specified use +iti1,831,ACTINIDIA (KIWI),ACTINIDIA (KIWI),3330107000,kiwi,kiwi,3303010700,0,No specified use +iti1,842,RICINO,Castor oil,3330806000,ricinus_castor,ricinus_castor,3303080600,8,Oil +iti1,862,FIENO GRECO,FENUGREEK,3310201060,fenugreek,fenugreek,3301020400,0,No specified use +iti1,88,VIGNA CINESE,Cowpea,3310201010,beans,beans,3301020100,0,No specified use +iti1,89,PATATA AMERICANA (BATATA),Sweet potato,3310400000,sweet_potatoes,sweet_potatoes,3301040000,0,No specified use +iti1,899,PRATO PASCOLO,PASTURE,3320100000,permanent_grassland,pasture_meadow_grassland_grass,3302000000,3,Fodder/Forage +iti1,902,ASPARAGO,ASPARAGUS,3310706000,asparagus,asparagus,3301200000,0,No specified use +iti1,903,BASILICO,BASIL,3310612070,basil,basil,3301061207,0,No specified use +iti1,909,CARCIOFO,ARTICHOKE,3310713000,artichoke,artichoke,3301270000,0,No specified use +iti1,910,CARDI,carduus (plumeless thistles),3310612400,marian_thistles,marian_thistles,3301061300,0,No specified use +iti1,917,CETRIOLO,Cucumber,3310702010,cucumber_pickle,cucumber_pickle,3301140100,0,No specified use +iti1,919,CICORIA,CHICORY,3310717010,chicory_chicories,chicory_chicories,3301310200,0,No specified use +iti1,92,LILIUM,LILY,3310825000,lilies,lilies,3301082500,0,No specified use +iti1,921,CIPOLLA ANCHE DI TIPO LUNGO (echalion),Scallion,3310708050,scallion,scallion,3301220500,0,No specified use +iti1,924,COCOMERO,WATERMELON,3310702050,watermelon,watermelon,3301140500,0,No specified use +iti1,926,FINOCCHIO,FENNEL,3310704000,fennel,fennel,3301170000,0,No specified use +iti1,927,FRAGOLA,STRAWBERRY,3310701000,strawberries,strawberries,3301130000,0,No specified use +iti1,932,PEPERONE,BELL PEPPER,3310716010,bell_pepper_paprika,bell_pepper_paprika,3301300100,0,No specified use +iti1,933,PREZZEMOLO,PARSLEY,3310612270,parsley,parsly,3301061227,0,No specified use +iti1,935,RADICCHIO,RADICCHIO,3310717990,other_salads_lettuce_leaf_vegetables,other_salads_lettuce_leaf_vegetables,3301319900,0,No specified use +iti1,936,RAVANELLO,RADISH,3310715040,radish,radish,3301290600,0,No specified use +iti1,939,SPINACIO,SPINACH,3310717070,spinach,spinach,3301310800,0,No specified use +iti1,940,ZUCCA,PUMPKIN,3310702040,pumpkin_squash_gourd,pumpkin_squash_gourd,3301140400,0,No specified use +iti1,941,ZUCCHINO,COURGETTE,3310702060,zucchini_courgette,zucchini_courgette,3301140600,0,No specified use +iti1,942,ERBA CIPOLLINA,CHIVES,3310708010,chives,chives,3301220100,0,No specified use +iti1,950,PRUGNE,PLUMS,3330113000,plums,plums,3303011300,0,No specified use +iti1,951,TIMO,THYME,3310612370,thyme,thyme,3301061237,0,No specified use +iti1,952,ZAFFERANO,SAFFRON,3310612320,saffron_crocus_sativus,saffron_crocus_sativus,3301061232,0,No specified use +iti1,954,CAVOLO RAPA,Kohlrabi,3310707100,kohlrabi,kohlrabi,3301210209,0,No specified use +iti1,956,SEDANO RAPA,CELERIAC,3310711010,celeriac,celeriac,3301250100,0,No specified use +iti1,961,MELISSA,LEMON BALM,3310612200,lemon_balm_melissa,lemon_balm_melissa,3301061220,0,No specified use +iti1,962,MENTA,MINT,3310612220,mints_peppermint,mints_peppermint,3301061222,0,No specified use +iti1,963,ORIGANO,OREGANO,3310612260,oregano,oregano,3301061226,0,No specified use +iti1,964,MAGGIORANA,MARJORAM,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,965,ROSMARINO,ROSEMARY,3310612300,rosemary,rosemary,3301061230,0,No specified use +iti1,966,SALVIA,SAGE,3310703040,sage_chia,sage_chia,3301190000,0,No specified use +iti1,967,FRASSINO,ASH,3369900000,other_tree_wood_forest,other_tree_wood_forest,3306990000,0,No specified use +iti1,968,CAPPERO,CAPER,3310612990,other_aromatic_medicinal_culinary_plants_spices_herbs,other_aromatic_medicinal_culinary_plants_spices_herbs,3301061299,0,No specified use +iti1,969,AZZERUOLO,Hawthorn,3330803000,crataegus_hawthorn,crataegus_hawthorn,3303080300,0,No specified use +iti1,970,CAVOLO BROCCOLO,CABBAGE BROCCOLI,3310707030,broccoli,broccoli,3301210202,0,No specified use +iti1,971,ALCHECHENGI,Hozuki (similar to groundcherry but purely ornamental),3310899000,other_flowers_ornamental_plants,other_flowers_ornamental_plants,3301089900,0,No specified use +iti1,972,BRASSICA CARINATA (CAVOLO ABISSINO),BRASSICA CARINATA (Ethiopian Rape),3310707990,other_brassicaceae_cruciferae,brassicaceae_cruciferae,3301210000,0,No specified use +iti1,99,MARGHERITA,DAISY,3310813000,daisy_daisies,daisy_daisies,3301081300,0,No specified use +iti1,998,CAMELINA o DORELLA COLTIVATA,CAMELINA,3310615000,camelina,camelina,3301061500,0,No specified use +iti1,,,,3390000000,not_known_and_other,not_known_and_other,3399000000,0,No specified use diff --git a/tests/data-files/convert/lt/lt_2021.csv b/tests/data-files/convert/lt/lt_2021.csv new file mode 100644 index 00000000..229ae4c6 --- /dev/null +++ b/tests/data-files/convert/lt/lt_2021.csv @@ -0,0 +1,25 @@ +original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +Ganyklos-pievos virð 5m.,Pastures-meadows over 5m.,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Þieminiai javai,Winter cereals,winter_unspecified_cereals,3301011501,winter_unspecified_cereals,3301011501 +Ganyklos-pievos iki 5m.,Pastures-meadows up to 5m.,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Vasariniai javai,Summer cereals,summer_unspecified_cereals,3301011503,summer_unspecified_cereals,3301011503 +Kita ariama þemë,Other arable land,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +Rapsai,Rape,rapeseed_rape,3301060400,rapeseed_rape,3301060400 +Darþovës,Vegetables,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +Aviþos,Oats,oats,3301010500,oats,3301010500 +Pûdymas,Fallow,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Ankðtiniai javai,Early cereals,spring_unspecified_cereals,3301011502,spring_unspecified_cereals,3301011502 +Daugiametës þolës,Perennial grasses,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Grikiai,Buckwheat,buckwheat,3301150200,buckwheat,3301150200 +Sodai,Gardens,kitchen_gardens,3301120000,kitchen_gardens,3301120000 +Miðkai,Woods,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +Kukurûzai,Corn,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Uogynai,Berries,berries_berry_species,3303020000,berries_berry_species,3303020000 +Ðlapynës,Spinach,spinach,3301310800,spinach,3301310800 +Kiti sodiniai,Other plantations,other_permanent_crops_plantations,3303990000,other_permanent_crops_plantations,3303990000 +Grioviai,Ditches,not_known_and_other,3399000000,not_known_and_other,3399000000 +Cukriniai runkeliai,Sugar beet,sugar_beet,3301290700,sugar_beet,3301290700 +Aromatinai augalai,Aromatic plants,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Pluoðtinës kanapës,Fiber hemp,hemp_cannabis,3301061000,hemp_cannabis,3301061000 +Grybai,Mushrooms,mushrooms_energy_genetically_modified_crops,3304000000,mushrooms_energy_genetically_modified_crops,3304000000 +Linai,Linen,flax_linen,3301060701,flax_linen,3301060701 \ No newline at end of file diff --git a/tests/data-files/convert/nl/nl.csv b/tests/data-files/convert/nl/nl.csv new file mode 100644 index 00000000..93dc00e7 --- /dev/null +++ b/tests/data-files/convert/nl/nl.csv @@ -0,0 +1,415 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code +174,Bloemzaden open grond,Flower seeds open ground,flowers_ornamental_plants,3301080000 +233,"Tarwe, winter-",wheat winter,winter_common_soft_wheat,3301010101 +234,"Tarwe, zomer-",wheat summer,spring_common_soft_wheat,3301010102 +235,"Gerst, winter-",barley winter,winter_barley,3301010401 +236,"Gerst, zomer-",barley summer,spring_barley,3301010402 +237,Rogge (geen snijrogge),Rye (not cut rye),rye,3301010300 +238,Haver,Oats,oats,3301010500 +241,Kapucijners (en grauwe erwten),Capuchins (and gray peas),peas,3301020600 +242,"Bonen, bruine-",beans brown,beans,3301020100 +244,"Erwten, groene/gele (groen te oogsten)",Peas green/yellow (green to be harvested),peas,3301020600 +246,Karwijzaad (oogst dit jaar),Caraway seed (harvest this year),caraway,3301061211 +247,Blauwmaanzaad,Poppy seeds,poppy,3301060600 +256,"Bieten, suiker-",beets sugar,sugar_beet,3301290700 +257,"Bieten, voeder-",Fodder beets,mangelwurzel_fodder_beet,3301290400 +258,Luzerne,Lucerne,alfalfa_lucerne,3301090301 +259,"Maïs, snij-",maize cut,green_silo_maize,3301090400 +262,"Uien, zaai-",onions sowing,onions,3301220400 +263,"Uien, zilver-",onions silver,onions,3301220400 +265,"Grasland, blijvend",Grassland permanent,pasture_meadow_grassland_grass,3302000000 +266,"Grasland, tijdelijk",Grassland temporary,temporary_grass,3301090100 +308,Erwten (droog te oogsten),Peas (to harvest dry),peas,3301020600 +311,"Bonen, veld- (onder andere duiven-, paarden-, wierbonen) ",Beans field,beans,3301020100 +314,Triticale,Triticale,triticale,3301010800 +316,"Maïs, korrel-",corn grain,grain_maize_corn_popcorn,3301010600 +317,"Maïs, corncob mix",Corn corncob mix,grain_maize_corn_popcorn,3301010600 +331,"Grasland, natuurlijk. Hoofdfunctie landbouw.",Grassland natural. Main function agriculture.,pasture_meadow_grassland_grass,3302000000 +332,"Grasland, natuurlijk. Hoofdfunctie natuur.",Grassland of course. Main function nature.,pasture_meadow_grassland_grass,3302000000 +333,"Rand, grenzend aan blijvend grasland of een blijvende teelt, hoofdzakelijk bestaand uit blijvend gras",Border adjacent to permanent grassland or a permanent crop consisting mainly of permanent grass,pasture_meadow_grassland_grass,3302000000 +334,"Rand, grenzend aan bouwland, hoofdzakelijk bestaand uit blijvend gras",Edge adjacent to arable land consisting mainly of permanent grass,pasture_meadow_grassland_grass,3302000000 +335,Natuurterreinen (incl. heide),Natural areas (incl. heath),not_known_and_other,3399000000 +337,"Bufferstrook, rand (inclusief eventuele oevervegetatie)",Buffer strip edge (including any riparian vegetation),not_known_and_other,3399000000 +338,"Rand, liggend op bouwland en direct grenzend aan bos. Geen landbouwproductie.",Edge lying on arable land and directly adjacent to forest. No agricultural production.,not_known_and_other,3399000000 +343,"Sloot, grenzend aan beheerde akkerrand",Ditch adjacent to managed field edge,not_known_and_other,3399000000 +344,"Rand, grenzend aan blijvend grasland of een blijvende teelt, hoofdzakelijk bestaand uit een ander gewas dan gras",Border adjacent to permanent pasture or permanent crop consisting mainly of a crop other than grass,other_arable_land_crops,3301990000 +346,Tagetes erecta (Afrikaantje),Tagetes erecta (marigold),tagetes,3301084700 +347,Tagetes patula (Afrikaantje),Tagetes patula (tagetes),tagetes,3301084700 +370,"Rand, grenzend aan blijvend grasland of een blijvende teelt, hoofdzakelijk bestaand uit tijdelijk gras",Border adjacent to permanent pasture or a permanent crop consisting mainly of temporary grass,temporary_grass,3301090100 +372,"Rand, grenzend aan bouwland, hoofdzakelijk bestaand uit tijdelijk gras",Edge adjacent to arable land consisting mainly of temporary grass,temporary_grass,3301090100 +375,Hop,Hop,hops,3301060200 +381,Teff,teff,teff,3301010904 +382,Spelt,Spelt,spelt,3301011000 +383,Graszaad,grass seed,temporary_grass,3301090100 +426,"Overige groenbemesters, vlinderbloemige- ",Other green manures leguminous plants,unspecified_legumes_harvested_green,3301090398 +427,"Overige groenbemesters, niet-vlinderbloemige-",Other green manures non-leguminous ,fallow_land_not_crop,3301110000 +428,Gele mosterd,Yellow mustard,mustard,3301210100 +511,Cichorei,Chicory,chicory_chicories,3301310200 +515,Zonnebloemen,sunflowers,sunflower,3301060500 +516,Miscanthus (olifantsgras),Miscanthus (elephant grass),miscanthus_silvergrass,3301083000 +636,Saffloer,safflower,safflower,3301083900 +637,Vrouwenmantel,lady's mantle,alchemilla_ladys_mantle,3301061202 +652,Meekrap,Madder,rubia_tinctorum_common_madder,3301061231 +653,Teunisbloem,evening primrose,primrose,3301083500 +654,Brandnetel,Nettle,nettles,3301061225 +655,Zwarte mosterd,black mustard,mustard,3301210100 +656,Zonnekroon,Sunflower,sunflower,3301060500 +657,Drachtplanten,Perennial plants,permanent_crops_perennial,3303000000 +662,Bos (SBL-regeling) ,Forest (SBL scheme),tree_wood_forest,3306000000 +663,"Lupinen, niet bittere",Lupins not bitter,sweet_lupins,3301020700 +664,Raapzaad,Rapeseed,rapeseed_rape,3301060400 +665,Sojabonen,soybeans,soy_soybeans,3301160000 +666,"Vlas, olie-. Lijnzaad niet van vezelvlas",Flax oil. Linseed not from fiber flax,flax_linseed_oil,3301060702 +670,Japanse haver,Japanese oats,oats,3301010500 +671,Raketblad (aaltjesvanggewas),Rocket leaf (nematode catch crop),rocket_arugula,3301310600 +794,Woudbomen met korte omlooptijd (excl. Wilgenhakhout),Short rotation forest trees (excl. willow coppice),tree_wood_forest,3306000000 +795,Wilgenhakhout,Willow coppice,willows_osiers,3306080000 +796,Kerstbomen,Christmas trees,other_tree_wood_forest,3306990000 +799,"Klaver, rode",clover red,clover,3301090303 +800,Rolklaver,Trefoil,legumes_harvested_green,3301090300 +801,Esparcette,esparcette,esparsette_onobrychis,3301020300 +802,"Wikke, bonte",Vetch variegated,vetches,3301090305 +803,"Wikke, voeder-",vetch feed,vetches,3301090305 +804,Klaverzaad,clover seed,clover,3301090303 +814,"Maïs, suiker-",corn sugar,grain_maize_corn_popcorn,3301010600 +853,"Bonen, tuin- (droog te oogsten) (geen consumptie)",Beans garden (dry harvest) (no consumption),beans,3301020100 +854,"Bonen, tuin- (groen te oogsten)",Beans garden (green to be harvested),beans,3301020100 +863,Bos zonder herplantplicht,Forest without obligation to replant,tree_wood_forest,3306000000 +864,Bos (set aside regeling),Forest (set aside arrangement),tree_wood_forest,3306000000 +944,"Hennep, vezel-",hemp,hemp_cannabis,3301061000 +964,"Dahlia, overige bloemkwekerijgewassen",Dahlia other flower nursery crops,dahlia,3301081200 +965,"Dahlia, droogbloemen",Dahlia dried flowers,dahlia,3301081200 +967,"Gladiool, overige bloemkwekerijgewassen",Gladiolus other flower nursery crops,gladiolus_gladioli,3301082100 +968,"Gladiool, droogbloemen",Gladiolus dried flowers,gladiolus_gladioli,3301082100 +970,"Hyacint, overige bloemkwekerijgewassen",Hyacinth other flower nursery crops,other_flowers_ornamental_plants,3301089900 +971,"Hyacint, droogbloemen",Hyacinth dried flowers,other_flowers_ornamental_plants,3301089900 +973,"Iris, overige bloemkwekerijgewassen",Iris other flower nursery crops,iris,3301082200 +974,"Iris, droogbloemen",Iris dried flowers,iris,3301082200 +976,"Krokus, overige bloemkwekerijgewassen",Crocus other flower nursery crops,saffron_crocus_sativus,3301061232 +979,"Lelie, overige bloemkwekerijgewassen",Lily other flower nursery crops,lilies,3301082500 +982,"Narcis, overige bloemkwekerijgewassen",Narcissus other flower nursery crops,narcissus_daffodil,3301083300 +985,"Tulp, overige bloemkwekerijgewassen",Tulip other flower nursery crops,tulips,3301084900 +988,"Zantedeschia, overige bloemkwekerijgewassen",Zantedeschia other flower nursery crops,other_flowers_ornamental_plants,3301089900 +991,"Overige bloemen, overige bloemkwekerijgewassen",Other flowers other flower nursery crops,unspecified_flowers_ornamental_plants,3301089800 +992,"Overige bloemen, droogbloemen",Other flowers dried flowers,unspecified_flowers_ornamental_plants,3301089800 +994,"Amaryllis, overige bloemkwekerijgewassen",Amaryllis other flower nursery crops,other_flowers_ornamental_plants,3301089900 +997,"Dahlia, bloembollen en - knollen",Dahlia,dahlia,3301081200 +998,"Gladiool, bloembollen en - knollen",Gladiolus flower bulbs and tubers,gladiolus_gladioli,3301082100 +999,"Hyacint, bloembollen en - knollen",Hyacinth flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +1001,"Krokus, bloembollen en - knollen",Crocus flower bulbs and tubers,saffron_crocus_sativus,3301061232 +1002,"Lelie, bloembollen en -knollen",Lily flower bulbs and tubers,lilies,3301082500 +1003,"Narcis, bloembollen en -knollen",Daffodil flower bulbs and tubers,narcissus_daffodil,3301083300 +1004,"Tulp, bloembollen en -knollen",Tulip flower bulbs and tubers,tulips,3301084900 +1005,"Zantedeschia, bloembollen en -knollen",Zantedeschia flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +1006,"Overige bloemen, bloembollen en -knollen",Other flowers bulbs and tubers,unspecified_flowers_ornamental_plants,3301089800 +1007,"Amaryllis, bloembollen en -knollen",Amaryllis flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +1010,"Sierui, overige bloemkwekerijgewassen",Ornamental onions other flower nursery crops,other_flowers_ornamental_plants,3301089900 +1012,"Sierui, bloembollen en -knollen",Ornamental onions flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +1013,"Blauw druifje, overige bloemkwekerijgewassen",Grape hyacinth other flower nursery crops,other_flowers_ornamental_plants,3301089900 +1014,"Blauw druifje, droogbloemen",Grape hyacinth dried flowers,other_flowers_ornamental_plants,3301089900 +1015,"Blauw druifje, bloembollen en -knollen",Grape hyacinth flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +1016,"Kuifhyacint, overige bloemkwekerijgewassen",Crested hyacinth other flower nursery crops,other_flowers_ornamental_plants,3301089900 +1019,"Valeriaan, productie",Valerian production,valerian,3301061238 +1020,"Valeriaan, zaden en opkweekmateriaal",Valerian seeds and propagation material,valerian,3301061238 +1021,Knoflook,Garlic,garlic,3301220200 +1022,Quinoa,Quinoa,quinoa,3301150300 +1023,"Pastinaak, productie",Parsnip production,parsnips,3301290500 +1024,"Pastinaak, zaden en opkweekmateriaal",Parsnips seeds and propagation material,parsnips,3301290500 +1025,"Pioenroos, overige bloemkwekerijgewassen",Peony other flower nursery crops,peony_peonies,3301083400 +1026,"Pioenroos, droogbloemen",Peony dried flowers,peony_peonies,3301083400 +1028,"Lavas (Maggiplant), productie",Lovage (Maggiplant) production,lovage_maggiplant,3301061221 +1030,"Wilde marjolein (Oregano), productie",Wild Marjoram (Oregano) production,oregano,3301061226 +1032,Goudsbloem,Marigold,calendula_marigold,3301061210 +1033,Igniscum Candy,Igniscum Candy,igniscum_candy,3304020100 +1034,Kanariezaad,canary seed,canary_seed_canaryseed,3301011400 +1035,Naaldaar (Setaria),Needle (Setaria),setaria,3301090206 +1036,Wortelpeterselie,parsley root,parsley,3301061227 +1037,"Peterselie, productie",Parsley production,parsley,3301061227 +1039,"Chrysant, overige bloemkwekerijgewassen",Chrysanthemum other flower nursery crops,chrysanthemum,3301080900 +1042,"Angelica, productie",Angelica production,angelica,3301061204 +1044,Papaver,Poppy,poppy,3301060600 +1045,Adonis ,Adonis,adonis,3301080100 +1046,Vergeet mij nietje ,forget me not,other_flowers_ornamental_plants,3301089900 +1047,Cranberry,cranberry,cranberry,3303020500 +1048,"Echinacea (zonnehoed), productie",Echinacea (coneflower) production,echinacea_sun_hat,3301081500 +1050,Leeuwenbekjes,Snapdragons,snapdragons,3301084500 +1051,"Iris, Bolvormend",Iris Spherical,iris,3301082200 +1052,"Iris, Rhizoomvormend",Iris Rhizomatous,iris,3301082200 +1053,"Palmen, pot- en containervelden",Palms pot and container fields,other_tree_wood_forest,3306990000 +1054,"Pioenroos, vermeerdering",Peony propagation,peony_peonies,3301083400 +1067,"Bos- en haagplanten, open grond,",Forest and hedge plants open ground,tree_wood_forest,3306000000 +1068,"Buxus, open grond,",boxwood open ground,shrubberries_shrubs,3303080000 +1069,"Ericaceae (Zoals erica, calluna, rododendron, azalea), open grond,",Ericaceae (Like erica calluna rhododendron azalea) open ground,ericaceae_heather,3301061216 +1070,"Laanbomen/parkbomen, onderstammen, open grond,",Avenue trees/park trees,tree_wood_forest,3306000000 +1071,"Laanbomen/parkbomen, opzetters, open grond,",Avenue trees/park trees erectors open ground,tree_wood_forest,3306000000 +1072,"Laanbomen/parkbomen, spillen, open grond,",avenue trees/park trees spindles open ground,tree_wood_forest,3306000000 +1073,"Rozenstruiken (incl, zaailingen en onderstammen), open grond,",Rose bushes (incl seedlings and rootstocks) open ground,roses,3301083700 +1074,"Sierconiferen, open grond,",Ornamental conifers open ground,other_tree_wood_forest,3306990000 +1075,"Sierheesters en klimplanten, open grond,",Ornamental shrubs and creepers open ground,shrubberries_shrubs,3303080000 +1076,"Trek- en besheesters, open grond,",climbing and berry shrubs,berries_berry_species,3303020000 +1077,"Vruchtbomen, moerbomen, open grond,",Fruit trees nut trees open ground,orchards_fruits,3303010000 +1078,"Vruchtbomen, onderstammen, open grond,",Fruit trees rootstocks open ground,orchards_fruits,3303010000 +1079,"Vruchtbomen, overig, open grond,",Fruit trees other open ground,unspecified_orchards_fruits,3303019800 +1080,"Vaste planten, open grond,",Perennials open ground,permanent_crops_perennial,3303000000 +1081,"Bos- en haagplanten, pot- en containerveld,",Forest and hedge plants pot and container field,tree_wood_forest,3306000000 +1082,"Buxus, pot- en containerveld,",Boxwood pot and container field,shrubberries_shrubs,3303080000 +1083,"Ericaceae (Zoals erica, calluna, rododendron, azalea), pot- en containerveld,",Ericaceae (Like erica calluna rhododendron azalea) pot and container field,ericaceae_heather,3301061216 +1084,"Laanbomen/parkbomen, onderstammen, pot- en containerveld,",Avenue trees/park trees rootstocks pot and container fields,tree_wood_forest,3306000000 +1085,"Laanbomen/parkbomen, opzetters, pot- en containerveld,",Avenue trees/park trees erectors pot and container fields,tree_wood_forest,3306000000 +1086,"Laanbomen/parkbomen, spillen, pot- en containerveld,",Avenue trees/park trees spindles pot and container fields,tree_wood_forest,3306000000 +1087,"Rozenstruiken (incl, zaailingen en onderstammen), pot- en containerveld,",Rose bushes (incl seedlings and rootstocks) pot and container field,roses,3301083700 +1088,"Sierconiferen, pot- en containerveld,",Ornamental conifers pot and container field,other_tree_wood_forest,3306990000 +1089,"Sierheesters en klimplanten, pot- en containerveld,",Ornamental shrubs and climbing plants pot and container field,shrubberries_shrubs,3303080000 +1090,"Trek- en besheesters, pot- en containerveld,",Tensile and berry shrubs pot and container field,shrubberries_shrubs,3303080000 +1091,"Vruchtbomen, moerbomen, pot- en containerveld,",Fruit trees nut trees pot and container field,orchards_fruits,3303010000 +1092,"Vruchtbomen, onderstammen, pot- en containerveld,",Fruit trees rootstocks pot and container fields,orchards_fruits,3303010000 +1093,"Vruchtbomen, overig, pot- en containerveld,",Fruit trees other pot and container field,unspecified_orchards_fruits,3303019800 +1094,"Vaste planten, pot- en containerteelt,",Perennials pot and container cultivation,permanent_crops_perennial,3303000000 +1095,Appelen. Aangeplant lopende seizoen.,Apples. Planted current season.,apples,3303010200 +1096,Appelen. Aangeplant voorafgaande aan lopende seizoen.,apples. Planted prior to current season.,apples,3303010200 +1097,Peren. Aangeplant lopende seizoen.,pears. Planted current season.,pears,3303011200 +1098,Peren. Aangeplant voorafgaande aan lopende seizoen.,pears. Planted prior to current season.,pears,3303011200 +1099,Wijndruiven,Wine grapes,vineyards_wine_vine_rebland_grapes,3303060000 +1100,"Overige pit- en steenvruchten (zoals perziken, tafeldruiven)",Other pome and stone fruits (such as peaches table grapes),orchards_fruits,3303010000 +1869,"Bessen, blauwe",Berries blue,blueberry,3303020400 +1870,Pruimen,plums,plums,3303011300 +1872,"Kersen, zuur (opbrengst bestemd voor verwerkende industrie)",Cherries sour (yield intended for processing industry),cherry_cherries,3303010400 +1873,"Bessen, zwarte (opbrengst verwerkt voor verwerkende industrie)",Berries black (yield processed for processing industry),blackberry,3303020200 +1874,"Overig kleinfruit (zoals kruisbessen, kiwi's)",Other small fruit (such as gooseberries kiwis),orchards_fruits,3303010000 +1876,Snijgroen,cut green,plants_harvested_green,3301090000 +1921,Graszoden,Grass turf,sod_turf,3301090207 +1922,"Koolzaad, winter (incl. boterzaad)",Rapeseed winter (incl. butter seed),winter_rapeseed_rape,3301060401 +1923,"Koolzaad, zomer (incl. boterzaad)",Rapeseed summer (incl. butter seed),spring_rapeseed_rape,3301060402 +1926,Agrarisch natuurmengsel,Agricultural nature mixture,not_known_and_other,3399000000 +1927,Overige akkerbouwgewassen,Other arable crops,other_arable_land_crops,3301990000 +1932,Uien poot en plant eerstejaars,Onions leg and plant first year,onions,3301220400 +1933,Uien poot en plant tweedejaars,Onions leg and plant second year,onions,3301220400 +1934,Sjalotten,shallots,shallot,3301220600 +1935,Maiskolvesilage,Corn silage,green_silo_maize,3301090400 +1936,"Bos, blijvend, met herplantplicht",Forest permanent with replanting obligation,tree_wood_forest,3306000000 +1940,Voedselbos,food forest,tree_wood_forest,3306000000 +1949,Aardperen,Topinambur,topinambur_jerusalem_artichoke,3301290900 +2014,"Aardappelen, consumptie",Potatoes consumption,potatoes,3301030000 +2015,"Aardappelen, poot NAK",Potatoes leg NAK,potatoes,3301030000 +2016,"Aardappelen, poot TBM",Potatoes leg TBM,potatoes,3301030000 +2017,"Aardappelen, zetmeel ",Potatoes starch,potatoes,3301030000 +2025,"Aardappelen, bestrijdingsmaatregel AM",Potatoes AM . control measure,potatoes,3301030000 +2032,"Maïs, energie-",corn energy,grain_maize_corn_popcorn,3301010600 +2300,Onbeteelde grond vanwege een teeltverbod/ontheffing,Uncultivated land due to a cultivation ban/exemption,unmaintained,3308000000 +2325,"Bessen, rode",Berries red,redcurrant,3303021100 +2326,Frambozen,Raspberries,raspberry_raspberries,3303021000 +2327,Bramen,blackberries,blackberry,3303020200 +2328,"Kersen, zoet",Cherries sweet,cherry_cherries,3303010400 +2645,Notenbomen,Nut trees,nuts,3303030000 +2652,Overige granen,Other grains,unspecified_cereals,3301011500 +2700,"Aardbeien open grond, vermeerdering",Strawberries open ground propagation,strawberries,3301130000 +2701,"Aardbeien open grond, wachtbed",Strawberry open ground waiting bed,strawberries,3301130000 +2702,"Aardbeien open grond, productie",Strawberries open ground production,strawberries,3301130000 +2703,"Aardbeien open grond, zaden en opkweekmateriaal",Strawberries open ground seeds and propagation material,strawberries,3301130000 +2704,"Aardbeien op stellingen, vermeerdering",Strawberries on racks propagation,strawberries,3301130000 +2705,"Aardbeien op stellingen, wachtbed",Strawberries on racks waiting bed,strawberries,3301130000 +2706,"Aardbeien op stellingen, productie",Strawberries on racks production,strawberries,3301130000 +2707,"Aardbeien op stellingen, zaden en opkweekmateriaal",Strawberries on racks seeds and propagation material,strawberries,3301130000 +2708,"Andijvie, productie",Endive production,endive,3301310300 +2709,"Andijvie, zaden en opkweekmateriaal",Endive seeds and propagation material,endive,3301310300 +2710,"Asperges, oppervlakte die productie oplevert",Asparagus area yielding production,asparagus,3301200000 +2711,"Asperges, oppervlakte die nog geen productie oplevert",Asparagus area that does not yet yield production,asparagus,3301200000 +2712,"Asperges, zaden en opkweekmateriaal",Asparagus seeds and propagation material,asparagus,3301200000 +2715,"Boerenkool, productie",Kale production,kale,3301210208 +2716,"Boerenkool, zaden en opkweekmateriaal",Kale seeds and propagation material,kale,3301210208 +2717,"Bospeen, productie",Carrot production,carrots_daucus,3301290300 +2718,"Bospeen, zaden en opkweekmateriaal",Carrots seeds and propagation material,carrots_daucus,3301290300 +2719,"Broccoli, productie",Broccoli production,broccoli,3301210202 +2720,"Brocolli, zaden en opkweekmateriaal",Brocolli seeds and growing material,broccoli,3301210202 +2721,"Chinese kool, productie",Chinese cabbage production,chinese_cabbage,3301210205 +2722,"Chinese kool, zaden en opkweekmateriaal",Chinese cabbage seeds and propagation material,chinese_cabbage,3301210205 +2723,"Courgette, productie",Zucchini production,zucchini_courgette,3301140600 +2724,"Courgette, zaden en opkweekmateriaal",Zucchini seeds and propagation material,zucchini_courgette,3301140600 +2725,"Knolselderij, productie",Celeriac production,celeriac,3301250100 +2726,"Knolselderij, zaden en opkweekmateriaal",Celeriac seeds and propagation material,celeriac,3301250100 +2727,"Knolvenkel/venkel, productie",Fennel/fennel production,fennel,3301170000 +2728,"Knolvenkel/venkel, zaden en opkweekmateriaal",Fennel/fennel seeds and propagation material,fennel,3301170000 +2729,"Komkommer, productie",Cucumber production,cucumber_pickle,3301140100 +2731,"Augurk, productie",Pickle production,cucumber_pickle,3301140100 +2732,"Augurk, zaden en opkweekmateriaal",Pickle seeds and propagation material,cucumber_pickle,3301140100 +2735,"Pompoen, productie",Pumpkin production,pumpkin_squash_gourd,3301140400 +2736,"Pompoen, zaden en opkweekmateriaal",Pumpkin seeds and propagation material,pumpkin_squash_gourd,3301140400 +2737,"Koolraap, productie",Rutabaga production,swede_rutabaga,3301210500 +2739,"Koolrabi, productie",Kohlrabi production,kohlrabi,3301210209 +2741,"Kroten/rode bieten, productie",Beets/red beets production,beetroot_beets,3301290200 +2742,"Kroten/rode bieten, zaden en opkweekmateriaal",Beets/red beets seeds and propagation material,beetroot_beets,3301290200 +2743,"Kruiden, productie",Herbs production,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +2744,"Kruiden, zaden en opkweekmateriaal",Herbs seeds and propagation material,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +2745,"Paksoi, productie",Bok choy production,bok_choy_pak_choi,3301210201 +2747,"Peulen, productie","Pulses, production",legumes_dried_pulses_protein_crops,3301020000 +2748,"Peulen, zaden en opkweekmateriaal","Pulses, seeds and propagation material",legumes_dried_pulses_protein_crops,3301020000 +2751,"Pronkbonen, productie",Runner beans production,beans,3301020100 +2752,"Pronkbonen, zaden en opkweekmateriaal",Runner beans seeds and propagation material,beans,3301020100 +2753,"Raapstelen, productie",Turnip greens production,turnips,3301290800 +2754,"Raapstelen, zaden en opkweekmateriaal",Turnip greens seeds and propagation material,turnips,3301290800 +2755,"Rabarber, productie",Rhubarb production,rhubarb,3301230000 +2756,"Rabarber, zaden en opkweekmateriaal",Rhubarb seeds and propagation material,rhubarb,3301230000 +2757,"Radijs, productie",Radish production,radish,3301290600 +2758,"Radijs, zaden en opkweekmateriaal",Radish seeds and propagation material,radish,3301290600 +2759,"Rodekool, productie",Red cabbage production,red_cabbage,3301210210 +2760,"Rodekool, zaden en opkweekmateriaal",Red cabbage seeds and propagation material,red_cabbage,3301210210 +2761,"Savooiekool, productie",Savoy cabbage production,savoy_cabbage,3301210211 +2762,"Savooiekool, zaden en opkweekmateriaal",Savoy cabbage seeds and propagation material,savoy_cabbage,3301210211 +2763,Schorseneren; productie,salsify; production,salsify,3301084000 +2764,"Schorseneren, zaden en opkweekmateriaal",Salsify seeds and propagation material,salsify,3301084000 +2765,"Selderij, bleek- en groen-, productie",Celery bleach and green production,leaf_celery,3301250200 +2766,"Selderij, bleek- en groen-, zaden en opkweekmateriaal",Celery bleach and greens seeds and propagation material,leaf_celery,3301250200 +2767,"Sla, ijsberg-, productie",Lettuce iceberg production,iceberg,3301310400 +2768,"Sla, ijsberg-, zaden en opkweekmateriaal",Lettuce iceberg seeds and propagation material,iceberg,3301310400 +2769,"Sla; radicchio rosso, productie",Salad; radicchio rosso production,other_salads_lettuce_leaf_vegetables,3301319900 +2770,"Sla; radicchio rosso, zaden en opkweekmateriaal",Salad; radicchio rosso seeds and propagation material,other_salads_lettuce_leaf_vegetables,3301319900 +2771,"Sla; overig, productie",Salad; other production,salads_lettuce_leaf_vegetables,3301310000 +2772,"Sla; overig, zaden en opkweekmateriaal",Salad; other seeds and propagation material,salads_lettuce_leaf_vegetables,3301310000 +2773,"Spinazie, productie",Spinach production,spinach,3301310800 +2774,"Spinazie, zaden en opkweekmateriaal",Spinach seeds and propagation material,spinach,3301310800 +2775,"Spitskool, productie",pointed cabbage production,other_brassica_oleracea_cabbage,3301210299 +2776,"Spitskool, zaden en opkweekmateriaal",Pointed cabbage seeds and propagation material,other_brassica_oleracea_cabbage,3301210299 +2777,"Spruitkool/spruitjes, productie",Brussels sprouts production,brussels_sprouts,3301210203 +2778,"Spruitkool/spruitjes, zaden en opkweekmateriaal",Brussels sprouts/brussels sprouts seeds and propagation material,brussels_sprouts,3301210203 +2779,"Stamsperziebonen (=stamslabonen), productie",Main green beans (= main green beans) production,beans,3301020100 +2780,"Stamsperziebonen (=stamslabonen), zaden en opkweekmateriaal",Main green beans (= main green beans) seeds and propagation material,beans,3301020100 +2781,"Stoksnijbonen en stokslabonen, productie",French French beans and French green beans production,beans,3301020100 +2782,"Stoksnijbonen en stokslabonen, zaden en opkweekmateriaal",French haricot beans and French green beans seeds and propagating material,beans,3301020100 +2783,"Waspeen, productie","Carrot, production",carrots_daucus,3301290300 +2784,"Waspeen, zaden en opkweekmateriaal","Carrot, seeds and propagating material",carrots_daucus,3301290300 +2785,"Winterpeen, productie",Carrot production,carrots_daucus,3301290300 +2786,"Winterpeen, zaden en opkweekmateriaal",Winter carrots seeds and propagation material,carrots_daucus,3301290300 +2787,"Witlofwortel, productie",Chicory root production,chicory_chicories,3301310200 +2788,"Witlofwortel, zaden en opkweekmateriaal",Chicory root seeds and propagation material,chicory_chicories,3301310200 +2789,"Witte kool, productie",White cabbage production,white_cabbage,3301210212 +2790,"Witte kool, zaden en opkweekmateriaal",White cabbage seeds and propagation material,white_cabbage,3301210212 +2791,"Overige niet genoemde bladgewassen, productie",Other leaf crops not mentioned production,salads_lettuce_leaf_vegetables,3301310000 +2792,"Overige niet genoemde bladgewassen, zaden en opkweekmateriaal",Other not mentioned leaf crops seeds and propagation material,salads_lettuce_leaf_vegetables,3301310000 +2793,"Overige niet genoemde groenten, productie",Other vegetables not mentioned production,fresh_vegetables,3301070000 +2794,"Overige niet genoemde groenten, zaden en opkweekmateriaal",Other vegetables seeds and propagation material not mentioned,fresh_vegetables,3301070000 +2795,"Bloemkool, winter, productie",Cauliflower winter production,cauliflower,3301210204 +2796,"Bloemkool, winter, zaden en opkweekmateriaal",Cauliflower winter seeds and propagation material,cauliflower,3301210204 +2797,"Bloemkool, zomer, productie",Cauliflower summer production,cauliflower,3301210204 +2798,"Bloemkool, zomer, zaden en opkweekmateriaal",Cauliflower summer seeds and growing material,cauliflower,3301210204 +2799,"Prei, winter, productie",Leek winter production,leek,3301220300 +2800,"Prei, winter, zaden en opkweekmateriaal",Leek winter seeds and propagation material,leek,3301220300 +2801,"Prei, zomer, productie",Leek summer production,leek,3301220300 +2802,"Prei, zomer, zaden en opkweekmateriaal",Leek summer seeds and propagation material,leek,3301220300 +3055,Lisdodde,bulrush,bulrush,3301080600 +3500,"Klaver, Alexandrijnse",Clover Alexandrian,clover,3301090303 +3502,Bladkool,cabbage leaf,brassica_oleracea_cabbage,3301210200 +3503,Bladraap,turnip,turnips,3301290800 +3504,Bladrammenas,fodder radish,radish,3301290600 +3505,Deder,Camelina sativa,camelina,3301061500 +3506,Engels raaigras,perennial ryegrass,lolium_ryegrass,3301090205 +3507,Ethiopische mosterd,Ethiopian mustard,mustard,3301210100 +3508,Facelia,Facelia,phacelia,3301061400 +3509,Festulolium,Festulolium,festulolium,3301090204 +3510,Franse boekweit,French buckwheat,buckwheat,3301150200 +3511,"Klaver, incarnaat",Clover incarnate,clover,3301090303 +3512,Italiaans raaigras,Italian ryegrass,lolium_ryegrass,3301090205 +3513,Westerwolds raaigras,Westerwolds ryegrass,lolium_ryegrass,3301090205 +3515,"Klaver, Perzische",Clover Persian,clover,3301090303 +3517,Sarepta mosterd/Caliente,Sarepta mustard/Caliente,mustard,3301210100 +3519,Soedangras/Sorghum,Sudan grass/Sorghum,millet_sorghum,3301010900 +3521,Stoppelknollen,Stubble Tubers,turnips,3301290800 +3522,Timothee,Timothy,timothy,3301090209 +3523,Veldbeemdgras,Field meadow grass,pasture_meadow_grassland_grass,3302000000 +3524,"Klaver, witte",clover white,clover,3301090303 +3736,"Vlas, vezel-",flax fiber,flax_linen,3301060701 +3801,"Tijdelijk onbeteelde grond, i.v.m. publieke werken",Temporarily uncultivated land due to public works,fallow_land_not_crop,3301110000 +3802,"Tijdelijk onbeteelde grond, anders dan voor publieke werken",Temporary uncultivated land other than for public works,fallow_land_not_crop,3301110000 +3803,"Rand, grenzend aan bouwland, hoofdzakelijk bestaand uit een ander gewas dan gras. (EA: beheer)",Border adjacent to arable land consisting mainly of a crop other than grass. (EA: management),other_arable_land_crops,3301990000 +3804,"Rand, grenzend aan bouwland, hoofdzakelijk bestaand uit een ander gewas dan gras. (EA: onbeheerd)",Border adjacent to arable land consisting mainly of a crop other than grass. (EA: unattended),other_arable_land_crops,3301990000 +3805,"Rietzwenkgras, industriegras",Reed fescue industrial grass,festuca_fescue,3301090202 +3807,"Rietzwenkgras, anders dan voor industriegras",Reed fescue other than for industrial grass,festuca_fescue,3301090202 +3808,Roodzwenkgras,red fescue,festuca_fescue,3301090202 +6522,Riet,reed,poaceae_grasses,3301090200 +2621,Houtwal en houtsingel,Wood bank and tree row,tree_wood_forest,3306000000 +2630,Hakhoutbosje,Small coppice wood,tree_wood_forest,3306000000 +6750,"Engels raaigras, graszaad",Perennial ryegrass seed,lolium_ryegrass,3301090205 +6794,"Groene braak, spontane opkomst",Green fallow spontaneous growth,fallow_land_not_crop,3301110000 +6807,Stroken wild gras,Strips of wild grass,pasture_meadow_grassland_grass,3302000000 +6801,Schouwpad,Inspection path,not_known_and_other,3399000000 +2620,Poel en klein historisch water,Pond and small historic water,not_known_and_other,3399000000 +6751,"Engels raaigras, groenbemesting, vanggewas",Perennial ryegrass green manure catch crop,lolium_ryegrass,3301090205 +2642,Bosje,Small wood,tree_wood_forest,3306000000 +2624,Knip- of scheerheg,Trimmed hedge,shrubberries_shrubs,3303080000 +2629,Struweelrand,Scrub edge,shrubberries_shrubs,3303080000 +2617,Boomgroep,Group of trees,tree_wood_forest,3306000000 +6664,"Uien, rode zaai-",Onions red sowing,onions,3301220400 +2622,Elzensingel,Alder tree row,tree_wood_forest,3306000000 +2626,Laan,Avenue,tree_wood_forest,3306000000 +6660,"Uien, gele zaai-",Onions yellow sowing,onions,3301220400 +2634,Natuurvriendelijke oever,Nature-friendly bank,not_known_and_other,3399000000 +2619,Bossingel,Wooded bank,tree_wood_forest,3306000000 +6800,Ruigtes op landbouwpercelen,Rough vegetation on agricultural plots,not_known_and_other,3399000000 +2639,"Water, overig",Water other,not_known_and_other,3399000000 +7126,"Overige gras, groenbemesting, vanggewas",Other grass green manure catch crop,pasture_meadow_grassland_grass,3302000000 +2625,"Struweelhaag",Shrub hedge,shrubberries_shrubs,3303080000 +2628,Hoogstamboomgaard,Standard orchard,orchards_fruits,3303010000 +6762,"Klaver, rode, groenbemesting, vanggewas",Red clover green manure catch crop,clover,3301090303 +7130,"Rogge, korrelgewas",Rye grain crop,rye,3301010300 +7127,Rietland,Reed land,poaceae_grasses,3301090200 +2618,"Windhaag, in een perceel fruitteelt",Windbreak hedge in a fruit plot,shrubberries_shrubs,3303080000 +6755,"Italiaans raaigras, groenbemesting, vanggewas",Italian ryegrass green manure catch crop,lolium_ryegrass,3301090205 +6760,"Klaver, Perzische, groenbemesting, vanggewas",Persian clover green manure catch crop,clover,3301090303 +2637,Schurvelingen en zandwallen,Field ridges and sand walls,not_known_and_other,3399000000 +7129,"Rogge, groenvoedergewas",Rye green fodder crop,rye,3301010300 +7135,Riet in een subsidiabele sloot,Reed in an eligible ditch,not_known_and_other,3399000000 +7134,"Riet in water, anders dan een subsidiabele sloot",Reed in water other than an eligible ditch,not_known_and_other,3399000000 +6764,"Klaver, witte, groenbemesting, vanggewas",White clover green manure catch crop,clover,3301090303 +6793,Graften,Lynchets,not_known_and_other,3399000000 +6756,"Klaver, Alexandrijnse, groenbemesting, vanggewas",Alexandrian clover green manure catch crop,clover,3301090303 +7131,"Rogge, groenbemesting, vanggewas",Rye green manure catch crop,rye,3301010300 +6763,"Klaver, rode, klaverzaad",Red clover seed,clover,3301090303 +6759,"Klaver, incarnaat, klaverzaad",Crimson clover seed,clover,3301090303 +6783,"Rietzwenkgras, groenbemesting, vanggewas",Tall fescue green manure catch crop,festuca_fescue,3301090202 +6765,"Klaver, witte, klaverzaad",White clover seed,clover,3301090303 +6769,Overig klaverzaad,Other clover seed,clover,3301090303 +6808,Tuunwallen,Turf banks,not_known_and_other,3399000000 +7121,"Bonen, witte-",White beans,beans,3301020100 +6758,"Klaver, incarnaat, groenbemesting, vanggewas",Crimson clover green manure catch crop,clover,3301090303 +6782,"Rietzwenkgras, graszaad",Tall fescue grass seed,festuca_fescue,3301090202 +7125,"Overige groenbemesters, niet-vlinderbloemige-, niet zijnde gras",Other green manures non-leguminous,fallow_land_not_crop,3301110000 +7137,"Bonen, overig",Beans other,beans,3301020100 +6754,"Italiaans raaigras, graszaad",Italian ryegrass seed,lolium_ryegrass,3301090205 +6788,"Veldbeemdgras, graszaad",Meadow grass seed,pasture_meadow_grassland_grass,3302000000 +6809,Voederhaag,Fodder hedge,shrubberries_shrubs,3303080000 +2636,Leibomen,Espalier trees,tree_wood_forest,3306000000 +6768,Overig graszaad,Other grass seed,pasture_meadow_grassland_grass,3302000000 +2631,Griendje,Small willow coppice,willows_osiers,3306080000 +6752,"Festulolium, graszaad",Festulolium grass seed,festulolium,3301090204 +6632,Zoete aardappelen,Sweet potatoes,fresh_vegetables,3301070000 +6802,Sedum,Sedum,other_flowers_ornamental_plants,3301089900 +6791,"Westerwolds raaigras, groenbemesting, vanggewas",Westerwolds ryegrass green manure catch crop,lolium_ryegrass,3301090205 +6789,"Veldbeemdgras, groenbemesting, vanggewas",Meadow grass green manure catch crop,pasture_meadow_grassland_grass,3302000000 +6790,"Westerwolds raaigras, graszaad",Westerwolds ryegrass seed,lolium_ryegrass,3301090205 +7138,Palmkool,Black kale,kale,3301210208 +7124,"Raapzaad, winter",Rapeseed winter,winter_rapeseed_rape,3301060401 +6785,"Roodzwenkgras, groenbemesting, vanggewas",Red fescue green manure catch crop,festuca_fescue,3301090202 +6784,"Roodzwenkgras, graszaad",Red fescue grass seed,festuca_fescue,3301090202 +6792,Zeekraal,Glasswort,fresh_vegetables,3301070000 +7122,Yacon,Yacón,fresh_vegetables,3301070000 +6636,Naakte haver,Naked oats,oats,3301010500 +1049,"Echinacea (zonnehoed), zaden en opkweekmateriaal",Echinacea seeds and propagation material,echinacea_sun_hat,3301081500 +6787,"Timothee, groenbemesting, vanggewas",Timothy green manure catch crop,timothy,3301090209 +6803,"Sneeuwklokje, bloembollen en -knollen",Snowdrop flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +6746,"Beemdlangbloem, graszaad",Meadow foxtail grass seed,pasture_meadow_grassland_grass,3302000000 +6798,Perceel bedekt met gewasresten (mulching),Plot covered with crop residues (mulching),fallow_land_not_crop,3301110000 +1038,"Peterselie, zaden en opkweekmateriaal",Parsley seeds and propagation material,parsley,3301061227 +6757,"Klaver, Alexandrijnse, klaverzaad",Alexandrian clover seed,clover,3301090303 +983,"Narcis, droogbloemen",Daffodil dried flowers,narcissus_daffodil,3301083300 +6786,"Timothee, graszaad",Timothy grass seed,timothy,3301090209 +6749,Bieslook,Chives,parsley,3301061227 +6753,"Festulolium, groenbemesting, vanggewas",Festulolium green manure catch crop,festulolium,3301090204 +2730,"Komkommer, zaden en opkweekmateriaal",Cucumber seeds and propagation material,cucumber_pickle,3301140100 +2733,"Meloen, productie",Melon production,fresh_vegetables,3301070000 +1043,"Angelica, zaden en opkweekmateriaal",Angelica seeds and propagation material,angelica,3301061204 +6797,"Liatris, overige bloemkwekerijgewassen",Liatris other flower nursery crops,other_flowers_ornamental_plants,3301089900 +6795,"Liatris, bloembollen en -knollen",Liatris flower bulbs and tubers,other_flowers_ornamental_plants,3301089900 +6748,"Beemdlangbloem, groenbemesting, vanggewas",Meadow foxtail green manure catch crop,pasture_meadow_grassland_grass,3302000000 +6767,Linzen,Lentils,legumes_dried_pulses_protein_crops,3301020000 +986,"Tulp, droogbloemen",Tulip dried flowers,tulips,3301084900 +2740,"Koolrabi, zaden en opkweekmateriaal",Kohlrabi seeds and propagation material,kohlrabi,3301210209 +6761,"Klaver, Perzische, klaverzaad",Persian clover seed,clover,3301090303 diff --git a/tests/data-files/convert/pt/pt.csv b/tests/data-files/convert/pt/pt.csv new file mode 100644 index 00000000..5fb7b19a --- /dev/null +++ b/tests/data-files/convert/pt/pt.csv @@ -0,0 +1,179 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +109,AMENDOA,ALMOND,almond,3303030100,almond,3303030100 +267,CONSOCIAÇÕES ANUAIS E OUTRAS CULT. FORRAG. ANUAIS,ANNUAL AND OTHER CULT. FORAGE ANNUALS,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +105,MAÇÃ,APPLE,apples,3303010200,apples,3303010200 +107,DAMASCO,apricot,apricots,3303010300,apricots,3303010300 +081,"PLANTAS AROM., MEDICINAIS E CONDIMENTARES",AROM. MEDICINAL AND CONDIMENTAL PLANTS,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +266,CONSOCIAÇÃO DE FIXADORAS DE AZOTO,association of nitrogen-fixing plants,legumes_harvested_green,3301090300,not_known_and_other,3399000000 +136,ABACATE,AVOCADO,avocado,3303100000,avocado,3303100000 +004,CEVADA,BARLEY,barley,3301010400,barley,3301010400 +076,AZEVEM,lolium,lolium_ryegrass,3301090205,lolium_ryegrass,3301090205 +014,FAVA,BEAN,beans,3301020100,beans,3301020100 +230,FEIJÃO,BEAN,beans,3301020100,beans,3301020100 +090,OUTRAS HORTÍCOLAS,Other crops,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +103,BATATA,Potato,potatoes,3301030000,potatoes,3301030000 +032,BETERRABA,BEETROOT,beetroot_beets,3301290200,beetroot_beets,3301290200 +164,POVOAMENTO CARVALHO NEGRAL,BLACK OAK NEGRAL PLANTATION,oak,3306060000,oak,3306060000 +201,AMORA,BLACKBERRY,blackberry,3303020200,blackberry,3303020200 +202,MIRTILO,BLUEBERRY,blueberry,3303020400,blueberry,3303020400 +249,CENOURA,CARROT,carrots_daucus,3301290300,carrots_daucus,3301290300 +110,CASTANHA,CASTANIA,sweet_chestnuts,3303030500,sweet_chestnuts,3303030500 +263,CHUCHU,Chayote Gourd,pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +106,CEREJA,CHERRY,cherry_cherries,3303010400,cherry_cherries,3303010400 +166,POVOAMENTO CASTANHEIRO,CHESTNUT PLANTATIONS,sweet_chestnuts,3303030500,sweet_chestnuts,3303030500 +038,GRÃO DE BICO,CHICKPEA,chickpeas,3301020200,chickpeas,3301020200 +046,TREVO,CLOVER,clover,3301090303,clover,3301090303 +283,MEDRONHO,COMMON LAND PASTURE,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +262,SOBREIRO PARA PRODUÇÃO DE CORTIÇA,CORK OAK FOR CORK PRODUCTION,oak,3306060000,oak,3306060000 +162,POVOAMENTO DE SOBREIROS,CORK OAK PLANTATION,oak,3306060000,oak,3306060000 +006,MILHO,CORN,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +250,COURGETTE,COURGETTE,zucchini_courgette,3301140600,zucchini_courgette,3301140600 +137,BERINGELA,EGGPLANT,aubergine_eggplant,3301260000,aubergine_eggplant,3301260000 +985,LINHAS DE ÁGUA - ÁREA ÚTIL,ELEGIBLE LANDSCAPE FEATURES - WATER LINES,not_known_and_other,3399000000,not_known_and_other,3399000000 +210,POVOAMENTO DE EUCALIPTO,EUCALYPTUS PLANTATION,eucalyptus,3306050000,eucalyptus,3306050000 +163,POVOAMENTO AZINHEIRAS,EVERGREEN OAK PLANTATION,oak,3306060000,oak,3306060000 +089,POUSIO,FALLOWING/ INTERCROPPING (INTERRUPTED CULTIVATEN TO MAKE SOIL MORE FERTILE),fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +89,POUSIO,FALLOWING/ INTERCROPPING (INTERRUPTED CULTIVATEN TO MAKE SOIL MORE FERTILE),fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +085,FIGO,FIG,fig,3303010600,fig,3303010600 +173,ACEIRO FLORESTAL,FOREST FIREBREAKS,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +293,ALHO FRANCÊS,FRENCH GARLIC,garlic,3301220200,garlic,3301220200 +245,ALHO,GARLIC,garlic,3301220200,garlic,3301220200 +254,COUVE,GREEN CABBAGE,brassica_oleracea_cabbage,3301210200,brassica_oleracea_cabbage,3301210200 +139,BOSQUETES,groves,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +116,AVELÃ,HAZELNUT,hazelnuts_hazel,3303030200,hazelnuts_hazel,3303030200 +982,CABECEIRAS CULT. PERMANENTES - ÁREA ÚTIL,Headlands of permanent crops - useful area,not_known_and_other,3399000000,permanent_crops_perennial,3303000000 +124,KIWI,KIWI,kiwi,3303010700,kiwi,3303010700 +097,LIMÃO,LEMON,citrus_plantations,3303040000,citrus_plantations,3303040000 +244,ALFACE,LETTUCE,salads_lettuce_leaf_vegetables,3301310000,salads_lettuce_leaf_vegetables,3301310000 +914,ELEMENTO LINEAR SEBE OU CORTA-VENTO-ÁREA ÚTIL,Linear element hedge or windbreak,not_known_and_other,3399000000,wire_bush,3303080700 +924,ELEMENTO LINEAR EM ORIZICULTURA-ÁREA ÚTIL,Linear element in rice cultivation - useful area,rice,3301010700,rice,3301010700 +724,ELEMENTO LINEAR ARROZ (NÃO ÚTIL-COMP. MAA),Linear element RICE (COMP. MAA),rice,3301010700,rice,3301010700 +044,LUZERNA,LUCERNE,alfalfa_lucerne,3301090301,alfalfa_lucerne,3301090301 +047,TREMOÇO,LUPINE,sweet_lupins,3301020700,sweet_lupins,3301020700 +240,TREMOCILHA,Lupinus luteus,sweet_lupins,3301020700,sweet_lupins,3301020700 +205,MELÃO,MELON,melon,3301140300,melon,3301140300 +170,POVOAMENTO F MISTO,MIXED Forest,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +161,MISTO CULTURAS PERMANENTES,MIXED PERMANENT CULTURES,permanent_crops_perennial,3303000000,unspecified_permanent_crops,3303120000 +276,MOSTARDA,MUSTARD,mustard,3301210100,mustard,3301210100 +265,SUPERFÍCIE ARBUSTIVA NÃO PASTOREÁVEL,non-grazable shrub area,shrubberries_shrubs,3303080000,wire_bush,3303080700 +101,VIVEIROS,NURSERY,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +112,NOZ,NUT,nuts,3303030000,nuts,3303030000 +165,POVOAMENTO MISTO QUERCUS(SOB.AZENH.CARVAL/NEGRAL),oak MIXED SETTLEMENT (UNDER AZENH.CARVAL/NEGRAL),oak,3306060000,oak,3306060000 +005,AVEIA,OAT,oats,3301010500,oats,3301010500 +083,OLIVAL,OLIVE VALLEY,olive_plantations,3303050000,olive_plantations,3303050000 +248,CEBOLA,ONION,onions,3301220400,onions,3301220400 +096,LARANJA,ORANGE,citrus_plantations,3303040000,citrus_plantations,3303040000 +091,FLORES E PLANTAS ORNAMENTAIS,ORNAMENTAL FLOWERS AND PLANTS,flowers_ornamental_plants,3301080000,flowers_ornamental_plants,3301080000 +026,OUTROS CEREAIS,OTHER CEREALS,unspecified_cereals,3301011500,other_cereals,3301019900 +157,OUTROS CITRINOS,OTHER CITRUS FRUIT,citrus_plantations,3303040000,citrus_plantations,3303040000 +169,POVOAMENTO OUTRAS RESINOSAS,OTHER CONIFEROUS FORESTS,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +086,OUTROS FRUTOS SECOS,Other Dried Fruits,unspecified_orchards_fruits,3303019800,orchards_fruits,3303010000 +148,OUTRAS LEGUMINOSAS SECAS,OTHER DRIED VEGETABLES,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +174,OUTRAS SUPERFÍCIES FLORESTAIS,OTHER FOREST SURFACES,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +195,OUTRAS FRUTOS FRESCOS,OTHER FRESH FRUIT,orchards_fruits,3303010000,orchards_fruits,3303010000 +156,OUTRAS OLEAGINOSAS,OTHER OILSEEDS,oilseed_crops,3301060800,oilseed_crops,3301060800 +060,OUTRAS CULTURAS PERMANENTES,OTHER PERMANENT CROPS,other_permanent_crops_plantations,3303990000,other_permanent_crops_plantations,3303990000 +117,OUTROS PEQUENOS FRUTOS,OTHER SMALL FRUITS,orchards_fruits,3303010000,orchards_fruits,3303010000 +102,OUTROS FRUTOS SUB-TROPICAIS,OTHER SUB-TROPICAL FRUITS,orchards_fruits,3303010000,orchards_fruits,3303010000 +307,OUTRAS CULTURAS TEMPORÁRIAS,OTHER TEMPORARY CROPS,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +280,PASTAGENS ARBUSTIVAS,PASTURE WITH BUSHES,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +013,ERVILHA,PEA,peas,3301020600,peas,3301020600 +094,PÊSSEGO,PEACH,peach,3303011100,peach,3303011100 +130,AMENDOIM,PEANUT,arachis,3301090302,nuts,3303030000 +093,PERA,PEAR,pears,3303011200,pears,3303011200 +078,PIMENTO,PEPPER,bell_pepper_paprika,3301300100,piper_pepper,3301061228 +143,PASTAGENS PERMANENTES,PERMANENT PASTURES,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +208,DIOSPIRO,Persimmon,orchards_fruits,3303010000,orchards_fruits,3303010000 +135,PINHÃO,PINE NUTS,other_tree_wood_forest,3306990000,nuts,3303030000 +168,POVOAMENTO DE PINHEIRO MANSO,PINE TREES PLANTATION,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +134,PISTACIOS,PISTACES,pistachio,3303030400,pistachio,3303030400 +167,POVOAMENTO OUTRAS FOLHOSAS,PLANTATION OF OTHER HARDWOODS,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +108,AMEIXA,PLUM,plums,3303011300,plums,3303011300 +209,ROMÃ,POMEGRANATE,pomegranate,3303011400,pomegranate,3303011400 +285,FIGO DA INDIA,Prickly pear,other_permanent_crops_plantations,3303990000,fig,3303010600 +118,MARMELO,QUINCE,quinces,3303011500,quinces,3303011500 +264,COLZA,RAPESEED,rapeseed_rape,3301060400,rapeseed_rape,3301060400 +203,FRAMBOESA,RASPBERRY,raspberry_raspberries,3303021000,raspberry_raspberries,3303021000 +024,ARROZ,RICE,rice,3301010700,rice,3301010700 +125,GALERIA RIPÍCOLA,RIPARIAN GALLERY,not_known_and_other,3399000000,tree_wood_forest,3306000000 +003,CENTEIO,RYE,rye,3301010300,rye,3301010300 +067,AZEVEM,ryegrass,lolium_ryegrass,3301090205,lolium_ryegrass,3301090205 +287,SERRADELA,SERRADELA,serradella,3301084200,legumes_harvested_green,3301090300 +008,SORGO,SORGHUM,millet_sorghum,3301010900,millet_sorghum,3301010900 +211,GINJA,sour cherry,cherry_cherries,3303010400,cherry_cherries,3303010400 +305,ESPINAFRE,SPINACH,spinach,3301310800,spinach,3301310800 +204,MORANGO,STRAWBERRY,strawberries,3301130000,strawberries,3301130000 +131,MEDRONHEIRO,Strawberry Tree,orchards_fruits,3303010000,strawberries,3301130000 +017,GIRASSOL,SUNFLOWER,sunflower,3301060500,sunflower,3301060500 +127,BATATA DOCE,SWEET POTATO,sweet_potatoes,3301040000,sweet_potatoes,3301040000 +033,TOMATE,TOMATO,tomato,3301280000,tomato,3301280000 +007,TRITICALE,TRITICALE,triticale,3301010800,triticale,3301010800 +233,NABO,TURNIP,turnips,3301290800,turnips,3301290800 +277,NABIÇA,Turnip greens,turnips,3301290800,turnips,3301290800 +034,VINHA,VINEYARD,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +242,AGRIÃO,watercress,cress,3301210300,cress,3301210300 +231,MELANCIA,WATERMELON,watermelon,3301140500,watermelon,3301140500 +001,TRIGO,WHEAT,common_soft_wheat,3301010100,common_soft_wheat,3301010100 +286,GROSELHA,Redcurrant,redcurrant,3303021100,redcurrant,3303021100 +155,TANGERINA,Tangerine,citrus_plantations,3303040000,citrus_plantations,3303040000 +925,GALERIA RIPÍCOLA - ÁREA ÚTIL,Riparian Gallery - Usable Area,not_known_and_other,3399000000,not_known_and_other,3399000000 +269,MARACUJÁ,Passion fruit,orchards_fruits,3303010000,orchards_fruits,3303010000 +119,NESPERA,Loquat,medlar_loquat,3303010800,medlar_loquat,3303010800 +009,LINHO,Linen,flax_linen,3301060701,flax_linen,3301060701 +301,CULTURAS EM HIDROPONIA,Hydroponic crops,not_known_and_other,3399000000,not_known_and_other,3399000000 +300,TALUDE DA VINHA,Vineyard,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +234,PEPINO,Cucumber,cucumber_pickle,3301140100,cucumber_pickle,3301140100 +232,MELOA,Melon,melon,3301140300,melon,3301140300 +939,EP-BOSQUETE E FORMAÇÕES RELIQUIAIS-ÁREA ÚTIL,Usable Area,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +128,INHAME,Yam,unspecified_root_vegetables,3301299800,unspecified_root_vegetables,3301299800 +989,ELP VALA DE REGA OU DRENAGEM - ÁREA ÚTIL,Usable Area,not_known_and_other,3399000000,not_known_and_other,3399000000 +123,LIMA,LIME,citrus_plantations,3303040000,citrus_plantations,3303040000 +294,TALHADIA DE CURTA ROTAÇÃO,SHORT ROTATION COPPICE,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +309,ESPARGOS,ASPARAGUS,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +319,FEIJÃO FRADE,COWPEA,beans,3301020100,beans,3301020100 +986,ELP CHARCAS E LAGOAS - ÁREA ÚTIL,ELP PONDS AND LAGOONS - USABLE AREA,not_known_and_other,3399000000,not_known_and_other,3399000000 +725,SUPERFICIE NÃO AGRICOLA,NON-AGRICULTURAL SURFACE,not_known_and_other,3399000000,not_known_and_other,3399000000 +241,ABÓBORAS E ABOBORINHAS,PUMPKINS AND COURGETTES,pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +142,PRADOS TEMPORÁRIOS,TEMPORARY MEADOWS,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +321,CÁRTAMO,SAFFLOWER,oilseed_crops,3301060800,oilseed_crops,3301060800 +279,RÚCULA,ARUGULA,salads_lettuce_leaf_vegetables,3301310000,salads_lettuce_leaf_vegetables,3301310000 +311,ARAÇÁ,STRAWBERRY GUAVA,orchards_fruits,3303010000,orchards_fruits,3303010000 +299,GOIABA,GUAVA,orchards_fruits,3303010000,orchards_fruits,3303010000 +190,MACIÇOS OU FORMAÇÕES RELIQUIAIS OU NOTÁVEIS,RELICT OR REMARKABLE FORMATIONS,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +048,ERVILHACA,VETCH,legumes_harvested_green,3301090300,legumes_harvested_green,3301090300 +310,GOJI,GOJI BERRY,orchards_fruits,3303010000,orchards_fruits,3303010000 +111,ALFARROBA,CAROB,orchards_fruits,3303010000,orchards_fruits,3303010000 +023,MILHO DOCE,SWEET CORN,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +312,PITAIA,DRAGON FRUIT,orchards_fruits,3303010000,orchards_fruits,3303010000 +059,ALGODAO,COTTON,cotton,3301060700,cotton,3301060700 +268,GALERIA RIPICOLA FLORESTAL,RIPARIAN GALLERY FOREST,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +058,CÂNHAMO,HEMP,hemp,3301060702,hemp,3301060702 +322,BAMBU,BAMBOO,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +288,FESTUCA,FESCUE,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +261,TANGERA,TANGERINE,citrus_plantations,3303040000,citrus_plantations,3303040000 +324,TRIGO-SARRACENO,BUCKWHEAT,other_cereals,3301019900,other_cereals,3301019900 +223,SABUGUEIRO (BAGA),ELDERBERRY,orchards_fruits,3303010000,orchards_fruits,3303010000 +298,MANGA,MANGO,orchards_fruits,3303010000,orchards_fruits,3303010000 +314,QUINOA,QUINOA,other_cereals,3301019900,other_cereals,3301019900 +302,CULTURAS SEM SOLO,SOILLESS CULTURES,not_known_and_other,3399000000,not_known_and_other,3399000000 +035,LUPULO,HOPS,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +315,LENTILHA,LENTIL,beans,3301020100,beans,3301020100 +316,CHICHARO,GRASS PEA,beans,3301020100,beans,3301020100 +304,TRIGO SPELTA,SPELT WHEAT,common_soft_wheat,3301010100,common_soft_wheat,3301010100 +987,ELP MURO DE PEDRA POSTA - ÁREA ÚTIL,ELP DRY STONE WALL - USABLE AREA,not_known_and_other,3399000000,not_known_and_other,3399000000 +289,PANASCO,HALFA GRASS,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +151,ANONA,CHERIMOYA,orchards_fruits,3303010000,orchards_fruits,3303010000 +313,PHYSALIS,PHYSALIS,orchards_fruits,3303010000,orchards_fruits,3303010000 +325,MILHO PAINÇO,MILLET,millet_sorghum,3301010900,millet_sorghum,3301010900 +290,BROMUS,BROME GRASS,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +115,CHA,TEA,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +018,SOJA,SOYBEAN,oilseed_crops,3301060800,oilseed_crops,3301060800 +988,ELP PATRIMÓNIO CULTURAL - ÁREA ÚTIL,ELP CULTURAL HERITAGE - USABLE AREA,not_known_and_other,3399000000,not_known_and_other,3399000000 +133,BANANA,BANANA,orchards_fruits,3303010000,orchards_fruits,3303010000 +284,PAPAIA,PAPAYA,orchards_fruits,3303010000,orchards_fruits,3303010000 +237,RÁBANO,RADISH,unspecified_root_vegetables,3301299800,unspecified_root_vegetables,3301299800 +292,ANAFA,ANAFA,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +327,CANÓNIGOS,LAMB'S LETTUCE,salads_lettuce_leaf_vegetables,3301310000,salads_lettuce_leaf_vegetables,3301310000 +323,FUNCHO,FENNEL,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +238,RUTABAGA,RUTABAGA,turnips,3301290800,turnips,3301290800 +308,CARQUEJA,BROOM SHRUB,shrubberries_shrubs,3303080000,wire_bush,3303080700 diff --git a/tests/data-files/convert/se/se.csv b/tests/data-files/convert/se/se.csv new file mode 100644 index 00000000..bb3956bf --- /dev/null +++ b/tests/data-files/convert/se/se.csv @@ -0,0 +1,100 @@ +original_code,original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +1,Korn (höst),Barley (autumn),winter_barley,3301010401,winter_barley,3301010401 +2,Korn (vår),Barley (spring),spring_barley,3301010402,spring_unspecified_cereals,3301011502 +3,Havre,Oats,oats,3301010500,oats,3301010500 +4,Vete (höst),Wheat (autumn),winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +5,Vete (vår),Wheat (spring),spring_common_soft_wheat,3301010102,spring_common_soft_wheat,3301010102 +6,Blandningar av baljväxter eller klöver till grovfoder/ensilage,Mixtures of legumes or clover for roughage / silage,legumes_harvested_green,3301090300,legumes_harvested_green,3301090300 +7,Rågvete (höst),Rye wheat (autumn),winter_rye,3301010301,winter_rye,3301010301 +8,Råg,Rye,rye,3301010300,rye,3301010300 +9,Majs,Maize,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +10,Bovete,Buckwheat,buckwheat,3301150200,buckwheat,3301150200 +11,Spannmålsförsök,Cereal experiments,unspecified_cereals,3301011500,unspecified_cereals,3301011500 +12,Blandsäd (stråsädesblandningar),Mixed grain (cereal mixtures),cereal,3301010000,unspecified_cereals,3301011500 +13,"Blandsäd (spannmåls-/baljväxtblandning), mer än 50% spannmål","Mixed grain (cereal / legume mixture), more than 50% cereals",cereal,3301010000,unspecified_cereals,3301011500 +14,Kanariefrö,Canary seeds,canary_seed_canaryseed,3301011400,canary_seed_canaryseed,3301011400 +15,Hirs,Millet,millet_sorghum,3301010900,millet_sorghum,3301010900 +16,Stråsäd till grönfoder/ensilage,Cereals for green fodder / silage,cereal,3301010000,unspecified_cereals,3301011500 +20,Raps (höst),Rapeseed (autumn),winter_rapeseed_rape,3301060401,winter_rapeseed_rape,3301060401 +21,Raps (vår),Rapeseed (spring),spring_rapeseed_rape,3301060402,spring_rapeseed_rape,3301060402 +22,Rybs (höst),Rybs (autumn),winter_rapeseed_rape,3301060401,winter_rapeseed_rape,3301060401 +23,Rybs (vår),Rybs (spring),spring_rapeseed_rape,3301060402,spring_rapeseed_rape,3301060402 +24,Solros,Sunflower,sunflower,3301060500,sunflower,3301060500 +25,Oljeväxtförsök,Oil plant experiments,oilseed_crops,3301060800,oilseed_crops,3301060800 +27,Vitsenap,Vitsenap,mustard,3301210100,mustard,3301210100 +28,Oljerättika,Oil radish,radish,3301290600,radish,3301290600 +29,Rågvete (vår),Triticale (spring),spring_triticale,3301010802,spring_rye,3301010302 +30,Ärter (ej konservärter),Peas (not canned),peas,3301020600,peas,3301020600 +31,Konservärter,Pea for preserving,peas,3301020600,not_known_and_other,3399000000 +32,Åkerbönor,Field beans,beans,3301020100,beans,3301020100 +33,Sötlupiner,Sweet lupines,sweet_lupins,3301020700,sweet_lupins,3301020700 +34,Proteingrödsblandningar (baljväxter/spannmål)*,Protein crop mixtures (legumes / cereals) *,legumes_dried_pulses_protein_crops,3301020000,legumes_dried_pulses_protein_crops,3301020000 +35,Bruna bönor,Brown beans,beans,3301020100,beans,3301020100 +36,Vicker,Vetch,vetches,3301090305,vetches,3301090305 +37,Kikärter,Chickpeas,chickpeas,3301020200,chickpeas,3301020200 +38,Sojabönor (oljeväxt),Soybeans (oil plant),soy_soybeans,3301160000,soy_soybeans,3301160000 +39,Sojabönor (foderväxt),Soybeans (fodder plant),soy_soybeans,3301160000,soy_soybeans,3301160000 +40,Oljelin,Flax oil,flax_linseed_oil,3301060702,flax_linen,3301060701 +41,Spånadslin,Flax linen,flax_linen,3301060701,flax_linen,3301060701 +42,Hampa,Hemp,hemp_cannabis,3301061000,hemp_cannabis,3301061000 +43,Bönor övriga,Other beans,beans,3301020100,beans,3301020100 +45,Matpotatis,Food potatoes,potatoes,3301030000,potatoes,3301030000 +46,Stärkelsepotatis,Starch potatoes,potatoes,3301030000,potatoes,3301030000 +47,Sockerbetor,Sugar beet,sugar_beet,3301290700,sugar_beet,3301290700 +48,Foderbetor,Feed beets,mangelwurzel_fodder_beet,3301290400,mangelwurzel_fodder_beet,3301290400 +49,Slåtter och betesvall på åkermark med en vallgröda som ej är godkänd för miljöersättning och ersättningar för ekologisk produktion,Mowing and grazing on arable land with a hay crop that is not approved for environmental compensation and compensation for organic production,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +50,Slåtter och betesvall på åkermark,Mowing and grazing on arable land,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +52,Betesmark (ej åker),Pasture (not arable),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +53,Slåtteräng (ej åker),Mowing meadow (does not go),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +54,Skogsbete,Forest grazing,tree_wood_forest,3306000000,tree_wood_forest,3306000000 +55,Fäbodbete som inte ger rätt till gårdsstöd och kompensationsstöd,Mountain pasture that does not entitle to farm support and compensation suppor,pasture_meadow_grassland_grass,3302000000,not_known_and_other,3399000000 +56,"Alvarbete (Öland, Gotland)",Alvar,not_known_and_other,3399000000,unmaintained,3308000000 +57,Slåttervall på åker (kontrakt med vallfodertork),Haymaking in the field (contract with forage dryer),temporary_grass,3301090100,pasture_meadow_grassland_grass,3302000000 +58,Gräsfrövall (ettårig),Grass seed embankment (annual),temporary_grass,3301090100,pasture_meadow_grassland_grass,3302000000 +59,Gräsfrövall (flerårig),Grass seed grass (perennial),temporary_grass,3301090100,pasture_meadow_grassland_grass,3302000000 +60,Träda,Fallow,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +61,Fäbodbete som ger rätt till gårdsstöd och kompensationsstöd,Mountain pasture that entitles to farm support and compensation support,pasture_meadow_grassland_grass,3302000000,not_known_and_other,3399000000 +62,Klöverfrövall,Clover seed embankment,clover,3301090303,clover,3301090303 +63,Energigräs,Energy grass,temporary_grass,3301090100,temporary_grass,3301090100 +65,Salix,Salix willow,willows_osiers,3306080000,pasture_meadow_grassland_grass,3302000000 +66,Anpassade skyddszoner,Custom protection zones,not_known_and_other,3399000000,not_known_and_other,3399000000 +67,Poppel,Poplar,populus,3306070000,aspen,3306020000 +68,Hybridasp,Hybrid poplar,populus,3306070000,aspen,3306020000 +70,Jordgubbsodling,Strawberry cultivation,strawberries,3301130000,strawberries,3301130000 +71,Övrig bärodling,Other berry cultivation,berries_berry_species,3303020000,berries_berry_species,3303020000 +72,Fruktodling,Fruit growing,orchards_fruits,3303010000,orchards_fruits,3303010000 +74,Grönsaksodling (köksväxter),Vegetable growing (vegetables),fresh_vegetables,3301070000,fresh_vegetables,3301070000 +77,Skyddszon mot vattendrag,Protection zone against watercourses,not_known_and_other,3399000000,not_known_and_other,3399000000 +78,Plantskolor med odling av permanenta grödor,Nurseries with cultivation of permanent crops,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +79,Kryddväxter och utsäde grönsaker,Herbs and seed vegetables,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +80,Grönfoder,Green fodder,plants_harvested_green,3301090000,pasture_meadow_grassland_grass,3302000000 +81,Gröngödsling,Green manure,plants_harvested_green,3301090000,pasture_meadow_grassland_grass,3302000000 +82,Våtmark,Wetland,not_known_and_other,3399000000,not_known_and_other,3399000000 +83,Julgransodling,Christmas tree cultivation,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +85,"Trädgårdsodling (ej köksväxter, frukt eller bär)","Horticulture (excluding vegetables, fruit or berries)",kitchen_gardens,3301120000,kitchen_gardens,3301120000 +86,Ej stödberättigande gröda (bara för ersättningarna inom ekologisk produktion),Ineligible crop (only for compensation in organic production),other_arable_land_crops,3301990000,not_known_and_other,3399000000 +87,Annan stödberättigande gröda (bara för ersättningarna inom ekologisk produktion),Other eligible crops (only for compensation in organic production),other_arable_land_crops,3301990000,not_known_and_other,3399000000 +88,Övrig odling på åkermark***,Other cultivation on arable land ***,other_arable_land_crops,3301990000,arable_crops,3301000000 +89,Mosaikbetesmark,Mosaic pasture,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +90,Gräsfattiga marker,Grass-poor lands,not_known_and_other,3399000000,pasture_meadow_grassland_grass,3302000000 +95,Betesmark och slåtteräng under restaurering,Pasture and hay meadow during restoration,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +73,Nötodling,Nut cultivation,nuts,3303030000,nuts,3303030000 +300,Fodermärgkål,Fodder marrowstem kale,kale,3301210208,kale,3301210208 +301,Westerwoldiskt rajgräs,Westerwolds ryegrass,lolium_ryegrass,3301090205,lolium_ryegrass,3301090205 +302,Lusern,Lucerne,alfalfa_lucerne,3301090301,alfalfa_lucerne,3301090301 +303,Cikoria,Chicory,chicory_chicories,3301310200,chicory_chicories,3301310200 +304,Gräsmatteodling,Turf cultivation,sod_turf,3301090207,sod_turf,3301090207 +305,Humle,Hops,hops,3301060200,hops,3301060200 +306,Quinoa,Quinoa,quinoa,3301150300,quinoa,3301150300 +307,Speltvete,Spelt,spelt,3301011000,spelt,3301011000 +308,Sötväppling,Sweet clover,melilot,3301090304,clover,3301090303 +309,Tobak,Tobacco,tobacco,3301060100,tobacco,3301060100 +310,Viltåker,Game field,not_known_and_other,3399000000,not_known_and_other,3399000000 +311,Färskpotatis,Fresh potatoes,potatoes,3301030000,potatoes,3301030000 +312,Sparris,Asparagus,asparagus,3301200000,asparagus,3301200000 +313,Rabarber,Rhubarb,rhubarb,3301230000,rhubarb,3301230000 +314,Annan markanvändning som inte är stödberättigande,Other land use that is not eligible,not_known_and_other,3399000000,not_known_and_other,3399000000 +315,Blandsäd (stråsädesblandningar) (höst),Mixed grain (cereal mixtures) (autumn),cereal,3301010000,unspecified_cereals,3301011500 +316,Vete (flerårigt),Wheat (perennial),common_soft_wheat,3301010100,common_soft_wheat,3301010100 +317,Råg (flerårigt),Rye (perennial),rye,3301010300,rye,3301010300 +318,Blommande åker och fältkant,Flowering arable field and field margin,not_known_and_other,3399000000,not_known_and_other,3399000000 \ No newline at end of file diff --git a/tests/data-files/convert/si/si.csv b/tests/data-files/convert/si/si.csv new file mode 100644 index 00000000..ca026ab0 --- /dev/null +++ b/tests/data-files/convert/si/si.csv @@ -0,0 +1,180 @@ +original_code,original_name,latin_name,translated_name,HCAT3_name,HCAT3_code +000,ni v uporabi,Not known,Not known,not_known_and_other,3399000000 +001,pšenica (jara),Triticum aestivum L.,Common wheat spring,spring_common_soft_wheat,3301010102 +002,rž (jara),Secale cereale L.,Rye (spring),spring_rye,3301010302 +003,pira (jara),Triticum spelta L.,Spring Spelt,spring_spelt,3301011002 +004,ajda,Fagopyrum esculentum Moench,Buckwheat,buckwheat,3301150200 +005,koruza za zrnje,Zea mays L.,Grain maize,grain_maize_corn_popcorn,3301010600 +006,koruza za silažo,Zea mays L.,Silo Maize,green_silo_maize,3301090400 +007,tritikala (jara),X Triticosecale Wittmack (Triticum x Secale),Spring Triticale,spring_triticale,3301010802 +008,oves (jari),Avena sativa L.,Oats (spring),spring_oats,3301010502 +009,ječmen (jari),"Hordeum vulgare L., spring barley",spring barley,spring_barley,3301010402 +010,proso,Panicum miliaceum L.,Proso millet,millet_sorghum,3301010900 +011,mešanice žit (jara),Mixture of cereals (spring),Mixture of cereals (spring),spring_unspecified_cereals,3301011502 +012,sončnice,Helianthus annus L.,Common Sunflower,sunflower,3301060500 +013,oljna buča,Cucurbita pepo var.pepo,Winter squash pumkin,pumpkin_squash_gourd,3301140400 +014,oljna ogrščica (jara),Brassica napus var.napus,Rapeseed spring,spring_rapeseed_rape,3301060402 +017,krmni bob,Vicia faba L. var. minor Harz,Field bean,beans,3301020100 +019,sladkorna pesa,Beta vulgaris L. subsp. vulgaris var. Altissima,Sugar beet,sugar_beet,3301290700 +020,krompir - pozni,Solanum tuberosum L.,potatoes (late),potatoes,3301030000 +022,krompir - zgodnji,Solanum tuberosum L.,potatoes (early),potatoes,3301030000 +024,sirek,Sorghum bicolor (L.) Moench,sorghum,millet_sorghum,3301010900 +025,trda pšenica (jara),Triticum durum Desf.,Durum wheat spring,spring_durum_hard_wheat,3301010202 +026,praha,Fallow land,Fallow land,fallow_land_not_crop,3301110000 +027,konoplja,Canabis sativa var.sativa,Cannabis,hemp_cannabis,3301061000 +028,lan,Linum usitatissimum L.,Flax,flax_linen,3301060701 +029,ukorenišče hmelja,The rooting of hop seedlings,The rooting of hop seedlings,hops,3301060200 +030,soja,Glycine max (L.) Merr.,Soybean,soy_soybeans,3301160000 +031,vrtni mak (jari),Papaver somniferum L. subsp. somniferum,Spring Poppy,summer_poppy,3301060602 +033,krmni grah (jari),Pisum sativum L.,Pea spring,peas,3301020600 +035,pšenica horasan (jara),Triticum turanicum Jakubz.,Khorasan wheat spring,other_cereals,3301019900 +036,riček,Camelina sativa L. Crantz,Camelina,camelina,3301061500 +037,amarant,Amaranthus caudatus L.,Pendant amaranth,amaranth,3301150100 +038,"repa, ki ni namenjena prehrani ljudi",Brassica rapa L. var. rapa (L.) Thell.,Turnip,turnips,3301290800 +049,sladka koruza,Zea mays L. convar. saccharata Koerm.,Sweet corn,grain_maize_corn_popcorn,3301010600 +052,mešanica medovitih rastlin,A mixture of honey plants,A mixture of honey plants,fallow_land_not_crop,3301110000 +053,mešanica medonosnih rastlin z drugimi kmetijskimi rastlinami,A mixture of honey plants with other agricultural plants,A mixture of honey plants with other agricultural plants,fallow_land_not_crop,3301110000 +054,bela gorjušica - medonosna praha,Sinapis alba L.,White mustard ,mustard,3301210100 +055,oljna redkev - medonosna praha,Raphanus sativus L. var. oleiformis Pers.,Oilseed radish,radish,3301290600 +056,facelija - medonosna praha,Phacelia tanacetifolia,Phacelia,phacelia,3301061400 +057,ajda - medonosna praha,Fagopyrum esculentum Moench,Buckwheat,buckwheat,3301150200 +058,sončnice - medonosna praha,Helianthus annus L.,Sunflower,sunflower,3301060500 +100,vinska trta,Vitis vinifera L.,Common grape vine,vineyards_wine_vine_rebland_grapes,3303060000 +101,krmna pesa,Beta vulgaris spp.vulgaris,Beet fodder,mangelwurzel_fodder_beet,3301290400 +102,krmna repa,Brassica rapa L. var. rapa (L.) Thell,Turnip,turnips,3301290800 +103,oljna repica,Brassica rapa L. subsp. campestris,Oilseed rape,rapeseed_rape,3301060400 +104,krmna repica (jara),Brassica rapa L. ssp. sylvestris f. autumnalis,Oilseed rape spring,spring_rapeseed_rape,3301060402 +105,krmni ohrovt,"Brassica oleracea L., convar.: acephala var. medullosa Thell.",Fodder cabbage,other_brassica_oleracea_cabbage,3301210299 +106,krmni radič,Cichorium intybus L. var. sativum DC. Bischoff,Common Chicory,chicory_chicories,3301310200 +107,krmno korenje,Daucus carota,Daucus,carrots_daucus,3301290300 +108,podzemna koleraba,Brassica napus L. var. napobrassica (L.) Rchb.,Swede,swede_rutabaga,3301210500 +109,krmni sirek,Sorghum bicolor (L.) Moench.,Sorghum,millet_sorghum,3301010900 +110,grašica (jara),Vicia sativa L.,Common peas spring,peas,3301020600 +111,bela gorjušica,Sinapis alba L.,White mustard,mustard,3301210100 +112,krmna ogrščica (jara),Brassica napus L. var. napus f. biennis,Spring oilseed rape,spring_rapeseed_rape,3301060402 +113,oljna redkev,Raphanus sativus L. var. oleiformis Pers.,Fodder Radish,radish,3301290600 +114,mešane rastline za krmo na njivah,Other fodder crops on arable land,Other fodder crops on arable land,other_arable_land_crops,3301990000 +116,sudanska trava,Sorghum sudannense P.,Sudan grass,millet_sorghum,3301010900 +129,"mešanice z rastlinami, ki vežejo dušik",Other mixtures with nitrogen-fixing crops,Other mixtures with nitrogen-fixing crops,not_known_and_other,3399000000 +200,trave za pridelavo semena,Grass for seed production,Grass for seed production,temporary_grass,3301090100 +201,trave,Grass on arable land,Grass on arable land,temporary_grass,3301090100 +202,travna ruša (travni tepih),Green cover (grass turf),Green cover (grass turf),sod_turf,3301090207 +203,travnodeteljne mešanice,Grass clover mixture,Grass clover mixture,temporary_grass,3301090100 +204,trajno travinje,Permanent grassland,Permanent grassland,pasture_meadow_grassland_grass,3302000000 +206,deteljnotravne mešanice,Clover grass mixture,Clover grass mixture,clover,3301090303 +207,detelja,Trifolium pratense L.,Red clover,clover,3301090303 +208,lucerna,Medicago sativa L.,Alfalfa,alfalfa_lucerne,3301090301 +210,volčji bob,Lupinus albus L.,White lupin,sweet_lupins,3301020700 +219,facelija,Phacelia tanacetifolia,Lacy phacelia,phacelia,3301061400 +221,perzijska detelja,Trifolium resupinatum L.,Reversed clover,clover,3301090303 +222,inkarnatka,Trifolium incarnatum L.,Crimson clover,clover,3301090303 +333,tehni?no ali drugo sredstvo,Not mantained because of technical obstacle,Not mantained because of technical obstacle,unmaintained,3308000000 +402,zelenjadnice,Mixed vegetables,Mixed vegetables,fresh_vegetables,3301070000 +403,različna trajna zelišča,Mixed permanent herbs,Mixed permanent herbs,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +404,enoletna in dvoletna njivska zelišča,One-year and two-year herbs on fields,One-year herbs on fields,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +405,"mešana raba pod 0,1 ha (zelenjadnice, poljščine, dišavnice in zelišča)","Mixed use (vegetables, crops, aromatic plants and medicinal herbs)","Mixed use (vegetables, crops, aromatic plants and medicinal herbs)",arable_crops,3301000000 +409,"mešane zelenjadnice pod 0,1 ha","Mixed vegetables on field under 0,1 ha size","Mixed vegetables on field under 0,1 ha size",fresh_vegetables,3301070000 +444,pridelava ni v tleh,Production not in the soil,Production not in the soil,not_known_and_other,3399000000 +611,jablana,Malus domestica Borkh.,Apple,apples,3303010200 +612,hruška,Pyrus communis L.,European pear,pears,3303011200 +613,kutina,Cydonia oblonga Mill.,Quince,quinces,3303011500 +614,nashi,Pyrus pyrifolia (Burm.f.) Nakai,Asian pear,pears,3303011200 +615,granatno jabolko,Punica granatum L.,Pomegranate,pomegranate,3303011400 +616,nešplja,Mespilus germanica L.,Common medlar,medlar_loquat,3303010800 +618,žižula,Ziziphus sativa Gaert.,Jujube red date,orchards_fruits,3303010000 +619,feijoa,Feijoa sellowiana,Feijoa,feijoa,3303010500 +621,breskev,Prunus persica BATSCH,Peach,peach,3303011100 +622,nektarina,Prunus persica (L.) Batsch. var. nucipersica (Suckow) Schneid.,Nectarine,nectarine,3303010900 +623,sliva/češplja,Prunus domestica L.,Common plum,plums,3303011300 +624,marelica,Prunus armeniaca L.,Armenian plum,plums,3303011300 +625,češnja,Prunus avium L.,Sweet cherry,cherry_cherries,3303010400 +626,višnja,Prunus cerasus L. (Cerasus vulgaris Mill.),Sour cherry,cherry_cherries,3303010400 +631,oreh,Juglans regia,English walnut,walnuts,3303030600 +632,leska,Corylus avellana,Common hazel,hazelnuts_hazel,3303030200 +633,mandelj,Prunus dulcis (Mill.) D. A. Webb,Almond,almond,3303030100 +634,pekan oreh,Carya illinoensis,Pecan,pecan,3303030300 +642,kivi,Actinidia chinensis Planch. (Actinidia deliciosa),Golden kiwifruit,kiwi,3303010700 +643,kaki,Diospyros kaki L.,Japanese Persimmon,orchards_fruits,3303010000 +644,kostanj,Castanea sativa,Chestnut,sweet_chestnuts,3303030500 +646,bezeg,Sambucus L.,Elderberry,elder_elderberry,3303080400 +647,smokva (figa),Ficus carica L.,Fig,fig,3303010600 +648,asimina,Asimina triloba,Pawpaw,pawpaw,3303011000 +649,rakitovec,Hippophae rhamnoides L.,Seaberry,hippophae_sea_buckthorns_seaberry,3303020800 +651,jagoda,Fragaria L.,Strawberries,strawberries,3301130000 +652,ameriška borovnica,Vaccinium corymbosum L.,American blueberry,blueberry,3303020400 +653,malina,Rubus idaeus L.,Red raspberry,raspberry_raspberries,3303021000 +654,robida,Rubus fruticosus L.,Blackberry,blackberry,3303020200 +655,rdeči ribez,Ribes rubrum L.,Redcurrant,redcurrant,3303021100 +656,črni ribez,Ribes nigrum L.,Blackcurrant,blackcurrant_cassis,3303020300 +657,aronija,Aronia melanocarpa,Black chokeberry,aronia_chokeberries,3303020100 +658,murva,Morus sp.,White mulberry,berries_berry_species,3303020000 +659,goji jagoda,Lycium barbarum L.,Goji berry,shrubberries_shrubs,3303080000 +660,črni ribez x kosmulja,Ribes nidigrolaria,Jostaberry,jostaberry,3303020900 +661,namizno grozdje,Vitis vinifera,Common grape vine,vineyards_wine_vine_rebland_grapes,3303060000 +662,robida x malina,Rubus fruticosus x Rubus idaeus,Tayberry,tayberry,3303021400 +671,limonovec,Citrus limon (L.) Burm. f. lemon,Lemon,citrus_plantations,3303040000 +674,mandarinovec,Citrus reticulata Blanco,Mandarin orange,citrus_plantations,3303040000 +675,dren,Cornus mas L.,Cornelian cherry,dogwood_cornus,3306040000 +676,kosmulja,Ribes uva-crispa L.,European gooseberry,gooseberry_gooseberries_cranberries,3303020700 +677,skorš,Sorbus domestica L.,Sorb tree,other_tree_wood_forest,3306990000 +678,užitno modro kosteničje,Lonicera caerulea var. Kamtschatica,Kamchatka honeysuckle,honeysuckle,3303080500 +680,šipek,Rosa canina L.,Rosa canina,roses,3301083700 +682,šmarna hrušica,Amelanchier spp.,Serviceberry,amelanchier_serviceberry,3303010100 +698,oreh in kostanj,Castanea sativa,Sweet chestnut,sweet_chestnuts,3303030500 +699,mešane sadne vrste,Mixed fruit plants,Mixed fruit plants,orchards_fruits,3303010000 +702,drevesnice,Nurseries,Nurseries,nurseries_nursery,3303070000 +703,šparglji,Asparagus officinalis,Garden asparagus,asparagus,3301200000 +704,trsnice,Vine nurseries,Vine nurseries,vineyards_wine_vine_rebland_grapes,3303070000 +705,"mešane trajne rastline pod 0,1 ha","Mixed permanent crops on field under 0,1 ha size","Mixed permanent crops on field under 0,1 ha size",permanent_crops_perennial,3303000000 +706,"trta za drugo rabo, ki ni vino ali namizno grozdje",Vines for other uses than for wine or fruits,Vines for other uses than for wine or fruits,vineyards_wine_vine_rebland_grapes,3303060000 +707,matičnjak,Root-stock nursery,Root-stock nursery,nurseries_nursery,3303070000 +710,mešane rastline za rejo polžev,Mixed plants for snail farming,Mixed plants for snail farming,other_arable_land_crops,3301990000 +720,"hitro rastoči panjevec (vrba, topol)","Short rotation coppice (willow tree, poplar)","Short rotation coppice (willow tree, poplar)",tree_wood_forest,3306000000 +721,drugi hitro rastoči panjevci,Other short rotation coppices,Other short rotation coppices,tree_wood_forest,3306000000 +722,miskant,Miscanthus,Silvergrass,miscanthus_silvergrass,3301083000 +733,artičoka,Cynara cardunculus var. Scolymus L.,Globe artichocke,artichoke,3301270000 +734,rabarbara,Rheum rhabarbarum L.,Garden rhubarb,rhubarb,3301230000 +735,okrasne rastline,Ornamental plants,Ornamental plants,flowers_ornamental_plants,3301080000 +736,vrtnice,Roses,Roses,roses,3301083700 +737,sivka,Lavandula spica L.,Lavandula,lavender_lavandula,3301061219 +738,ameriški slamnik,Echinacea purpurea (L.) Moench Echinacea angustifolia,Purple coneflower,echinacea_sun_hat,3301081500 +777,površina v odstopu,Crop not known,Crop not known,not_known_and_other,3399000000 +800,oljka,Olea europaea L. v. europaea,Olive,olive_plantations,3303050000 +801,pšenica (ozimna),Triticum L.,Common wheat winter,winter_common_soft_wheat,3301010101 +802,rž (ozimna),Secale cereale L.,Rye winter,winter_rye,3301010301 +803,pira (ozimna),Triticum spelta L.,Spelt winter,winter_spelt,3301011001 +804,krmna repica (ozimna),Brassica rapa L. var. silvestris (Lam.) Briggs,Turnip rape winter,winter_rapeseed_rape,3301060401 +807,tritikala (ozimna),X Triticosecale Wittmack (Triticum x Secale),Triticale winter,winter_triticale,3301010801 +808,oves (ozimni),Avena sativa L.,Winter oats,winter_oats,3301010501 +809,ječmen (ozimni),Hordeum L.,Barley winter,winter_barley,3301010401 +811,mešanice žit (ozimna),Mixture of cereals (winter),Mixture of cereals (winter),winter_unspecified_cereals,3301011501 +812,krmna ogrščica (ozimna),Brassica napus L. var. napus f. biennis,Fodder rape (winter),winter_rapeseed_rape,3301060401 +814,oljna ogrščica (ozimna),Brassica napus L. ssp. oleifera (Metzg.) Sinsk.,Winter rape,winter_rapeseed_rape,3301060401 +821,soržica (ozimna),Meslin (winter),Meslin (winter),winter_meslin,3301011101 +825,trda pšenica (ozimna),Triticum durum Desf.,Durum winter,winter_durum_hard_wheat,3301010201 +831,vrtni mak (ozimni),Papaver somniferum L. subsp. somniferum,Opium poppy winter,winter_poppy,3301060601 +833,krmni grah (ozimni),Pisum sativum L. (partim),Peas winter,peas,3301020600 +835,pšenica horasan (ozimna),Triticum turanicum Jakubz.,Khorasan wheat (winter),other_cereals,3301019900 +900,hmelj,Humulus lupulus L.,Common hop,hops,3301060200 +060,praha brez posevka (prazna površina),Fallow land without crops (empty area),Fallow land without crops (empty area),fallow_land_not_crop,3301110000 +400,"zelenjadnice in zelišča pod 0,1 ha (kombinacija kratkoročne in dolgoročne prisotnosti)",vegetables and herbs under 0.1 ha (combination of short-term and long-term presence),vegetables and herbs under 0.1 ha,fresh_vegetables,3301070000 +901,krajinska značilnost,Landscape feature,Landscape feature,not_known_and_other,3399000000 +063,praha za namen zagotavljanja DKOP 7 in DKOP 8,Fallow land for the purpose of GAEC 7 and GAEC 8.,Fallow land for GAEC 7 and GAEC 8,fallow_land_not_crop,3301110000 +401,"sadike zelenjadnic, zelišč in dišavnic","seedlings of vegetables, herbs and spices","seedlings of vegetables, herbs and spices",nurseries_nursery,3303070000 +701,"drevesnice in trsnice, kjer pridelava ni v tleh",nurseries and Vine nurseries where cultivation is not in the ground,nurseries and Vine nurseries (not in the ground),nurseries_nursery,3303070000 +778,nekmetijska površina,non-agricultural area,non-agricultural area,not_known_and_other,3399000000 +062,zeleni pokrov,Green cover,Green cover,temporary_grass,3301090100 +410,mešani posevki v ekološkem kmetovanju,mixed crops in organic farming,mixed crops in organic farming,arable_crops,3301000000 +061,praha s posevkom brez kmetijske proizvodnje,Fallow land without agricultural production,Fallow land without agricultural production,fallow_land_not_crop,3301110000 +397,"fižol od 0,1 ha",Phaseolus (from 0.1 ha size),Beans (from 0.1 ha),beans,3301020100 +034,rjava indijska gorčica,"Brassica juncea L",Brown mustard,mustard,3301210100 +399,"čičerika od 0,1 ha","Cicer arietinum (from 0.1 ha size)",Chickpeas (from 0.1 ha),chickpeas,3301020200 +723,tobakovec,Nicotiana tabacum L.,Tobacco,other_arable_land_crops,3301990000 +810,grašica (ozimna),Vicia villosa R.,Winter vetch,peas,3301020600 +635,pistacija,Pistacia vera,Pistachio,pistachios,3303030400 +023,kvinoja,Chenopodium quinoa,Quinoa,other_cereals,3301019900 +395,"bob od 0,1 ha","Vicia faba (from 0,1 ha size)",Field bean (from 0.1 ha),beans,3301020100 +220,aleksandrijska detelja,Trifolium alexandrinum L,Egyptian clover,clover,3301090303 +396,"grah od 0,1 ha","Pisum sativum (from 0,1 ha size)",Pea (from 0.1 ha),peas,3301020600 +398,"leča od 0,1 ha","Lens culinaris (from 0,1 ha size)",Lentils (from 0.1 ha),lentils,3301020300 +021,soržica (jara),Meslin,Meslin (spring),spring_meslin,3301011102 diff --git a/tests/data-files/convert/sk/sk.csv b/tests/data-files/convert/sk/sk.csv new file mode 100644 index 00000000..8f0eff8c --- /dev/null +++ b/tests/data-files/convert/sk/sk.csv @@ -0,0 +1,245 @@ +original_name,translated_name,HCAT3_name,HCAT3_code,HCAT2_name,HCAT2_code +Trvalý trávny porast,Permanent grassland,pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Pšenica ozimná,Winter wheat,winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +Mezofilné trvalé trávne porasty (typ B),Mesophilic permanent grassland (type B),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Kukurica,Sweet corn,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Pôda ležiaca úhorom,Fallow land,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Trávy a iné rastlinné krmivá,Grasses and other vegetable fodder,plants_harvested_green,3301090000,pasture_meadow_grassland_grass,3302000000 +Jačmeň jarný,Spring barley,spring_barley,3301010402,spring_barley,3301010402 +Lucerna siata,Lucerne seed,alfalfa_lucerne,3301090301,alfalfa_lucerne,3301090301 +Kapusta repková pravá - ozimná,Rapeseed - winter,winter_rapeseed_rape,3301060401,winter_rapeseed_rape,3301060401 +Sója fazuľová,Soy beans,soy_soybeans,3301160000,soy_soybeans,3301160000 +Slnečnica ročná,Annual sunflower,sunflower,3301060500,sunflower,3301060500 +Zmiešaná plodina,Mixed crop,arable_crops,3301000000,other_arable_land_crops,3301990000 +Pšenica jarná,Spring wheat,spring_common_soft_wheat,3301010102,spring_common_soft_wheat,3301010102 +Jačmeň ozimný,Winter barley,winter_barley,3301010401,winter_barley,3301010401 +Vinohrady,Vineyard,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +Kukurica na siláž,Corn for silage,green_silo_maize,3301090400,green_silo_maize,3301090400 +Ovos siaty,Sown oats,oats,3301010500,oats,3301010500 +Zemiaky konzumné (neskoré),Potatoes for consumption (late),potatoes,3301030000,potatoes,3301030000 +Ďatelina lúčna,Meadow clover,clover,3301090303,clover,3301090303 +Pšenica tvrdá,Durum wheat,durum_hard_wheat,3301010200,durum_hard_wheat,3301010200 +Tritikale,Triticale,triticale,3301010800,triticale,3301010800 +Raž siata,Rye seeds,rye,3301010300,rye,3301010300 +Hrach siaty,Peas,peas,3301020600,peas,3301020600 +Teplomilné a suchomilné trvalé trávne porasty (typ A),Thermophilic and drought-tolerant permanent grassland (type A),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Vlhkomilné porasty nižších polôh (typ D),Moisture-loving vegetation of lower positions (type D),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Zemiaky konzumné (skoré),Potatoes for consumption (early),potatoes,3301030000,potatoes,3301030000 +Horčica biela,White mustard,mustard,3301210100,mustard,3301210100 +Repa cukrová,Sugar beet,sugar_beet,3301290700,sugar_beet,3301290700 +Ovocné sady,Fruit sets,orchards_fruits,3303010000,orchards_fruits,3303010000 +"Vlhkomilné porasty vyšších polôh, slatinné a bezkolencové lúky (typ F)","Moor grasslands of higher altitudes, marshy and kneeless meadows (type F)",pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Zelenina a iné záhradné plodiny voľne pestované,Vegetables and other horticultural crops,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +Facélia vratičolistá,Phacelia wobbly-leaved,phacelia,3301061400,phacelia,3301061400 +Jabloň domáca,Domestic apple tree,apples,3303010200,apples,3303010200 +Ostropestrec mariánsky,Marian thistle,marian_thistles,3301061300,marian_thistles,3301061300 +Cirok,Sorghum,millet_sorghum,3301010900,millet_sorghum,3301010900 +Proso,Millet,millet_sorghum,3301010900,millet_sorghum,3301010900 +Mak siaty,Poppy,poppy,3301060600,poppy,3301060600 +Pšenica špaldová,Spelt wheat,spelt,3301011000,spelt,3301011000 +Ovos nahý,Naked oats,oats,3301010500,oats,3301010500 +Horské kosné lúky (typ C),Mountain mowing meadows (type C),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Tekvica obyčajná (pre produkciu semien na konzum a lisovanie),Pumpkin (for seed production for consumption and pressing),pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +Kukurica cukrová,Sweet corn,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Slivka domáca,Domestic plum,plums,3303011300,plums,3303011300 +Liečivé rastliny,Medicinal plants,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Poľná zelenina,Field vegetables,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +Kapusta hlávková,Cabbage,white_cabbage,3301210212,white_cabbage,3301210212 +Reďkev siata olejná (iná ako na priamy konzum),Oilseed radish (other than for direct human consumption),radish,3301290600,radish,3301290600 +Orech kráľovský,Royal walnut,walnuts,3303030600,walnuts,3303030600 +Jahody,Strawberries,strawberries,3301130000,strawberries,3301130000 +Pohánka,Buckwheat,buckwheat,3301150200,buckwheat,3301150200 +Cirok sudánsky,Sudan sorghum,millet_sorghum,3301010900,millet_sorghum,3301010900 +Ďatelina purpurová,Purple clover,clover,3301090303,clover,3301090303 +Topoľ robusta,Populus robusta,populus,3306070000,populus,3306070000 +Kapusta repková pravá - jarná,Rapeseed - spring,spring_rapeseed_rape,3301060402,spring_rapeseed_rape,3301060402 +Kukurica osivová,Seed corn,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Tekvica obyčajná (pre produkciu na priamy konzum),Pumpkin (for production for direct consumption),pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +Ďatelina plazivá,Creeping clover,clover,3301090303,clover,3301090303 +Hrach siaty kŕmny,Feed peas,peas,3301020600,peas,3301020600 +Cibuľa (jarná),Scallion (spring),scallion,3301220500,onions,3301220400 +Marhuľa obyčajná,Apricot,apricots,3303010300,apricots,3303010300 +Vysokohorské trávne porasty (typ G),Alpine grasslands (type G),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Paprika ročná,Sweet pepper,bell_pepper_paprika,3301300100,piper_pepper,3301061228 +Konopa siata,Cannabis seed,hemp_cannabis,3301061000,hemp_cannabis,3301061000 +Repica olejnatá,Oilseed rape,rapeseed_rape,3301060400,rapeseed_rape,3301060400 +Broskyňa obyčajná,Peach,peach,3303011100,peach,3303011100 +Mrkva obyčajná,Common carrots,carrots_daucus,3301290300,carrots_daucus,3301290300 +Nížinné aluviálne porasty (typ E),Lowland alluvial stands (type E),pasture_meadow_grassland_grass,3302000000,pasture_meadow_grassland_grass,3302000000 +Hruška obyčajná,Common pear,pears,3303011200,pears,3303011200 +Ľan siaty olejný,Oilseed flax,flax_linseed_oil,3301060702,oilseed_crops,3301060800 +Cesnak (zimný),Garlic (winter),garlic,3301220200,garlic,3301220200 +Škôlky s drevnatými rastlinami,Nurseries with woody plants,nurseries_nursery,3303070000,nurseries_nursery,3303070000 +Zemiaky sadbové,Seedling potatoes,potatoes,3301030000,potatoes,3301030000 +Hrach siaty (peluška),Peas (peas),peas,3301020600,peas,3301020600 +Lupina biela,White lupine,sweet_lupins,3301020700,sweet_lupins,3301020700 +Repa obyčajná cviklová (Cvikla),Beetroot (Beetroot),beetroot_beets,3301290200,beetroot_beets,3301290200 +Ďatelina hybridná,Hybrid clover,clover,3301090303,clover,3301090303 +Rajčiak jedlý,Edible tomato,tomato,3301280000,tomato,3301280000 +Petržlen záhradný,Garden parsley,parsley,3301061227,parsley,3301061227 +Čerešňa vtáčia,Bird cherry,cherry_cherries,3303010400,cherry_cherries,3303010400 +Cibuľa (zimná),Scallion,scallion,3301220500,onions,3301220400 +Repa kŕmna,Fodder beet,mangelwurzel_fodder_beet,3301290400,mangelwurzel_fodder_beet,3301290400 +Ríbezľa,Currants,currants,3303020600,currants,3303020600 +Vika siata,Common vetch,vetches,3301090305,vetches,3301090305 +Vičenec vikolistý,Onobrychis viciifolia,onobrychis_sainfoins,3301061600,onobrychis_sainfoins,3301061600 +Dyňa červená,Watermelon,watermelon,3301140500,watermelon,3301140500 +Čučoriedka (Brusnica chocholíkatá),Blueberry (Cranberry),blueberry,3303020400,blueberry,3303020400 +Topoľ maximowiczov,Populus maximowiczii,populus,3306070000,populus,3306070000 +Karfiol,Cauliflower,cauliflower,3301210204,cauliflower,3301210204 +Zelenina a iné záhradné plodiny pod sklom alebo fóliou,Vegetables and other horticultural crops under glass or foil,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +Bôb obyčajný,Common beans,beans,3301020100,beans,3301020100 +Jarabina čierna,black chokeberry,aronia_chokeberries,3303020100,aronia_chokeberries,3303020100 +Tekvica obrovská (pre produkciu na priamy konzum),Giant pumpkin (for production for direct consumption),pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +Lesknica kanárska,Canary grass,canary_seed_canaryseed,3301011400,shrubberries_shrubs,3303080000 +Melón cukrový,Sugar melon,melon,3301140300,melon,3301140300 +Vŕba biela,White willow,willows_osiers,3306080000,willows_osiers,3306080000 +Topoľ čierny,Black poplar,populus,3306070000,populus,3306070000 +Kukurica na zeleno,Corn on the green,green_silo_maize,3301090400,grain_maize_corn_popcorn,3301010600 +Bylinné trvalé plodiny,Herbal permanent crops,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Uhorka nakladačka,Cucumber loader,cucumber_pickle,3301140100,cucumber_pickle,3301140100 +Tekvica obrovská (pre produkciu semien na konzum a lisovanie),Giant pumpkin (for seed production for consumption and pressing),pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +Kvety a okrasné rastliny voľne pestované,Flowers and ornamental plants in the wild,flowers_ornamental_plants,3301080000,flowers_ornamental_plants,3301080000 +Lupina úzkolistá,Narrow-leaved lupine,sweet_lupins,3301020700,sweet_lupins,3301020700 +Ovos ozimný,Winter oats,winter_oats,3301010501,winter_oats,3301010501 +Kel hlávkový,Savoy cabbage,savoy_cabbage,3301210211,savoy_cabbage,3301210211 +Rasca lúčna,Caraway,caraway,3301061211,caraway,3301061211 +Chmeľ obyčajný,Common hops,hops,3301060200,hops,3301060200 +Kaleráb (skorý),Kohlrabi (German turnip) (early),kohlrabi,3301210209,kohlrabi,3301210209 +Hrach siaty pravý stržňový,Peas sown,peas,3301020600,peas,3301020600 +Ostatné aromatické byliny,Other aromatic herbs,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Višňa,Cherry,cherry_cherries,3303010400,cherry_cherries,3303010400 +Rakytník rešetliakovitý,Sea buckthorn,hippophae_sea_buckthorns_seaberry,3303020800,hippophae_sea_buckthorns_seaberry,3303020800 +Zeler voňavý buľvový,Celeriac,celeriac,3301250100,celeriac,3301250100 +Špargľa,Asparagus,asparagus,3301200000,aspargus,3301200000 +Vŕba košikárska,Basket willow,willows_osiers,3306080000,willows_osiers,3306080000 +Ďatelina alexandrijská,Alexandrian clover,clover,3301090303,clover,3301090303 +Reďkev siata pravá (reďkovka),Radish sown (radish),radish,3301290600,radish,3301290600 +Šošovica jedlá,Edible lentils,lentils,3301020500,lentils,3301020500 +Špenát siaty,Spinach seeds,spinach,3301310800,spinach,3301310800 +Malina,Raspberry,raspberry_raspberries,3303021000,raspberry_raspberries,3303021000 +Gaštan jedlý,Edible chestnut,sweet_chestnuts,3303030500,sweet_chestnuts,3303030500 +Vŕba lykovcovitá,Willow,willows_osiers,3306080000,willows_osiers,3306080000 +Medovka lekárska,Lemon balm,lemon_balm_melissa,3301061220,honeydew,3301140200 +Šalát siaty,Lettuce salad,salads_lettuce_leaf_vegetables,3301310000,salads_lettuce_leaf_vegetables,3301310000 +Mäta pieporná,Peppermint,mints_peppermint,3301061222,mints_peppermint,3301061222 +Reďkev siata čierna,Black seed radish,radish,3301290600,radish,3301290600 +Baza čierna,Elderberry,elder_elderberry,3303080400,elder_elderberry,3303080400 +Fazuľa záhradná (obyčajná),Garden beans (common),beans,3301020100,beans,3301020100 +Vika huňatá,Hairy vetch,vetches,3301090305,vetches,3301090305 +Černica,Blackberry,blackberry,3303020200,blackberry,3303020200 +Tabak Virginia,Tobacco Virginia,tobacco,3301060100,tobacco,3301060100 +Rumanček kamilkový,Chamomile,chamomile,3301061213,chamomile,3301061213 +Uhorka šalátová,Cucumber salad,cucumber_pickle,3301140100,cucumber_pickle,3301140100 +Fenikel obyčajný,Fennel,fennel,3301170000,fennel,3301170000 +Ľan siaty priadny,Flax mesh,flax_linseed,3301060700,flax_linseed,3301060700 +Vika panónska,vetches Pannonian,vetches,3301090305,vetches,3301090305 +Koreňová zelenina (ostatná),Root vegetables (other),root_vegetables,3301290000,root_vegetables,3301290000 +Cícer baraní,Chickpeas,chickpeas,3301020200,chickpeas,3301020200 +Mandľa obyčajná,Almond,almond,3303030100,almond,3303030100 +Požlt farbiarsky,Safflower,safflower,3301083900,safflower,3301083900 +Cesnak (jarný),Garlic (spring),garlic,3301220200,garlic,3301220200 +Ľuľok baklažánový (baklažán),Eggplant (eggplant),aubergine_eggplant,3301260000,aubergine_eggplant,3301260000 +Kaleráb (neskorý),Kohlrabi (German turnip) (late),kohlrabi,3301210209,kohlrabi,3301210209 +Slnečnica hľuznatá,Jerusalem artichoke,topinambur_jerusalem_artichoke,3301180000,sunflower,3301060500 +Kukurica pukancová,Popcorn,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Kvety a okrasné rastliny pod sklom alebo fóliou,Flowers and ornamental plants under glass or foil,greenhouse_foil_film,3305000000,greenhouse_foil_film,3305000000 +Topoľ osikový,Aspen poplar,aspen,3306020000,aspen,3306020000 +Rebríček obyčajný,Yarrow,yarrow,3301061239,yarrow,3301061239 +Lieska obyčajná,Hazelnut,hazelnuts_hazel,3303030200,hazelnuts_hazel,3303030200 +Hrachor siaty,Peas,peas,3301020600,peas,3301020600 +Nechtík lekársky,Marigold medical,calendula_marigold,3301061210,calendula_marigold,3301061210 +Koreninové rastliny (ostatné),Spices (other),aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Ruža jabĺčková,Apple rose,rose_hip_rosehip,3303021200,rose_hip_rosehip,3303021200 +Nektárinka,Nectarine,nectarine,3303010900,nectarine,3303010900 +Jelša lepkavá,Sticky alder,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +Bôb konský,broad beans,beans,3301020100,beans,3301020100 +Ďatelina perzská,Persian clover,clover,3301090303,clover,3301090303 +Breza previsnutá,Birch overhanging,birch,3306030000,birch,3306030000 +Sida obojpohlavná,Sida dioecious,sida_virginia_mallow,3304030000,sida_virginia_mallow,3304030000 +Ringlota,Greengage plums,plums,3303011300,plums,3303011300 +Zemolez,Honeysuckle,honeysuckle,3303080500,honeysuckle,3303080500 +Šalvia lekárska,Sage,sage_chia,3301190000,sage_chia,3301190000 +Pór pestovaný (jarný),Leek grown (spring),leek,3301220300,leek,3301220300 +Kôpor voňavý,Fragrant dill,anethum_dill,3301061203,anethum_dill,3301061203 +Brusnica pravá,Cranberry,cranberry,3303020500,cranberry,3303020500 +Paštrnák siaty pravý,Parsnip sown right,parsnips,3301290500,parsnips,3301290500 +Bazalka pravá,Basil,basil,3301061207,basil,3301061207 +Jaseň štíhly,Slender ash tree,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +Povojník batátový (batát),Sweet potato (sweet potato),sweet_potatoes,3301040000,sweet_potatoes,3301040000 +Jarabina vtáčia,rowan,rowan_rowanberries,3303021300,rowan_rowanberries,3303021300 +Kapusta sitinová,Brassica juncea,mustard,3301210100,mustard,3301210100 +Zeler voňavý stonkový,Celery fragrant stalk,celery,3301250000,celery,3301250000 +Drieň obyčajný,Common dogwood,dogwood_cornus,3306040000,dogwood_cornus,3306040000 +Mišpuľa,Loquat,medlar_loquat,3303010800,medlar_loquat,3303010800 +Jelša sivá,Gray alder,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +Pšenica letná ozimná,Winter common wheat,winter_common_soft_wheat,3301010101,winter_common_soft_wheat,3301010101 +Pšenica tvrdá ozimná,Winter durum wheat,winter_durum_hard_wheat,3301010201,winter_durum_hard_wheat,3301010201 +Pôda ležiaca úhorom pre medonosné plodiny,Fallow land for honey plants,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Pôda ležiaca úhorom s porastom,Fallow land with vegetation,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Pšenica letná jarná,Spring common wheat,spring_common_soft_wheat,3301010102,spring_common_soft_wheat,3301010102 +Trávy alebo iné bylinné krmoviny,Grasses or other herbaceous fodder,plants_harvested_green,3301090000,pasture_meadow_grassland_grass,3302000000 +Paprika ročná zeleninová,Annual sweet pepper vegetable,bell_pepper_paprika,3301300100,piper_pepper,3301061228 +Zmiešaná zelenina,Mixed vegetables,fresh_vegetables,3301070000,fresh_vegetables,3301070000 +Biopás pre medonosné plodiny,Biostrip for honey plants,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Pšenica tvrdá jarná,Spring durum wheat,spring_durum_hard_wheat,3301010202,spring_durum_hard_wheat,3301010202 +Tekvica hokkaido,Hokkaido pumpkin,pumpkin_squash_gourd,3301140400,pumpkin_squash_gourd,3301140400 +Biopás,Biostrip,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Zemiaky konzumné (skoré) s podsevom,Potatoes for consumption (early) with undersowing,potatoes,3301030000,potatoes,3301030000 +Biopás tvorený úhorom s porastom,Biostrip consisting of fallow with vegetation,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Biopás kosený po 31. júli,Biostrip mown after July 31,fallow_land_not_crop,3301110000,fallow_land_not_crop,3301110000 +Zatrávnená orná pôda v CHÚ (ekoschéma),Grassed arable land in protected area (eco-scheme),temporary_grass,3301090100,temporary_grass,3301090100 +Líniový vegetačný prvok,Linear vegetation feature,not_known_and_other,3399000000,not_known_and_other,3399000000 +Paprika ročná koreninová,Annual spice pepper,bell_pepper_paprika,3301300100,piper_pepper,3301061228 +None,None,not_known_and_other,3399000000,not_known_and_other,3399000000 +Ďatelina egyptská (alexandrijská),Egyptian clover,clover,3301090303,clover,3301090303 +Strukovinovo-olejná miešanka,Legume-oilseed mixture,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +Nárazníková zóna a medza,Buffer zone and balk,not_known_and_other,3399000000,not_known_and_other,3399000000 +Kukurica s podsevom,Maize with undersowing,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Slnečnica ročná s podsevom,Annual sunflower with undersowing,sunflower,3301060500,sunflower,3301060500 +Levanduľa úzkolistá,Narrow-leaved lavender,lavender_lavandula,3301061219,lavender_lavandula,3301061219 +Vinohrad v reštrukturalizácii,Vineyard under restructuring,vineyards_wine_vine_rebland_grapes,3303060000,vineyards_wine_vine_rebland_grapes,3303060000 +Ostatná manipulačná plocha,Other handling area,not_known_and_other,3399000000,not_known_and_other,3399000000 +Strukovinovo-obilná miešanka,Legume-cereal mixture,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +Olejninovo-obilná miešanka,Oilseed-cereal mixture,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +Cuketa,Zucchini,zucchini_courgette,3301140200,zucchini_courgette,3301140200 +Cirok s podsevom,Sorghum with undersowing,millet_sorghum,3301010900,millet_sorghum,3301010900 +Pamajorán obyčajný (oregano),Oregano,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Šalvia muškátová,Clary sage,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Skorocel kopijovitý,Ribwort plantain,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Raž trváca,Perennial rye,rye,3301010300,rye,3301010300 +Dúška tymiánová ( tymián),Thyme,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Kukurica osivová s podsevom,Seed maize with undersowing,grain_maize_corn_popcorn,3301010600,grain_maize_corn_popcorn,3301010600 +Zmiešaný krajinotvorný sad,Mixed landscape orchard,orchards_fruits,3303010000,orchards_fruits,3303010000 +Zemiaky konzumné (neskoré) s podsevom,Potatoes for consumption (late) with undersowing,potatoes,3301030000,potatoes,3301030000 +Viacročná miešanka bez tráv,Perennial mixture without grasses,other_arable_land_crops,3301990000,other_arable_land_crops,3301990000 +Bylinné políčko,Herb plot,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Kukurica na siláž s podsevom,Silage maize with undersowing,green_silo_maize,3301090400,green_silo_maize,3301090400 +Topoľ biely,White poplar,populus,3306070000,populus,3306070000 +Repa obyčajná cviklová (Cvikla) s podsevom,Beetroot with undersowing,beetroot_beets,3301290200,beetroot_beets,3301290200 +Moruša,Mulberry,berries_berry_species,3303020000,berries_berry_species,3303020000 +Zatrávnená orná pôda (neprojektové opatrenia),Grassed arable land (non-project measures),temporary_grass,3301090100,temporary_grass,3301090100 +Jarabina čierna (Arónia čiernoplodá),Black chokeberry,aronia_chokeberries,3303020100,aronia_chokeberries,3303020100 +Hrab obyčajný,Common hornbeam,other_tree_wood_forest,3306990000,tree_wood_forest,3306000000 +Repa cukrová s podsevom,Sugar beet with undersowing,sugar_beet,3301290700,sugar_beet,3301290700 +Kukurica na zeleno s podsevom,Corn on the green with undersowing,green_silo_maize,3301090400,grain_maize_corn_popcorn,3301010600 +Topoľ kanadský robusta,Canadian poplar robusta,populus,3306070000,populus,3306070000 +Dula,Quince,quinces,3303011500,quinces,3303011500 +Ľubovník bodkovaný,St John's wort,aromatic_medicinal_culinary_plants_spices_herbs,3301061200,aromatic_medicinal_culinary_plants_spices_herbs,3301061200 +Pohánka s podsevom,Buckwheat with undersowing,buckwheat,3301150200,buckwheat,3301150200 +Zemolez kamčatský,Kamchatka honeysuckle,honeysuckle,3303080500,honeysuckle,3303080500 +Zemiaky sadbové s podsevom,Seed potatoes with undersowing,potatoes,3301030000,potatoes,3301030000 +Lupina žltá,Yellow lupine,sweet_lupins,3301020700,sweet_lupins,3301020700 +Repa kŕmna s podsevom,Fodder beet with undersowing,mangelwurzel_fodder_beet,3301290400,mangelwurzel_fodder_beet,3301290400 +Vika siata pre ekoschému,Common vetch for eco-scheme,vetches,3301090305,vetches,3301090305 +Ďatelina purpurová pre ekoschému,Purple clover for eco-scheme,clover,3301090303,clover,3301090303 +Artičoka,Artichoke,artichoke,3301270000,artichoke,3301270000 +Egreš obyčajný,Common gooseberry,gooseberry_gooseberries_cranberries,3303020700,gooseberry_gooseberries_cranberries,3303020700 +Karotka,Carrot,carrots_daucus,3301290300,carrots_daucus,3301290300 +Rebríček kopcový,Mountain yarrow,yarrow,3301061239,yarrow,3301061239 +Pór pestovaný (zimný),Leek grown (winter),leek,3301220300,leek,3301220300 +Echinacea purpurová,Purple coneflower,echinacea_sun_hat,3301081500,echinacea_sun_hat,3301081500 +Čakanka obyčajná šalátová,Chicory (salad),chicory_chicories,3301310200,chicory_chicories,3301310200 +Kapusta repková kvaková (kvaka),Swede (rutabaga),swede_rutabaga,3301210500,swede_rutabaga,3301210500 +Šalotka (jarná),Shallot (spring),onions,3301220400,onions,3301220400 +Kapusta hlávková s podsevom,Head cabbage with undersowing,white_cabbage,3301210212,white_cabbage,3301210212 \ No newline at end of file diff --git a/tests/data-files/convert/us_ca_scm/scm.csv b/tests/data-files/convert/us_ca_scm/scm.csv new file mode 100644 index 00000000..87bdf1ff --- /dev/null +++ b/tests/data-files/convert/us_ca_scm/scm.csv @@ -0,0 +1,60 @@ +original_name,original_code,color,translated_name,HCAT3_name,HCAT3_code +"****",****,#44aae6,"****",not_known_and_other,3399000000 +"Citrus and Subtropical - No Subclass",C,#cb1469,"Citrus and Subtropical - No Subclass",citrus_plantations,3303040000 +"Eucalyptus",C10,#410cc9,"Eucalyptus",eucalyptus,3306050000 +"Dates",C4,#43ce34,"Dates",orchards_fruits,3303010000 +"Avocados",C5,#86cb3c,"Avocados",avocado,3303100000 +"Olives",C6,#d84d72,"Olives",olive_plantations,3303050000 +"Subtropical Fruits Misc.",C7,#5eda3f,"Subtropical Fruits Misc.",unspecified_orchards_fruits,3303019800 +"Kiwis",C8,#d18274,"Kiwis",kiwi,3303010700 +"Apples",D1,#d4886f,"Apples",apples,3303010200 +"Deciduous - Misc.",D10,#9f54d1,"Deciduous - Misc.",unspecified_orchards_fruits,3303019800 +"Almonds",D12,#dfad84,"Almonds",almond,3303030100 +"Walnuts",D13,#665aea,"Walnuts",walnuts,3303030600 +"Pistachios",D14,#b063d1,"Pistachios",pistachio,3303030400 +"Pomegranates",D15,#cf8429,"Pomegranates",pomegranate,3303011400 +"Pecans",D17,#0fe769,"Pecans",pecan,3303030300 +"Apricots",D2,#1546e7,"Apricots",apricots,3303010300 +"Cherries",D3,#31e4a2,"Cherries",cherry_cherries,3303010400 +"Peaches and Nectarines",D5,#b5db77,"Peaches and Nectarines",unspecified_orchards_fruits,3303019800 +"Pears",D6,#ba84ef,"Pears",pears,3303011200 +"Plums",D7,#ebe41d,"Plums",plums,3303011300 +"Prunes",D8,#6649e9,"Prunes",plums,3303011300 +"Cotton",F1,#6ee185,"Cotton",cotton,3301060300 +"Beans (dry)",F10,#e4777e,"Beans (dry)",beans,3301020100 +"Field Misc.",F11,#dcea13,"Field Misc.",other_arable_land_crops,3301990000 +"Sunflowers",F12,#86d3ec,"Sunflowers",sunflower,3301060500 +"Corn, Sorghum or Sudan (grouped for remote sensing classification only)",F16,#0e55e3,"Corn, Sorghum or Sudan (grouped for remote sensing classification only)",cereal,3301010000 +"Safflower",F2,#26cd5b,"Safflower",safflower,3301083900 +"Sugar beets",F5,#b5d32f,"Sugar beets",sugar_beet,3301290700 +"Wheat",G2,#7ad3cf,"Wheat",common_soft_wheat,3301010100 +"Grain and Hay - Misc.",G6,#69cdbe,"Grain and Hay - Misc.",other_arable_land_crops,3301990000 +"Idle - Land not cropped in current or prior year, but within last 3 yrs.",I1,#1e38e1,"Idle - Land not cropped in current or prior year, but within last 3 yrs.",fallow_land_not_crop,3301110000 +"Idle - Long Term - land consistently idle for four or more years",I4,#6aade8,"Idle - Long Term - land consistently idle for four or more years",unmaintained,3308000000 +"Alfalfa and alfalfa mixtures",P1,#e1b96a,"Alfalfa and alfalfa mixtures",alfalfa_lucerne,3301090301 +"Pasture - Mixed",P3,#7a3cde,"Pasture - Mixed",pasture_meadow_grassland_grass,3302000000 +"Pasture - Native Improved",P4,#de3853,"Pasture - Native Improved",pasture_meadow_grassland_grass,3302000000 +"Pasture - Induced High Water",P5,#ed2d26,"Pasture - Induced High Water",pasture_meadow_grassland_grass,3302000000 +"Pasture - Miscellaneous Grasses",P6,#61eb26,"Pasture - Miscellaneous Grasses",pasture_meadow_grassland_grass,3302000000 +"Pasture - Turf Farms",P7,#5b99e9,"Pasture - Turf Farms",sod_turf,3301090207 +"Rice",R1,#c872b5,"Rice",rice,3301010700 +"Rice - Wild",R2,#f035a2,"Rice - Wild",rice,3301010700 +"Onions and Garlic",T10,#4eec5e,"Onions and Garlic",alliums,3301220000 +"Potatoes",T12,#e7da85,"Potatoes",potatoes,3301030000 +"Sweet Potatoes",T13,#ef6ec6,"Sweet Potatoes",sweet_potatoes,3301040000 +"Flowers, nursery and Christmas Tree Farms",T16,#6264e5,"Flowers, nursery and Christmas Tree Farms",flowers_ornamental_plants,3301080000 +"Truck Crops - Misc.",T18,#e95ad8,"Truck Crops - Misc.",fresh_vegetables,3301070000 +"Bushberries",T19,#96d26c,"Bushberries",unspecified_berries_berry_species,3303029800 +"Strawberries",T20,#d116e6,"Strawberries",strawberries,3301130000 +"Peppers (Chili, Bell, etc.)",T21,#73cbda,"Peppers (Chili, Bell, etc.)",capsicum,3301300000 +"Greenhouse",T27,#ab1ccf,"Greenhouse",greenhouse_foil_film,3305000000 +"Lettuce or Leafy Greens (grouped for remote sensing classification only)",T30,#47d047,"Lettuce or Leafy Greens (grouped for remote sensing classification only)",salads_lettuce_leaf_vegetables,3301310000 +"Tomatoes (all)",T32,#de4980,"Tomatoes (all)",tomato,3301280000 +"Cole crops (mixture of T22-T25)",T4,#31db89,"Cole crops (mixture of T22-T25)",brassicaceae_cruciferae,3301210000 +"Carrots",T6,#cb19c8,"Carrots",carrots_daucus,3301290300 +"Melons, Squash, and Cucumbers",T9,#bdd879,"Melons, Squash, and Cucumbers",cucurbits,3301140000 +"Vineyards - No Subclass",V,#ebcc65,"Vineyards - No Subclass",vineyards_wine_vine_rebland_grapes,3303060000 +"Not cropped, or unclassified at the time of remote-sensing analysis. Idle status not determined",X,#4debc1,"Not cropped, or unclassified at the time of remote-sensing analysis. Idle status not determined",not_known_and_other,3399000000 +"Young Perennial (grouped for remote sensing or when CLASS C, D or V is not determined)",YP,#ef8c57,"Young Perennial (grouped for remote sensing or when CLASS C, D or V is not determined)",permanent_crops_perennial,3303000000 +"Unclassified",U,#4debc1,"Unclassified",not_known_and_other,3399000000 +"Urban Landscape",UL2,#4debc1,"Urban Landscape",not_known_and_other,3399000000 From 5463f6c31baffb7609bdec023dae8a84c983bd94 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 15:43:09 +0200 Subject: [PATCH 62/94] =?UTF-8?q?NL:=202020=20edition=20is=20a=20gpkg,=20a?= =?UTF-8?q?nd=202009=20(zip)=20exists=20=E2=80=94=20fix=20the=20variant=20?= =?UTF-8?q?ranges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/nl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/datasets/nl.py b/fiboa_cli/datasets/nl.py index 5388b7d0..f012dd68 100644 --- a/fiboa_cli/datasets/nl.py +++ b/fiboa_cli/datasets/nl.py @@ -13,8 +13,8 @@ class NLCropConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): area_calculate_missing = True variants = { "2026": f"{base}/gewaspercelen_concept_2026.gpkg", - **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.gpkg" for y in range(2025, 2020, -1)}, - **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.zip" for y in range(2020, 2009, -1)}, + **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.gpkg" for y in range(2025, 2019, -1)}, + **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.zip" for y in range(2019, 2008, -1)}, } id = "nl" From a2b91fc6f068aca68d395c7fba339711733760e7 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 16:11:37 +0200 Subject: [PATCH 63/94] NL: the 2009-2019 zips contain a FileGDB; extract and glob it Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/nl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/nl.py b/fiboa_cli/datasets/nl.py index f012dd68..ee530ead 100644 --- a/fiboa_cli/datasets/nl.py +++ b/fiboa_cli/datasets/nl.py @@ -14,7 +14,11 @@ class NLCropConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): variants = { "2026": f"{base}/gewaspercelen_concept_2026.gpkg", **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.gpkg" for y in range(2025, 2019, -1)}, - **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.zip" for y in range(2019, 2008, -1)}, + # the zip editions each contain one FileGDB (naming varies per year) + **{ + str(y): {f"{base}/brpgewaspercelen_definitief_{y}.zip": ["*.gdb"]} + for y in range(2019, 2008, -1) + }, } id = "nl" From a201a988b58f0313ed9b1e7ee9240621fa1c61ca Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 16:21:26 +0200 Subject: [PATCH 64/94] NL: map the 2009-2019 FileGDB column names; year from the variant Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/nl.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fiboa_cli/datasets/nl.py b/fiboa_cli/datasets/nl.py index ee530ead..42bd0ba7 100644 --- a/fiboa_cli/datasets/nl.py +++ b/fiboa_cli/datasets/nl.py @@ -54,6 +54,21 @@ class NLCropConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): "jaar": "determination:datetime", } + def migrate(self, gdf): + if "GWS_GEWASCODE" in gdf.columns: + # 2009-2019 FileGDB editions: prefixed names, no year column, and + # only a m2 shape area (left unmapped; area_calculate_missing + # derives metrics:area from the geometry instead) + gdf = gdf.rename( + columns={ + "GWS_GEWASCODE": "gewascode", + "GWS_GEWAS": "gewas", + "CAT_GEWASCATEGORIE": "category", + } + ) + gdf["jaar"] = int(self.variant) + return super().migrate(gdf) + column_filters = { # category = "Grasland" | "Bouwland" | "Sloot" | "Landschapselement" "category": lambda col: col.isin(["Grasland", "Bouwland"]) From b154cf4f87c6e7530f9100ab882d22160f1d8cb8 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 18:48:03 +0200 Subject: [PATCH 65/94] Drop rows without an id like rows without a crop code (bounded at 1%) Old dk editions carry a handful of rows with a null Marknr; the id column is non-nullable, so the write failed after a full conversion. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/fiboa_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index 43e3870d..3d7016c5 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -6,7 +6,7 @@ AREA_KEY = "metrics:area" # Properties that a schema requires to be non-null; rows lacking them cannot # validate, so they are dropped (with a warning) rather than failing the run. -REQUIRED_NON_NULL = ("crop:code",) +REQUIRED_NON_NULL = ("id", "crop:code") class FiboaBaseConverter(BaseConverter): From 7d5d9aa5e28e975b1ce07a4f0805f33e843fc827 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 18:57:45 +0200 Subject: [PATCH 66/94] DK: the 2008/2009 editions have no crop columns; convert them as boundaries Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/dk.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/dk.py b/fiboa_cli/datasets/dk.py index 2ff311cd..0cf4e7d6 100644 --- a/fiboa_cli/datasets/dk.py +++ b/fiboa_cli/datasets/dk.py @@ -28,5 +28,7 @@ class DKConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): use_variant_as_determination = True def migrate(self, gdf) -> gpd.GeoDataFrame: - gdf["Afgkode"] = gdf["Afgkode"].astype(float).fillna(value=0).astype(int).astype(str) + if "Afgkode" in gdf.columns: + gdf["Afgkode"] = gdf["Afgkode"].astype(float).fillna(value=0).astype(int).astype(str) + # the 2008 and 2009 editions carry no crop columns (boundaries only) return super().migrate(gdf) From 10a8cbfac6bbdc5ce4754c2db8dd034487276dcc Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 19:03:01 +0200 Subject: [PATCH 67/94] HCAT: skip mapping when an edition has no crop columns at all Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/commons/hcat.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fiboa_cli/datasets/commons/hcat.py b/fiboa_cli/datasets/commons/hcat.py index 997ce32d..4794097b 100644 --- a/fiboa_cli/datasets/commons/hcat.py +++ b/fiboa_cli/datasets/commons/hcat.py @@ -53,6 +53,10 @@ def add_hcat(self, gdf): # Lookup column that will be renamed after the migration to hcat:code hcat_code_column = next(k for k, v in self.hcat_columns.items() if v == "hcat:code") if hcat_code_column not in gdf.columns: + code_sources = [k for k, v in self.columns.items() if v in ("crop:code", "crop:name")] + if not any(k in gdf.columns for k in code_sources): + # this edition carries no crop columns at all: nothing to map + return gdf # Add HCAT columns based on crop-columns # Map to HCAT categories by using the mapping from the csv file From 835cebd5813d93b80b9c0db87176d4518c8e71e9 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 19:48:04 +0200 Subject: [PATCH 68/94] Canonical Hilbert ordering for every output; publish repairs in place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpio check flagged published files as spatially unordered. Measured from the published footers: the plain writer yields mediocre locality, and the DuckDB converter's 1-arg ST_Hilbert is meaningless without bounds (jp row groups span 38% of Japan). The per-file merge sort measures excellent (groups 0.1% of extent). One sort implementation now serves every path: _ensure_hilbert_sorted (cheap bbox-only sortedness probe, 50k-row groups) runs at the end of the DuckDB converter and in fiboa publish — re-running publish over an existing parquet doubles as the repair tool for published data. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/duckdb.py | 32 +++++++++++++++++++++---- fiboa_cli/conversion/per_file.py | 13 +++++++--- fiboa_cli/publish.py | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 55347c62..5748ecc7 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -67,14 +67,11 @@ def convert( ) selections = [] - geom_column = None for k, v in self.columns.items(): if k in self.column_migrations: selections.append(f'{self.column_migrations.get(k)} as "{v}"') else: selections.append(f'"{k}" as "{v}"') - if v == "geometry": - geom_column = k selection = ", ".join(selections) filters = [] @@ -111,15 +108,19 @@ def convert( con = duckdb.connect() con.install_extension("spatial") con.load_extension("spatial") + # No ORDER BY here: ST_Hilbert without bounds is meaningless (whole + # countries collapse into a handful of cells), and with bounds it uses + # a different reference grid than the rest of the pipeline. The + # canonical in-place Hilbert sort below runs after post-processing. con.execute( f""" COPY ( SELECT {selection} FROM read_parquet({sources}, union_by_name=true) {where} - ORDER BY ST_Hilbert({geom_column}) ) TO ? ( FORMAT parquet, + ROW_GROUP_SIZE 50_000, compression ?, KV_METADATA {{ collection: ?, @@ -219,4 +220,27 @@ def convert( except Exception as e: self.warning(f"GeoParquet 1.1 post-processing failed: {e}") + # canonical spatial ordering, same grid as the per-file merge + try: + from vecorel_cli.vecorel.hilbert import crs_total_bounds + except ImportError: + from .hilbert import crs_total_bounds + from .per_file import _ensure_hilbert_sorted + + with pq.ParquetFile(output_file) as pf: + meta = pf.schema_arrow.metadata or {} + if b"geo" in meta: + geo = json.loads(meta[b"geo"]) + primary = geo["primary_column"] + crs = geo["columns"][primary].get("crs") or "EPSG:4326" + if _ensure_hilbert_sorted( + output_file, + primary, + crs_total_bounds(crs), + compression, + None, + row_group_size=50_000, + ): + self.info("Sorted output into Hilbert order") + return output_file diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index e504269a..68351387 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -271,6 +271,7 @@ def _ensure_hilbert_sorted( total_bounds, compression: str, compression_level: Optional[int], + row_group_size: Optional[int] = None, ) -> bool: """If ``path`` is already Hilbert-sorted against ``total_bounds``, leave it untouched and return False. Otherwise sort it in place and return True. @@ -279,20 +280,26 @@ def _ensure_hilbert_sorted( is bounded by a single source partition (much smaller than the merged dataset). Schema metadata (``geo``, collection JSON, etc.) is preserved. """ + # cheap check first: the keys only need the bbox covering column with pq.ParquetFile(path) as pf: - table = pf.read() - metadata = pf.schema_arrow.metadata - hilberts = _hilbert_keys_for_table(table, primary_col, total_bounds) + has_bbox = "bbox" in pf.schema_arrow.names + probe = pf.read(columns=["bbox"]) if has_bbox else pf.read() + hilberts = _hilbert_keys_for_table(probe, primary_col, total_bounds) # NB: hilberts is uint64; never use np.diff for monotonicity here — uint # underflow makes any descent wrap to a huge positive and fool the check. if hilberts.size <= 1 or bool(np.all(hilberts[1:] >= hilberts[:-1])): return False + with pq.ParquetFile(path) as pf: + table = pf.read() + metadata = pf.schema_arrow.metadata order = np.argsort(hilberts, kind="stable") sorted_table = table.take(pa.array(order)) sorted_table = sorted_table.replace_schema_metadata(metadata) write_kwargs = {"compression": compression} if compression_level is not None: write_kwargs["compression_level"] = compression_level + if row_group_size is not None: + write_kwargs["row_group_size"] = row_group_size pq.write_table(sorted_table, path, **write_kwargs) return True diff --git a/fiboa_cli/publish.py b/fiboa_cli/publish.py index 4c25f87e..9dced76e 100644 --- a/fiboa_cli/publish.py +++ b/fiboa_cli/publish.py @@ -126,6 +126,8 @@ def publish( else: self.success(f"Using existing file {parquet_file}") + self.ensure_spatial_order(parquet_file) + # Validate parquet file, we only want to publish valid files self.info(f"Validating {parquet_file}") ValidateData().validate(parquet_file, num=-1) @@ -199,6 +201,45 @@ def file_metadata(path: Path) -> dict: "file:checksum": multihash_sha256(path), } + # ~50k rows per group: inside gpio's spatial-query sweet spot, and with a + # Hilbert order every small group is finer bbox-skipping granularity + ROW_GROUP_SIZE = 50_000 + + def ensure_spatial_order(self, parquet_file: Path): + """Hilbert-sort the file in place unless it already is sorted. + + Every write path ends up spatially ordered regardless of converter + (plain pandas, per-file merge, DuckDB), and re-running publish over an + existing parquet doubles as the repair tool for published data.""" + import json as _json + + import pyarrow.parquet as _pq + + from .conversion.per_file import _ensure_hilbert_sorted + + try: + from vecorel_cli.vecorel.hilbert import crs_total_bounds + except ImportError: + from .conversion.hilbert import crs_total_bounds + + with _pq.ParquetFile(parquet_file) as pf: + meta = pf.schema_arrow.metadata or {} + if b"geo" not in meta: + self.warning(f"{parquet_file} has no geo metadata; skipping spatial ordering") + return + geo = _json.loads(meta[b"geo"]) + primary = geo["primary_column"] + crs = geo["columns"][primary].get("crs") or "EPSG:4326" + if _ensure_hilbert_sorted( + str(parquet_file), + primary, + crs_total_bounds(crs), + "zstd", + None, + row_group_size=self.ROW_GROUP_SIZE, + ): + self.success(f"Re-sorted {parquet_file} into Hilbert order") + def generate_pmtiles(self, parquet_file: Path, pmtiles_file: Path, tippecanoe_opts: str): if is_windows: self.warning( From 6bf0a665079e0b92bf96afd90ce769b641f15801 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Fri, 28 Aug 2026 20:08:56 +0200 Subject: [PATCH 69/94] Widen binary/string offsets before the in-place Hilbert take take() concatenates chunks; int32 offsets overflow past 2 GB of WKB (us/jp editions). Parquet's BYTE_ARRAY is identical for large_binary. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/per_file.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 68351387..9aa57f07 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -292,6 +292,22 @@ def _ensure_hilbert_sorted( with pq.ParquetFile(path) as pf: table = pf.read() metadata = pf.schema_arrow.metadata + # int32 offsets of plain binary/string columns overflow when a take() + # concatenates >2 GB of chunks (large WKB columns); widen them first. + # Parquet's physical BYTE_ARRAY is identical either way. + fields = [] + widened = False + for f in table.schema: + if pa.types.is_binary(f.type): + fields.append(f.with_type(pa.large_binary())) + widened = True + elif pa.types.is_string(f.type): + fields.append(f.with_type(pa.large_string())) + widened = True + else: + fields.append(f) + if widened: + table = table.cast(pa.schema(fields, metadata=table.schema.metadata)) order = np.argsort(hilberts, kind="stable") sorted_table = table.take(pa.array(order)) sorted_table = sorted_table.replace_schema_metadata(metadata) From eeceb86cf5ad25f395fbdbad21c2393472e17ba7 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sat, 29 Aug 2026 11:16:37 +0200 Subject: [PATCH 70/94] DuckDB converter: declare the appended bbox column as geo covering Engines only use the bbox column for pushdown when the geo metadata declares it; the post-processing appended the column without the covering entry (visible on the published jp editions). Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/duckdb.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 5748ecc7..7e770f0f 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -150,9 +150,21 @@ def convert( # Update for version 1.1.0 metadata = existing_schema.metadata + add_covering = geoparquet_version > "1.0.0" and "bbox" not in col_names if geoparquet_version > "1.0.0": geo_meta = json.loads(existing_schema.metadata[b"geo"]) geo_meta["version"] = geoparquet_version + if add_covering: + # declare the appended bbox column so engines use it + primary = geo_meta.get("primary_column", "geometry") + geo_meta["columns"][primary]["covering"] = { + "bbox": { + "xmin": ["bbox", "xmin"], + "ymin": ["bbox", "ymin"], + "xmax": ["bbox", "xmax"], + "ymax": ["bbox", "ymax"], + } + } metadata[b"geo"] = json.dumps(geo_meta).encode("utf-8") # Build a new Arrow schema with adjusted nullability From c5742c0696db99c25980590c61857a95e2668cef Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 30 Aug 2026 00:20:27 +0200 Subject: [PATCH 71/94] Adopt vecorel-cli 0.2.16: upstream Hilbert sorting, drop the vendored fallbacks vecorel/cli#23 is merged and released, so the converter output arrives pre-sorted; fiboa_cli/conversion/hilbert.py and the try/except fallback imports are gone. The in-place resort now writes without the embedded arrow schema: the large_binary widening used for >2GB takes must not leak into the file, or a rewritten part stops schema-matching untouched pre-sorted siblings during the streaming merge (masked before 0.2.16, when every part needed rewriting). Co-Authored-By: Claude Fable 5 --- build/lib/fiboa_cli/__init__.py | 5 + build/lib/fiboa_cli/cli/__init__.py | 0 build/lib/fiboa_cli/cli/setup.py | 3 + build/lib/fiboa_cli/conversion/__init__.py | 0 build/lib/fiboa_cli/conversion/convert_gml.py | 34 ++ .../fiboa_cli/conversion/converter_rest.py | 100 +++++ build/lib/fiboa_cli/conversion/duckdb.py | 258 +++++++++++ .../fiboa_cli/conversion/fiboa_converter.py | 69 +++ .../lib/fiboa_cli}/conversion/hilbert.py | 0 build/lib/fiboa_cli/conversion/per_file.py | 424 ++++++++++++++++++ build/lib/fiboa_cli/convert.py | 5 + build/lib/fiboa_cli/converters.py | 5 + build/lib/fiboa_cli/create_geojson.py | 5 + build/lib/fiboa_cli/create_geoparquet.py | 5 + build/lib/fiboa_cli/create_jsonschema.py | 5 + build/lib/fiboa_cli/create_stac.py | 47 ++ build/lib/fiboa_cli/datasets/__init__.py | 0 build/lib/fiboa_cli/datasets/ai4sf.py | 122 +++++ build/lib/fiboa_cli/datasets/at.py | 43 ++ build/lib/fiboa_cli/datasets/at_block.py | 44 ++ build/lib/fiboa_cli/datasets/be_vlg.py | 68 +++ build/lib/fiboa_cli/datasets/be_wal.py | 65 +++ build/lib/fiboa_cli/datasets/bg.py | 39 ++ build/lib/fiboa_cli/datasets/br_ba_lem.py | 95 ++++ build/lib/fiboa_cli/datasets/br_conab.py | 107 +++++ build/lib/fiboa_cli/datasets/ch.py | 47 ++ build/lib/fiboa_cli/datasets/commons/data.py | 8 + build/lib/fiboa_cli/datasets/commons/ec.py | 31 ++ .../fiboa_cli/datasets/commons/euro_land.py | 57 +++ build/lib/fiboa_cli/datasets/commons/hcat.py | 125 ++++++ build/lib/fiboa_cli/datasets/cz.py | 47 ++ build/lib/fiboa_cli/datasets/de_bb.py | 36 ++ build/lib/fiboa_cli/datasets/de_bb_block.py | 42 ++ build/lib/fiboa_cli/datasets/de_by.py | 41 ++ build/lib/fiboa_cli/datasets/de_mv.py | 79 ++++ build/lib/fiboa_cli/datasets/de_nds.py | 62 +++ build/lib/fiboa_cli/datasets/de_nds_block.py | 33 ++ build/lib/fiboa_cli/datasets/de_nrw.py | 29 ++ build/lib/fiboa_cli/datasets/de_sax.py | 70 +++ build/lib/fiboa_cli/datasets/de_sh.py | 28 ++ build/lib/fiboa_cli/datasets/de_sl.py | 57 +++ build/lib/fiboa_cli/datasets/de_th.py | 107 +++++ build/lib/fiboa_cli/datasets/digifarm.py | 31 ++ build/lib/fiboa_cli/datasets/dk.py | 34 ++ build/lib/fiboa_cli/datasets/ec_be_vlg.py | 15 + build/lib/fiboa_cli/datasets/ec_ee.py | 57 +++ build/lib/fiboa_cli/datasets/ec_lt.py | 50 +++ build/lib/fiboa_cli/datasets/ec_lv.py | 59 +++ build/lib/fiboa_cli/datasets/ec_nl_crop.py | 9 + build/lib/fiboa_cli/datasets/ec_ro.py | 43 ++ build/lib/fiboa_cli/datasets/ec_si.py | 47 ++ build/lib/fiboa_cli/datasets/ee.py | 40 ++ build/lib/fiboa_cli/datasets/es.py | 114 +++++ build/lib/fiboa_cli/datasets/es_an.py | 77 ++++ build/lib/fiboa_cli/datasets/es_ar.py | 106 +++++ build/lib/fiboa_cli/datasets/es_base.py | 50 +++ build/lib/fiboa_cli/datasets/es_cat.py | 80 ++++ build/lib/fiboa_cli/datasets/es_cb.py | 52 +++ build/lib/fiboa_cli/datasets/es_cl.py | 67 +++ build/lib/fiboa_cli/datasets/es_cm.py | 56 +++ build/lib/fiboa_cli/datasets/es_cn.py | 63 +++ build/lib/fiboa_cli/datasets/es_ex.py | 70 +++ build/lib/fiboa_cli/datasets/es_ga.py | 47 ++ build/lib/fiboa_cli/datasets/es_ib.py | 65 +++ build/lib/fiboa_cli/datasets/es_md.py | 30 ++ build/lib/fiboa_cli/datasets/es_nc.py | 68 +++ build/lib/fiboa_cli/datasets/es_pv.py | 62 +++ build/lib/fiboa_cli/datasets/es_vc.py | 54 +++ build/lib/fiboa_cli/datasets/fi.py | 43 ++ build/lib/fiboa_cli/datasets/fr.py | 122 +++++ build/lib/fiboa_cli/datasets/hr.py | 108 +++++ build/lib/fiboa_cli/datasets/ie.py | 64 +++ build/lib/fiboa_cli/datasets/india_10k.py | 29 ++ build/lib/fiboa_cli/datasets/it_1.py | 52 +++ build/lib/fiboa_cli/datasets/jecam.py | 77 ++++ build/lib/fiboa_cli/datasets/jp.py | 43 ++ build/lib/fiboa_cli/datasets/lacuna_labels.py | 60 +++ build/lib/fiboa_cli/datasets/lt.py | 13 + build/lib/fiboa_cli/datasets/lu.py | 25 ++ build/lib/fiboa_cli/datasets/lv.py | 53 +++ build/lib/fiboa_cli/datasets/nl.py | 89 ++++ build/lib/fiboa_cli/datasets/nl_block.py | 46 ++ build/lib/fiboa_cli/datasets/nz.py | 80 ++++ build/lib/fiboa_cli/datasets/planet_afb.py | 51 +++ build/lib/fiboa_cli/datasets/pt.py | 53 +++ build/lib/fiboa_cli/datasets/se.py | 47 ++ build/lib/fiboa_cli/datasets/si.py | 42 ++ build/lib/fiboa_cli/datasets/sk.py | 54 +++ build/lib/fiboa_cli/datasets/template.py | 123 +++++ build/lib/fiboa_cli/datasets/us_ca_scm.py | 62 +++ .../fiboa_cli/datasets/us_usda_cropland.py | 78 ++++ build/lib/fiboa_cli/datasets/varda.py | 34 ++ build/lib/fiboa_cli/describe.py | 30 ++ build/lib/fiboa_cli/fiboa/version.py | 28 ++ build/lib/fiboa_cli/improve.py | 161 +++++++ build/lib/fiboa_cli/merge.py | 5 + build/lib/fiboa_cli/publish.py | 289 ++++++++++++ build/lib/fiboa_cli/registry.py | 66 +++ build/lib/fiboa_cli/rename_extension.py | 13 + build/lib/fiboa_cli/validate.py | 5 + build/lib/fiboa_cli/validate_schema.py | 5 + coverage.json | 1 + fiboa_cli/conversion/duckdb.py | 6 +- fiboa_cli/conversion/per_file.py | 17 +- fiboa_cli/publish.py | 6 +- pixi.lock | 48 +- pyproject.toml | 2 +- tests/test_per_file.py | 2 +- 108 files changed, 5982 insertions(+), 43 deletions(-) create mode 100644 build/lib/fiboa_cli/__init__.py create mode 100644 build/lib/fiboa_cli/cli/__init__.py create mode 100644 build/lib/fiboa_cli/cli/setup.py create mode 100644 build/lib/fiboa_cli/conversion/__init__.py create mode 100644 build/lib/fiboa_cli/conversion/convert_gml.py create mode 100644 build/lib/fiboa_cli/conversion/converter_rest.py create mode 100644 build/lib/fiboa_cli/conversion/duckdb.py create mode 100644 build/lib/fiboa_cli/conversion/fiboa_converter.py rename {fiboa_cli => build/lib/fiboa_cli}/conversion/hilbert.py (100%) create mode 100644 build/lib/fiboa_cli/conversion/per_file.py create mode 100644 build/lib/fiboa_cli/convert.py create mode 100644 build/lib/fiboa_cli/converters.py create mode 100644 build/lib/fiboa_cli/create_geojson.py create mode 100644 build/lib/fiboa_cli/create_geoparquet.py create mode 100644 build/lib/fiboa_cli/create_jsonschema.py create mode 100644 build/lib/fiboa_cli/create_stac.py create mode 100644 build/lib/fiboa_cli/datasets/__init__.py create mode 100644 build/lib/fiboa_cli/datasets/ai4sf.py create mode 100644 build/lib/fiboa_cli/datasets/at.py create mode 100644 build/lib/fiboa_cli/datasets/at_block.py create mode 100644 build/lib/fiboa_cli/datasets/be_vlg.py create mode 100644 build/lib/fiboa_cli/datasets/be_wal.py create mode 100644 build/lib/fiboa_cli/datasets/bg.py create mode 100644 build/lib/fiboa_cli/datasets/br_ba_lem.py create mode 100644 build/lib/fiboa_cli/datasets/br_conab.py create mode 100644 build/lib/fiboa_cli/datasets/ch.py create mode 100644 build/lib/fiboa_cli/datasets/commons/data.py create mode 100644 build/lib/fiboa_cli/datasets/commons/ec.py create mode 100644 build/lib/fiboa_cli/datasets/commons/euro_land.py create mode 100644 build/lib/fiboa_cli/datasets/commons/hcat.py create mode 100644 build/lib/fiboa_cli/datasets/cz.py create mode 100644 build/lib/fiboa_cli/datasets/de_bb.py create mode 100644 build/lib/fiboa_cli/datasets/de_bb_block.py create mode 100644 build/lib/fiboa_cli/datasets/de_by.py create mode 100644 build/lib/fiboa_cli/datasets/de_mv.py create mode 100644 build/lib/fiboa_cli/datasets/de_nds.py create mode 100644 build/lib/fiboa_cli/datasets/de_nds_block.py create mode 100644 build/lib/fiboa_cli/datasets/de_nrw.py create mode 100644 build/lib/fiboa_cli/datasets/de_sax.py create mode 100644 build/lib/fiboa_cli/datasets/de_sh.py create mode 100644 build/lib/fiboa_cli/datasets/de_sl.py create mode 100644 build/lib/fiboa_cli/datasets/de_th.py create mode 100644 build/lib/fiboa_cli/datasets/digifarm.py create mode 100644 build/lib/fiboa_cli/datasets/dk.py create mode 100644 build/lib/fiboa_cli/datasets/ec_be_vlg.py create mode 100644 build/lib/fiboa_cli/datasets/ec_ee.py create mode 100644 build/lib/fiboa_cli/datasets/ec_lt.py create mode 100644 build/lib/fiboa_cli/datasets/ec_lv.py create mode 100644 build/lib/fiboa_cli/datasets/ec_nl_crop.py create mode 100644 build/lib/fiboa_cli/datasets/ec_ro.py create mode 100644 build/lib/fiboa_cli/datasets/ec_si.py create mode 100644 build/lib/fiboa_cli/datasets/ee.py create mode 100644 build/lib/fiboa_cli/datasets/es.py create mode 100644 build/lib/fiboa_cli/datasets/es_an.py create mode 100644 build/lib/fiboa_cli/datasets/es_ar.py create mode 100644 build/lib/fiboa_cli/datasets/es_base.py create mode 100644 build/lib/fiboa_cli/datasets/es_cat.py create mode 100644 build/lib/fiboa_cli/datasets/es_cb.py create mode 100644 build/lib/fiboa_cli/datasets/es_cl.py create mode 100644 build/lib/fiboa_cli/datasets/es_cm.py create mode 100644 build/lib/fiboa_cli/datasets/es_cn.py create mode 100644 build/lib/fiboa_cli/datasets/es_ex.py create mode 100644 build/lib/fiboa_cli/datasets/es_ga.py create mode 100644 build/lib/fiboa_cli/datasets/es_ib.py create mode 100644 build/lib/fiboa_cli/datasets/es_md.py create mode 100644 build/lib/fiboa_cli/datasets/es_nc.py create mode 100644 build/lib/fiboa_cli/datasets/es_pv.py create mode 100644 build/lib/fiboa_cli/datasets/es_vc.py create mode 100644 build/lib/fiboa_cli/datasets/fi.py create mode 100644 build/lib/fiboa_cli/datasets/fr.py create mode 100644 build/lib/fiboa_cli/datasets/hr.py create mode 100644 build/lib/fiboa_cli/datasets/ie.py create mode 100644 build/lib/fiboa_cli/datasets/india_10k.py create mode 100644 build/lib/fiboa_cli/datasets/it_1.py create mode 100644 build/lib/fiboa_cli/datasets/jecam.py create mode 100644 build/lib/fiboa_cli/datasets/jp.py create mode 100644 build/lib/fiboa_cli/datasets/lacuna_labels.py create mode 100644 build/lib/fiboa_cli/datasets/lt.py create mode 100644 build/lib/fiboa_cli/datasets/lu.py create mode 100644 build/lib/fiboa_cli/datasets/lv.py create mode 100644 build/lib/fiboa_cli/datasets/nl.py create mode 100644 build/lib/fiboa_cli/datasets/nl_block.py create mode 100644 build/lib/fiboa_cli/datasets/nz.py create mode 100644 build/lib/fiboa_cli/datasets/planet_afb.py create mode 100644 build/lib/fiboa_cli/datasets/pt.py create mode 100644 build/lib/fiboa_cli/datasets/se.py create mode 100644 build/lib/fiboa_cli/datasets/si.py create mode 100644 build/lib/fiboa_cli/datasets/sk.py create mode 100644 build/lib/fiboa_cli/datasets/template.py create mode 100644 build/lib/fiboa_cli/datasets/us_ca_scm.py create mode 100644 build/lib/fiboa_cli/datasets/us_usda_cropland.py create mode 100644 build/lib/fiboa_cli/datasets/varda.py create mode 100644 build/lib/fiboa_cli/describe.py create mode 100644 build/lib/fiboa_cli/fiboa/version.py create mode 100644 build/lib/fiboa_cli/improve.py create mode 100644 build/lib/fiboa_cli/merge.py create mode 100644 build/lib/fiboa_cli/publish.py create mode 100644 build/lib/fiboa_cli/registry.py create mode 100644 build/lib/fiboa_cli/rename_extension.py create mode 100644 build/lib/fiboa_cli/validate.py create mode 100644 build/lib/fiboa_cli/validate_schema.py create mode 100644 coverage.json diff --git a/build/lib/fiboa_cli/__init__.py b/build/lib/fiboa_cli/__init__.py new file mode 100644 index 00000000..276e3b08 --- /dev/null +++ b/build/lib/fiboa_cli/__init__.py @@ -0,0 +1,5 @@ +from vecorel_cli.registry import Registry + +from .registry import FiboaRegistry + +Registry.instance = FiboaRegistry() diff --git a/build/lib/fiboa_cli/cli/__init__.py b/build/lib/fiboa_cli/cli/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/fiboa_cli/cli/setup.py b/build/lib/fiboa_cli/cli/setup.py new file mode 100644 index 00000000..f1b86c27 --- /dev/null +++ b/build/lib/fiboa_cli/cli/setup.py @@ -0,0 +1,3 @@ +from vecorel_cli.cli.setup import setup_cli + +run = setup_cli() diff --git a/build/lib/fiboa_cli/conversion/__init__.py b/build/lib/fiboa_cli/conversion/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/fiboa_cli/conversion/convert_gml.py b/build/lib/fiboa_cli/conversion/convert_gml.py new file mode 100644 index 00000000..da034684 --- /dev/null +++ b/build/lib/fiboa_cli/conversion/convert_gml.py @@ -0,0 +1,34 @@ +import os + +import geopandas +from loguru import logger + + +def gml_assure_columns(data, path, uri, layer, **kwargs): + # if GDAL opens a GML file, it generates a gfs file in which it tries to 'guess' a + # mapping from the GML XML file to features. This is not always correct. + # Call this function to add additional attributes from the GML + # We modify the GFS file, a more elegant solution is preferred + # See https://gdal.org/en/latest/drivers/vector/gml.html#schema for more info + + if next(iter(kwargs)) not in data.columns: + logger.info("Patching generated GFS file") + assert path.endswith(".gml"), "Expected a gml file" + gfs_file = path[:-4] + ".gfs" + assert os.path.exists(gfs_file), "Expected a local, generated GFS file by OGR-GML driver" + # Fix GFS template file + with open(gfs_file, mode="r") as file: + gfs_xml = file.read() + + for property in kwargs: + assert f"{property}" not in gfs_xml, "Expected unpatched gfs file" + + lines = gfs_xml.splitlines() + for property, elements in kwargs.items(): + element_str = "\n".join(f"<{k}>{v}" for k, v in elements.items()) + lines.insert(-2, f"{property}{element_str}") + with open(gfs_file, mode="w") as file: + file.write("\n".join(lines)) + + data = geopandas.read_file(path, layer=layer) + return data diff --git a/build/lib/fiboa_cli/conversion/converter_rest.py b/build/lib/fiboa_cli/conversion/converter_rest.py new file mode 100644 index 00000000..9a5b25e8 --- /dev/null +++ b/build/lib/fiboa_cli/conversion/converter_rest.py @@ -0,0 +1,100 @@ +import os +from urllib.parse import urlencode + +import geopandas as gpd +import requests +from vecorel_cli.vecorel.util import get_fs, stream_file + + +class EsriRESTConverterMixin: + cache_folder = None + rest_base_url = None + rest_params = {} + rest_attribute = "OBJECTID" # orderable, filterable, indexed + + def rest_layer_filter(self, layers): + return next(iter(layers)) + + def get_urls(self): + assert self.rest_base_url, ( + "Either define {c}.rest_base_url or override {c}.get_urls()".format( + c=self.__class__.__name__ + ) + ) + return {"REST": self.rest_base_url} + + def download_files(self, uris, cache_folder=None): + # Read-data will just stream alle pages of rest-service + if next(iter(uris), "").startswith("REST"): + self.cache_folder = cache_folder + return list(uris.values()) + + # This happens when input_file param is used + return super().download_files(uris, cache_folder) + + def get_data(self, paths, **kwargs): + if isinstance(paths[0], tuple): + # (path, uri) pairs from the base downloader: input_file param was used + yield from super().get_data(paths, **kwargs) + return + + base_url = paths[0] # loop over paths to support more than 1 source + source_fs = get_fs(base_url) + cache_fs, cache_folder = self.get_cache(self.cache_folder) + + service_metadata = requests.get(base_url, {"f": "pjson"}).json() + layer = self.rest_layer_filter(service_metadata["layers"]) + page_size = service_metadata["maxRecordCount"] + layer_url = f"{base_url}/{layer['id']}/query" + get_dict = self.rest_params | { + "outFields": "*", + "returnGeometry": "true", + "f": "geojson", + "sortBy": self.rest_attribute, + "resultRecordCount": page_size, + } + gdfs = [] + last_id = -1 + while True: + get_dict["where"] = f"{self.rest_attribute}>{last_id}" + url = f"{layer_url}?{urlencode(get_dict)}" + if cache_fs is not None: + cache_file = os.path.join( + cache_folder, f"{self.id}_{layer['id']}_{last_id}.geojson" + ) + if not cache_fs.exists(cache_file): + try: + with cache_fs.open(cache_file, mode="wb") as file: + stream_file(source_fs, url, file) + except Exception: + # A download that broke off must not survive as a cached page + if cache_fs.exists(cache_file): + cache_fs.rm(cache_file) + raise + url = cache_file + + try: + data = gpd.read_file(url) + except Exception as e: + # An error response from the server must not survive as a cached page + if cache_fs is not None and cache_fs.exists(url): + cache_fs.rm(url) + raise RuntimeError(f"Could not read page {len(gdfs)} of {layer_url}: {e}") from e + print( + f"Read {len(data)} features, page {len(gdfs)} from [{data.iloc[0, 0]} ... {data.iloc[-1, 0]}]" + ) + # joined layers return the field as
. + id_column = next( + ( + c + for c in data.columns + if c == self.rest_attribute or c.endswith("." + self.rest_attribute) + ), + self.rest_attribute, + ) + last_id = data[id_column].values[-1] + + yield data, base_url, base_url, layer["id"] + + if not len(data) >= page_size: + break diff --git a/build/lib/fiboa_cli/conversion/duckdb.py b/build/lib/fiboa_cli/conversion/duckdb.py new file mode 100644 index 00000000..7e770f0f --- /dev/null +++ b/build/lib/fiboa_cli/conversion/duckdb.py @@ -0,0 +1,258 @@ +import json +import os +from pathlib import Path +from tempfile import NamedTemporaryFile + +import duckdb +import numpy as np +import pyarrow as pa +import pyarrow.parquet as pq +from geopandas.array import from_wkb +from pyarrow.lib import StructArray +from vecorel_cli.encoding.geojson import VecorelJSONEncoder + +from .fiboa_converter import FiboaBaseConverter + + +# This converter is experimental, use with caution. +# Results may not be fully fiboa compliant yet. +# Use this primarily for datasets that are too large to be processed by the default converter +class FiboaDuckDBBaseConverter(FiboaBaseConverter): + def convert( + self, + output_file, + cache=None, + input_files=None, + variant=None, + compression=None, + geoparquet_version=None, + original_geometries=False, + **kwargs, + ) -> str: + if not original_geometries: + self.warning( + "original_geometries is not supported for DuckDB-based converters and will always write original geometries" + ) + + geoparquet_version = geoparquet_version or "1.1.0" + compression = compression or "brotli" + + self.variant = variant + cid = self.id.strip() + if self.bbox is not None and len(self.bbox) != 4: + raise ValueError("If provided, the bounding box must consist of 4 numbers") + + # Create output folder if it doesn't exist + directory = os.path.dirname(output_file) + if directory: + os.makedirs(directory, exist_ok=True) + + if input_files is not None and isinstance(input_files, dict) and len(input_files) > 0: + self.warning("Using user provided input file(s) instead of the pre-defined file(s)") + urls = input_files + else: + urls = self.get_urls() + if urls is None: + raise ValueError("No input files provided") + + self.info("Getting file(s) if not cached yet") + if cache: + request_args = {} + if self.avoid_range_request: + request_args["block_size"] = 0 + urls = self.download_files(urls, cache, **request_args) + elif self.avoid_range_request: + self.warning( + "avoid_range_request is set, but cache is not used, so this setting has no effect" + ) + + selections = [] + for k, v in self.columns.items(): + if k in self.column_migrations: + selections.append(f'{self.column_migrations.get(k)} as "{v}"') + else: + selections.append(f'"{k}" as "{v}"') + selection = ", ".join(selections) + + filters = [] + where = "" + if self.bbox is not None: + filters.append( + f"ST_Intersects(geometry, ST_MakeEnvelope({self.bbox[0]}, {self.bbox[1]}, {self.bbox[2]}, {self.bbox[3]}))" + ) + for k, v in self.column_filters.items(): + filters.append(v) + if len(filters) > 0: + where = f"WHERE {' AND '.join(filters)}" + + if isinstance(urls, str): + sources = f'"{urls}"' + else: + paths = [] + for url in urls: + if isinstance(url, tuple): + paths.append(f'"{url[0]}"') + else: + paths.append(f'"{url}"') + sources = "[" + ",".join(paths) + "]" + + collection = self.create_collection(cid) + collection.update(self.column_additions) + collection["collection"] = self.id + + if isinstance(output_file, Path): + output_file = str(output_file) + + collection_json = json.dumps(collection, cls=VecorelJSONEncoder).encode("utf-8") + + con = duckdb.connect() + con.install_extension("spatial") + con.load_extension("spatial") + # No ORDER BY here: ST_Hilbert without bounds is meaningless (whole + # countries collapse into a handful of cells), and with bounds it uses + # a different reference grid than the rest of the pipeline. The + # canonical in-place Hilbert sort below runs after post-processing. + con.execute( + f""" + COPY ( + SELECT {selection} + FROM read_parquet({sources}, union_by_name=true) + {where} + ) TO ? ( + FORMAT parquet, + ROW_GROUP_SIZE 50_000, + compression ?, + KV_METADATA {{ + collection: ?, + }} + ) + """, + [output_file, compression, collection_json], + ) + + # Post-process the written Parquet to proper GeoParquet v1.1 with bbox and nullability + try: + pq_file = pq.ParquetFile(output_file) + + existing_schema = pq_file.schema_arrow + col_names = existing_schema.names + assert "geometry" in col_names, "Missing geometry column in output parquet file" + + schemas = collection.merge_schemas({}) + collection_only = {k for k, v in schemas.get("collection", {}).items() if v} + required_columns = {"geometry"} | { + r + for r in schemas.get("required", []) + if r in col_names and r not in collection_only + } + if "id" in col_names: + required_columns.add("id") + + # Update for version 1.1.0 + metadata = existing_schema.metadata + add_covering = geoparquet_version > "1.0.0" and "bbox" not in col_names + if geoparquet_version > "1.0.0": + geo_meta = json.loads(existing_schema.metadata[b"geo"]) + geo_meta["version"] = geoparquet_version + if add_covering: + # declare the appended bbox column so engines use it + primary = geo_meta.get("primary_column", "geometry") + geo_meta["columns"][primary]["covering"] = { + "bbox": { + "xmin": ["bbox", "xmin"], + "ymin": ["bbox", "ymin"], + "xmax": ["bbox", "xmax"], + "ymax": ["bbox", "ymax"], + } + } + metadata[b"geo"] = json.dumps(geo_meta).encode("utf-8") + + # Build a new Arrow schema with adjusted nullability + new_fields = [] + for field in existing_schema: + if field.name in required_columns and field.nullable: + new_fields.append( + pa.field(field.name, field.type, nullable=False, metadata=field.metadata) + ) + else: + new_fields.append(field) + + add_bbox = geoparquet_version > "1.0.0" and "bbox" not in col_names + if add_bbox: + new_fields.append( + pa.field( + "bbox", + pa.struct( + [ + ("xmin", pa.float64()), + ("ymin", pa.float64()), + ("xmax", pa.float64()), + ("ymax", pa.float64()), + ] + ), + ) + ) + new_schema = pa.schema(new_fields, metadata=metadata) + + # 7) Streamingly rewrite the file to a temp file and replace atomically + with NamedTemporaryFile( + "wb", delete=False, dir=os.path.dirname(output_file), suffix=".parquet" + ) as tmp: + tmp_path = tmp.name + + writer = pq.ParquetWriter( + tmp_path, + new_schema, + compression=compression, + use_dictionary=True, + write_statistics=True, + ) + try: + bbox_names = ["ymax", "xmax", "ymin", "xmin"] + for rg in range(pq_file.num_row_groups): + tbl = pq_file.read_row_group(rg) + if add_bbox: + # determine bounds, change to StructArray type + bounds = from_wkb(tbl["geometry"]).bounds + bbox_array = StructArray.from_arrays( + np.rot90(bounds), + names=bbox_names, + ) + tbl = tbl.append_column("bbox", bbox_array) + # Ensure table adheres to the new schema (mainly nullability); cast if needed + if tbl.schema != new_schema: + # Align field order/types; this does not materialize data beyond the batch + tbl = tbl.cast(new_schema, safe=False) + writer.write_table(tbl) + finally: + writer.close() + pq_file.close() # Windows cannot replace a file that is still open + + os.replace(tmp_path, output_file) + except Exception as e: + self.warning(f"GeoParquet 1.1 post-processing failed: {e}") + + # canonical spatial ordering, same grid as the per-file merge + try: + from vecorel_cli.vecorel.hilbert import crs_total_bounds + except ImportError: + from .hilbert import crs_total_bounds + from .per_file import _ensure_hilbert_sorted + + with pq.ParquetFile(output_file) as pf: + meta = pf.schema_arrow.metadata or {} + if b"geo" in meta: + geo = json.loads(meta[b"geo"]) + primary = geo["primary_column"] + crs = geo["columns"][primary].get("crs") or "EPSG:4326" + if _ensure_hilbert_sorted( + output_file, + primary, + crs_total_bounds(crs), + compression, + None, + row_group_size=50_000, + ): + self.info("Sorted output into Hilbert order") + + return output_file diff --git a/build/lib/fiboa_cli/conversion/fiboa_converter.py b/build/lib/fiboa_cli/conversion/fiboa_converter.py new file mode 100644 index 00000000..3d7016c5 --- /dev/null +++ b/build/lib/fiboa_cli/conversion/fiboa_converter.py @@ -0,0 +1,69 @@ +import numpy as np +from vecorel_cli.conversion.base import BaseConverter + +from ..fiboa.version import get_fiboa_uri + +AREA_KEY = "metrics:area" +# Properties that a schema requires to be non-null; rows lacking them cannot +# validate, so they are dropped (with a warning) rather than failing the run. +REQUIRED_NON_NULL = ("id", "crop:code") + + +class FiboaBaseConverter(BaseConverter): + area_is_in_ha = True + area_calculate_missing = False + use_variant_as_determination = False + # rows lacking a REQUIRED_NON_NULL value are dropped up to this share, else it's an error + max_dropped_share = 0.01 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.extensions.add(get_fiboa_uri()) + if self.use_variant_as_determination: + # The column is added in post_migrate; list it so it survives the + # "remove unlisted columns" step of the base converter. + self.columns = {**self.columns, "determination:datetime": "determination:datetime"} + + def post_migrate(self, gdf): + gdf = super().post_migrate(gdf) + + # post_migrate runs before columns are renamed, so look up the source column + for key in REQUIRED_NON_NULL: + for src, dst in self.columns.items(): + targets = dst if isinstance(dst, (list, tuple)) else [dst] + if key in targets and src in gdf.columns: + nulls = gdf[src].isna() + if nulls.any(): + share = nulls.mean() + if share > self.max_dropped_share: + raise ValueError( + f"{int(nulls.sum())} of {len(gdf)} rows ({share:.1%}) have no " + f"{key} ({src}); fix the converter instead of dropping them" + ) + self.warning( + f"Dropping {int(nulls.sum())} rows without a value for {key} ({src})" + ) + gdf = gdf[~nulls] + + gdf_area_key = next((k for k, v in self.columns.items() if v == AREA_KEY), None) + if self.area_calculate_missing: + # If CRS is not in meters, reproject to an equal-area projection for area calculation + crs_is_in_meters = gdf.crs.axis_info[0].unit_name in ("m", "metre", "meter") + + # Calculate geometry area; Use original geometries if crs_is_in_meters, else reproject to m-based projection + base = gdf if crs_is_in_meters else gdf["geometry"].to_crs("EPSG:6933") + + if gdf_area_key in gdf.columns: + factor = 10_000 if self.area_is_in_ha else 1 + gdf[gdf_area_key] = np.where( + gdf[gdf_area_key] == 0, base.area * factor, gdf[gdf_area_key] + ) + else: + gdf[gdf_area_key] = base.area + elif self.area_is_in_ha and gdf_area_key in gdf.columns: + # convert area in ha to meters + gdf[gdf_area_key] = gdf[gdf_area_key].astype(float) * 10_000 + + if self.use_variant_as_determination: + gdf["determination:datetime"] = f"{self.variant}-01-01T00:00:00Z" + return gdf diff --git a/fiboa_cli/conversion/hilbert.py b/build/lib/fiboa_cli/conversion/hilbert.py similarity index 100% rename from fiboa_cli/conversion/hilbert.py rename to build/lib/fiboa_cli/conversion/hilbert.py diff --git a/build/lib/fiboa_cli/conversion/per_file.py b/build/lib/fiboa_cli/conversion/per_file.py new file mode 100644 index 00000000..9aa57f07 --- /dev/null +++ b/build/lib/fiboa_cli/conversion/per_file.py @@ -0,0 +1,424 @@ +import json +import os +from typing import Optional + +import numpy as np +import pyarrow as pa +import pyarrow.parquet as pq + +from .fiboa_converter import FiboaBaseConverter + +GEO_META_KEY = b"geo" +DEFAULT_BATCH_SIZE = 64_000 + + +# This converter is experimental, use with caution. +# Use this primarily for datasets that are too large to be processed by the default converter +class PerFileBaseConverter(FiboaBaseConverter): + def convert( + self, + output_file, + cache=None, + input_files=None, + variant=None, + compression=None, + compression_level: Optional[int] = None, + geoparquet_version=None, + original_geometries=False, + **kwargs, + ) -> str: + dirname, filename = os.path.split(output_file) + filename, ext = os.path.splitext(filename) + if input_files is not None and isinstance(input_files, dict) and len(input_files) > 0: + self.warning("Using user provided input file(s) instead of the pre-defined file(s)") + urls = input_files + else: + urls = self.get_urls() + if urls is None: + raise ValueError("No input files provided") + + # Single-source: the per-file pipeline degenerates to plain convert. + if len(urls) <= 1: + return super().convert( + output_file=output_file, + cache=cache, + input_files=urls, + variant=variant, + compression=compression, + compression_level=compression_level, + geoparquet_version=geoparquet_version, + original_geometries=original_geometries, + **kwargs, + ) + + # Multi-source: convert each URI to its own GeoParquet part, then merge. + part_files = [] + for index, (uri, target) in enumerate(urls.items()): + part = os.path.join(dirname, f"{filename}_{index}_part{ext}") + part_files.append(part) + if os.path.exists(part): + self.info( + f"Skipping existing file {part}: {uri} -> {output_file} (part {index + 1}/{len(urls)})" + ) + continue + self.info(f"Converting source {index + 1}/{len(urls)}: {uri}") + super().convert( + output_file=part, + cache=cache, + input_files={uri: target}, + variant=variant, + compression=compression, + compression_level=compression_level, + geoparquet_version=geoparquet_version, + original_geometries=original_geometries, + **kwargs, + ) + self.merge_files( + output_file, + part_files, + compression=compression or "zstd", + compression_level=compression_level, + geoparquet_version=geoparquet_version, + cleanup_parts=True, + ) + return output_file + + def merge_files( + self, + output_file: str, + paths: list, + batch_size: int = DEFAULT_BATCH_SIZE, + compression: str = "zstd", + compression_level: Optional[int] = None, + geoparquet_version: Optional[str] = None, + cleanup_parts: bool = False, + ) -> str: + """ + Merge a list of GeoParquet files into a single GeoParquet, globally + sorted by Hilbert distance. Streams via pyarrow row groups so peak + memory is roughly O(batch_size * k). + + Each input file is expected to be sorted by Hilbert distance against + the CRS's total bounds (see ``vecorel_cli.vecorel.hilbert``). If a + part file is *not* in Hilbert order it is sorted in place before the + streaming merge — this guards against pre-existing part files that + were produced by an older vecorel-cli (which sorted by WKB lex order + instead of Hilbert) and would otherwise silently drop rows in the + streaming merge (``np.searchsorted`` requires a sorted input). + + ``geoparquet_version`` (``"1.0.0"`` / ``"1.1.0"`` / ``None``) sets the + ``version`` field of the merged file's ``geo`` metadata. When ``None`` + (default), the value declared by the input files is preserved unchanged. + """ + if geoparquet_version is not None: + from vecorel_cli.const import GEOPARQUET_VERSIONS + + if geoparquet_version not in GEOPARQUET_VERSIONS: + raise ValueError( + f"Invalid geoparquet_version {geoparquet_version!r}; " + f"expected one of {GEOPARQUET_VERSIONS}" + ) + if not paths: + raise ValueError("No paths to merge") + paths = [str(p) for p in paths] + + with pq.ParquetFile(paths[0]) as base_pf: + base_schema = base_pf.schema_arrow + base_meta = base_schema.metadata or {} + if GEO_META_KEY not in base_meta: + raise ValueError(f"{paths[0]} has no 'geo' metadata; not a GeoParquet?") + base_geo = json.loads(base_meta[GEO_META_KEY]) + primary_col = base_geo["primary_column"] + primary_col_meta = base_geo["columns"][primary_col] + crs = primary_col_meta.get("crs") + + # Validate schemas + CRS, collect per-file bboxes / geometry_types for + # the merged geo metadata. + bboxes: list = [] + geom_types: set = set() + if primary_col_meta.get("bbox") is not None: + bboxes.append(primary_col_meta["bbox"]) + geom_types.update(primary_col_meta.get("geometry_types") or []) + for path in paths[1:]: + with pq.ParquetFile(path) as pf: + sch = pf.schema_arrow + if not sch.equals(base_schema, check_metadata=False): + raise ValueError( + f"Schema mismatch: {path} differs from {paths[0]}.\n" + f" Expected: {base_schema}\n" + f" Got: {sch}" + ) + geo = json.loads((sch.metadata or {})[GEO_META_KEY]) + col = geo["columns"][primary_col] + if col.get("crs") != crs: + raise ValueError( + f"CRS mismatch: {path} has crs={col.get('crs')!r}, expected {crs!r}" + ) + if col.get("bbox") is not None: + bboxes.append(col["bbox"]) + geom_types.update(col.get("geometry_types") or []) + + merged_bbox = None + if bboxes: + merged_bbox = ( + min(b[0] for b in bboxes), + min(b[1] for b in bboxes), + max(b[2] for b in bboxes), + max(b[3] for b in bboxes), + ) + + # Same Hilbert reference grid that the upstream sort used. + try: + from vecorel_cli.vecorel.hilbert import crs_total_bounds + except ImportError: + from .hilbert import crs_total_bounds + + total_bounds = crs_total_bounds(crs) + + # Verify each part is Hilbert-sorted; sort in place if not. With a + # vecorel-cli that already Hilbert-sorts, this is a fast no-op read. + self.info(f"Verifying Hilbert order of {len(paths)} part file(s)") + n_resorted = 0 + for path in paths: + if _ensure_hilbert_sorted( + path, primary_col, total_bounds, compression, compression_level + ): + n_resorted += 1 + self.warning( + f" {path}: was not Hilbert-sorted, re-sorted in place. " + "(Bump vecorel-cli to skip this rewrite next time.)" + ) + if n_resorted: + self.warning(f"Re-sorted {n_resorted}/{len(paths)} part file(s) before merging.") + + self.info(f"Streaming merge -> {output_file} (Hilbert ref bounds = {total_bounds})") + expected_rows = sum(_num_rows(p) for p in paths) + _streaming_merge( + paths, + output_file, + primary_col, + total_bounds, + merged_bbox, + sorted(geom_types), + batch_size, + compression, + compression_level, + geoparquet_version, + ) + actual_rows = _num_rows(output_file) + if actual_rows != expected_rows: + raise RuntimeError( + f"Streaming merge dropped rows: expected {expected_rows:,} " + f"(sum of inputs), wrote {actual_rows:,} to {output_file}. " + "This is a bug — inputs were verified Hilbert-sorted before merge." + ) + self.info(f"Merged {actual_rows:,} rows into {output_file}") + + if cleanup_parts: + for path in paths: + try: + os.remove(path) + except OSError: + self.warning(f"Could not remove part file {path}") + + return output_file + + +# ---------- helpers ---------- + + +def _num_rows(path) -> int: + with pq.ParquetFile(path) as pf: + return pf.metadata.num_rows + + +def _bounds_array_for_table(table: pa.Table, primary_col: str) -> np.ndarray: + """Return an (N, 4) float64 array of [xmin, ymin, xmax, ymax] per feature. + + Uses the GeoParquet 1.1.0 covering ``bbox`` struct column when present + (zero-decode); otherwise falls back to decoding WKB. + """ + if "bbox" in table.column_names and pa.types.is_struct(table.column("bbox").type): + arr = table.column("bbox").combine_chunks() + return np.column_stack( + [ + arr.field("xmin").to_numpy(zero_copy_only=False), + arr.field("ymin").to_numpy(zero_copy_only=False), + arr.field("xmax").to_numpy(zero_copy_only=False), + arr.field("ymax").to_numpy(zero_copy_only=False), + ] + ).astype(np.float64, copy=False) + import shapely + + wkb_list = table.column(primary_col).combine_chunks().to_pylist() + geoms = shapely.from_wkb(wkb_list) + return shapely.bounds(geoms) + + +def _hilbert_keys_for_table(table: pa.Table, primary_col: str, total_bounds) -> np.ndarray: + try: + from vecorel_cli.vecorel.hilbert import hilbert_distances_from_bounds + except ImportError: + from fiboa_cli.conversion.hilbert import hilbert_distances_from_bounds + + bounds = _bounds_array_for_table(table, primary_col) + return hilbert_distances_from_bounds(bounds, total_bounds) + + +def _ensure_hilbert_sorted( + path: str, + primary_col: str, + total_bounds, + compression: str, + compression_level: Optional[int], + row_group_size: Optional[int] = None, +) -> bool: + """If ``path`` is already Hilbert-sorted against ``total_bounds``, leave it + untouched and return False. Otherwise sort it in place and return True. + + The whole file is loaded into memory once; for the per-file converter this + is bounded by a single source partition (much smaller than the merged + dataset). Schema metadata (``geo``, collection JSON, etc.) is preserved. + """ + # cheap check first: the keys only need the bbox covering column + with pq.ParquetFile(path) as pf: + has_bbox = "bbox" in pf.schema_arrow.names + probe = pf.read(columns=["bbox"]) if has_bbox else pf.read() + hilberts = _hilbert_keys_for_table(probe, primary_col, total_bounds) + # NB: hilberts is uint64; never use np.diff for monotonicity here — uint + # underflow makes any descent wrap to a huge positive and fool the check. + if hilberts.size <= 1 or bool(np.all(hilberts[1:] >= hilberts[:-1])): + return False + with pq.ParquetFile(path) as pf: + table = pf.read() + metadata = pf.schema_arrow.metadata + # int32 offsets of plain binary/string columns overflow when a take() + # concatenates >2 GB of chunks (large WKB columns); widen them first. + # Parquet's physical BYTE_ARRAY is identical either way. + fields = [] + widened = False + for f in table.schema: + if pa.types.is_binary(f.type): + fields.append(f.with_type(pa.large_binary())) + widened = True + elif pa.types.is_string(f.type): + fields.append(f.with_type(pa.large_string())) + widened = True + else: + fields.append(f) + if widened: + table = table.cast(pa.schema(fields, metadata=table.schema.metadata)) + order = np.argsort(hilberts, kind="stable") + sorted_table = table.take(pa.array(order)) + sorted_table = sorted_table.replace_schema_metadata(metadata) + write_kwargs = {"compression": compression} + if compression_level is not None: + write_kwargs["compression_level"] = compression_level + if row_group_size is not None: + write_kwargs["row_group_size"] = row_group_size + pq.write_table(sorted_table, path, **write_kwargs) + return True + + +def _build_output_schema( + input_schema: pa.Schema, + merged_bbox, + geom_types, + geoparquet_version: Optional[str] = None, +) -> pa.Schema: + """Patch the geo metadata: merged bbox + union of geometry_types, and + optionally overwrite the GeoParquet ``version`` field. Other schema + metadata and field metadata are preserved unchanged.""" + meta = dict(input_schema.metadata or {}) + geo = json.loads(meta[GEO_META_KEY]) + primary_col = geo["primary_column"] + if merged_bbox is not None: + geo["columns"][primary_col]["bbox"] = [float(v) for v in merged_bbox] + if geom_types: + geo["columns"][primary_col]["geometry_types"] = list(geom_types) + if geoparquet_version is not None: + geo["version"] = geoparquet_version + meta[GEO_META_KEY] = json.dumps(geo).encode("utf-8") + return input_schema.with_metadata(meta) + + +def _streaming_merge( + paths: list, + output_file: str, + primary_col: str, + total_bounds, + merged_bbox, + geom_types, + batch_size: int, + compression: str, + compression_level: Optional[int], + geoparquet_version: Optional[str] = None, +) -> None: + pq_files = [pq.ParquetFile(p) for p in paths] + in_schema = pq_files[0].schema_arrow # readers closed in the finally below + out_schema = _build_output_schema(in_schema, merged_bbox, geom_types, geoparquet_version) + + iters = [pf.iter_batches(batch_size=batch_size) for pf in pq_files] + heads: list = [None] * len(paths) + hilberts: list = [None] * len(paths) + + def refill(i): + # Skip any empty batches; mark the iterator exhausted only when next() raises. + while True: + try: + batch = next(iters[i]) + except StopIteration: + heads[i] = None + hilberts[i] = None + return + if batch.num_rows == 0: + continue + tbl = pa.Table.from_batches([batch]) + heads[i] = tbl + hilberts[i] = _hilbert_keys_for_table(tbl, primary_col, total_bounds) + return + + for i in range(len(paths)): + refill(i) + + write_kwargs = {"compression": compression} + if compression_level is not None: + write_kwargs["compression_level"] = compression_level + writer = pq.ParquetWriter(output_file, out_schema, **write_kwargs) + + try: + while any(h is not None for h in heads): + active = [i for i, h in enumerate(heads) if h is not None] + # The horizon is the smallest "current max" Hilbert across active heads. + # Every row with hilbert <= horizon is emit-safe in this round, because + # no still-pending row from any other file can possibly be less than it. + horizon = min(hilberts[i][-1] for i in active) + + chunks = [] + chunk_h = [] + for i in active: + h = hilberts[i] + cut = int(np.searchsorted(h, horizon, side="right")) + if cut == 0: + continue + chunks.append(heads[i].slice(0, cut)) + chunk_h.append(h[:cut]) + if cut == heads[i].num_rows: + refill(i) + else: + heads[i] = heads[i].slice(cut) + hilberts[i] = h[cut:] + + if not chunks: + # Defensive: shouldn't happen because at least the file defining the + # horizon will contribute its full current batch. + break + + combined = pa.concat_tables(chunks) + combined_h = np.concatenate(chunk_h) + order = np.argsort(combined_h, kind="stable") + writer.write_table(combined.take(pa.array(order))) + finally: + writer.close() + for pf in pq_files: + pf.close() diff --git a/build/lib/fiboa_cli/convert.py b/build/lib/fiboa_cli/convert.py new file mode 100644 index 00000000..b9ffa6f2 --- /dev/null +++ b/build/lib/fiboa_cli/convert.py @@ -0,0 +1,5 @@ +from vecorel_cli.convert import ConvertData as Base + + +class ConvertData(Base): + pass diff --git a/build/lib/fiboa_cli/converters.py b/build/lib/fiboa_cli/converters.py new file mode 100644 index 00000000..6b83d988 --- /dev/null +++ b/build/lib/fiboa_cli/converters.py @@ -0,0 +1,5 @@ +from vecorel_cli.converters import Converters as Base + + +class Converters(Base): + pass diff --git a/build/lib/fiboa_cli/create_geojson.py b/build/lib/fiboa_cli/create_geojson.py new file mode 100644 index 00000000..797d004a --- /dev/null +++ b/build/lib/fiboa_cli/create_geojson.py @@ -0,0 +1,5 @@ +from vecorel_cli.create_geojson import CreateGeoJson as Base + + +class CreateGeoJson(Base): + pass diff --git a/build/lib/fiboa_cli/create_geoparquet.py b/build/lib/fiboa_cli/create_geoparquet.py new file mode 100644 index 00000000..e7234ead --- /dev/null +++ b/build/lib/fiboa_cli/create_geoparquet.py @@ -0,0 +1,5 @@ +from vecorel_cli.create_geoparquet import CreateGeoParquet as Base + + +class CreateGeoParquet(Base): + pass diff --git a/build/lib/fiboa_cli/create_jsonschema.py b/build/lib/fiboa_cli/create_jsonschema.py new file mode 100644 index 00000000..a16cfd3d --- /dev/null +++ b/build/lib/fiboa_cli/create_jsonschema.py @@ -0,0 +1,5 @@ +from vecorel_cli.create_jsonschema import CreateJsonSchema as Base + + +class CreateJsonSchema(Base): + pass diff --git a/build/lib/fiboa_cli/create_stac.py b/build/lib/fiboa_cli/create_stac.py new file mode 100644 index 00000000..ae291d01 --- /dev/null +++ b/build/lib/fiboa_cli/create_stac.py @@ -0,0 +1,47 @@ +import click +from geopandas import GeoDataFrame +from vecorel_cli.cli.options import JSON_INDENT, VECOREL_FILE_ARG, VECOREL_TARGET_CONSOLE +from vecorel_cli.create_stac import CreateStacCollection as Base +from vecorel_cli.registry import VecorelRegistry +from vecorel_cli.vecorel.collection import Collection + +from fiboa_cli.fiboa.version import get_versions + + +class CreateStacCollection(Base): + temporal_property = "determination:datetime" + + @staticmethod + def get_cli_args(): + return { + "source": VECOREL_FILE_ARG, + "target": VECOREL_TARGET_CONSOLE, + "indent": JSON_INDENT, + "temporal": click.option( + "temporal_property", + "--temporal", + "-t", + type=click.STRING, + help="The temporal property to use for the temporal extent.", + show_default=True, + default=CreateStacCollection.temporal_property, + ), + # todo: allow additional parameters for missing data in the collection? + # https://stackoverflow.com/questions/36513706/python-click-pass-unspecified-number-of-kwargs + } + + def create(self, collection: Collection, gdf: GeoDataFrame, *args, **kwargs) -> dict: + data = super().create(collection, gdf, *args, **kwargs) + vecorel = VecorelRegistry() + data["assets"]["data"]["processing:software"].setdefault( + vecorel.name, vecorel.get_version() + ) + schemas = collection.get_schemas() + vecorel_version, _, fiboa_version, _, extensions = get_versions( + next(iter(schemas.values())) + ) + data["fiboa_version"] = fiboa_version + data.setdefault("vecorel_version", vecorel_version) + data.setdefault("vecorel_extensions", {k: list(v) for k, v in schemas.items()}) + + return data diff --git a/build/lib/fiboa_cli/datasets/__init__.py b/build/lib/fiboa_cli/datasets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/fiboa_cli/datasets/ai4sf.py b/build/lib/fiboa_cli/datasets/ai4sf.py new file mode 100644 index 00000000..4f043fa8 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ai4sf.py @@ -0,0 +1,122 @@ +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(FiboaBaseConverter): + sources = { + # Cambodia + "https://phys-techsciences.datastations.nl/api/access/datafile/100634?gbrecs=true": "2_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100282?gbrecs=true": "3_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100392?gbrecs=true": "4_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100252?gbrecs=true": "5_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100072?gbrecs=true": "6_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100169?gbrecs=true": "7_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100348?gbrecs=true": "8_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100487?gbrecs=true": "9_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100084?gbrecs=true": "10_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100155?gbrecs=true": "11_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100475?gbrecs=true": "12_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100372?gbrecs=true": "13_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100196?gbrecs=true": "14_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100006?gbrecs=true": "15_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100248?gbrecs=true": "16_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100397?gbrecs=true": "17_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100217?gbrecs=true": "18_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100652?gbrecs=true": "19_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100326?gbrecs=true": "20_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100625?gbrecs=true": "21_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100413?gbrecs=true": "33_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100593?gbrecs=true": "34_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100057?gbrecs=true": "35_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100536?gbrecs=true": "36_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100343?gbrecs=true": "37_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100711?gbrecs=true": "38_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100313?gbrecs=true": "39_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100679?gbrecs=true": "57_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100191?gbrecs=true": "58_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100023?gbrecs=true": "59_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100025?gbrecs=true": "60_cambodia_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100543?gbrecs=true": "61_cambodia_areas.gpkg", + # Vietnam + "https://phys-techsciences.datastations.nl/api/access/datafile/100297?gbrecs=true": "0_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100636?gbrecs=true": "1_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100574?gbrecs=true": "22_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100095?gbrecs=true": "23_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100398?gbrecs=true": "24_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100187?gbrecs=true": "25_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100065?gbrecs=true": "26_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100425?gbrecs=true": "27_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100589?gbrecs=true": "28_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100021?gbrecs=true": "29_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100043?gbrecs=true": "30_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100562?gbrecs=true": "31_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100437?gbrecs=true": "32_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100549?gbrecs=true": "40_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100039?gbrecs=true": "41_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100427?gbrecs=true": "42_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100466?gbrecs=true": "43_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100014?gbrecs=true": "44_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100464?gbrecs=true": "45_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100416?gbrecs=true": "46_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100115?gbrecs=true": "47_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100510?gbrecs=true": "48_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100459?gbrecs=true": "49_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100340?gbrecs=true": "50_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100119?gbrecs=true": "51_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100086?gbrecs=true": "52_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100467?gbrecs=true": "53_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100176?gbrecs=true": "54_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100145?gbrecs=true": "55_vietnam_areas.gpkg", + "https://phys-techsciences.datastations.nl/api/access/datafile/100492?gbrecs=true": "56_vietnam_areas.gpkg", + } + + id = "ai4sf" + short_name = "Cambodia/Vietnam (AI4SmallFarms)" + title = "Field boundaries for Cambodia and Vietnam (AI4SmallFarms)" + # from https://research.tudelft.nl/en/publications/ai4smallfarms-a-dataset-for-crop-field-delineation-in-southeast-a + description = """ +Agricultural field polygons within smallholder farming systems are essential to facilitate the collection of geo-spatial data useful for farmers, managers, and policymakers. +However, the limited availability of training labels poses a challenge in developing supervised methods to accurately delineate field boundaries using Earth Observation (EO) data. +This data set allows researchers to test and benchmark machine learning methods to delineate agricultural field boundaries in polygon format. +The large-scale data set consists of 439,001 field polygons divided into 62 tiles of approximately 5×5 km distributed across Vietnam and Cambodia, covering a range of fields and diverse landscape types. +The field polygons have been meticulously digitized from satellite images, following a rigorous multi-step quality control process and topological consistency checks. +Multi-temporal composites of Sentinel-2 (S2) images are provided to ensure cloud-free data. + """ + + provider = "DATA Archiving and Networked Services (DANS) " + attribution = "Persello, C., Grift, J., Fan, X., Paris, C., Hansch, R., Koeva, M., & Nelson, A. (2023). AI4SmallFarms: A Dataset for Crop Field Delineation in Southeast Asian Smallholder Farms. IEEE Geoscience and Remote Sensing Letters, 20, 1-5. Article 2505705. https://doi.org/10.1109/LGRS.2023.3323095" + license = "CC-BY-4.0" + + columns = { + "fiboa_id": "id", + "id": "group", + "_predicate": "_predicate", + "country": "admin:country_code", + "geometry": "geometry", + } + + extensions = {ADMIN_DIVISION} + column_migrations = {"country": lambda col: col.map({"cambodia": "KH", "vietnam": "VN"})} + + # Add columns with constant values. + # The key is the column name, the value is a constant value that's used for all rows. + column_additions = { + "determination:datetime": "2021-08-01T00:00:00Z", + "determination:method": "auto-imagery", + } + + def migrate(self, gdf): + # Create unique IDs from the dataset in the form "xx_xxxxx" + gdf["fiboa_id"] = ( + gdf["id"].astype(str).str.zfill(2) + "_" + gdf.index.astype(str).str.zfill(5) + ) + return super().migrate(gdf) + + missing_schemas = { + "properties": { + "group": {"type": "uint8"}, + "group_id": {"type": "uint16"}, + "_predicate": {"type": "string", "enum": ["INTERSECTS"]}, + } + } diff --git a/build/lib/fiboa_cli/datasets/at.py b/build/lib/fiboa_cli/datasets/at.py new file mode 100644 index 00000000..4ebbc3e2 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/at.py @@ -0,0 +1,43 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + "2025": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2025-1_polygon.gpkg.zip", + "2024": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2024-2_polygon.gpkg.zip", + "2023": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2023-2_polygon.gpkg.zip", + "2022": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2022_polygon.gpkg.zip", + "2021": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2021_polygon.gpkg.zip", + "2020": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2020_polygon.gpkg.zip", + "2019": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2019_polygon.gpkg.zip", + "2018": "https://inspire.lfrz.gv.at/009501/ds/inspire_schlaege_2018_polygon.gpkg.zip", + } + + id = "at" + country = "AT" + short_name = "Austria" + title = "Field boundaries for Austria" + description = """ +**Crop Field boundaries for Austria - INVEKOS Schläge Österreich 2025.** + +This layer includes all field uses recorded by the applicants, which serve as the basis for the funding process. A field +is a contiguous area of a piece of land that is cultivated for a growing season with only one crop (field use type) and +uniform management requirements or as a landscape element type in accordance with Annex 1 of the regulation of the responsible +Federal Ministry with horizontal rules for the area of the Common Agricultural Policy (Horizontal CAP Regulation) +StF: BGBl. II No. 100/2015 or is simply maintained in good agricultural and ecological condition in accordance with +Art. 94 of Regulation (EU) No. 1306/2013 and is digitized in the GIS as a polygon or as a point. + """ + provider = "Agrarmarkt Austria " + license = "CC-BY-4.0" + columns = { + "GEO_ID": "id", + "geometry": "geometry", + "SNAR_CODE": "crop:code", + "SNAR_BEZEICHNUNG": "crop:name", + "SL_FLAECHE_BRUTTO_HA": "metrics:area", + "GEOM_DATE_CREATED": "determination:datetime", + } + ec_mapping_csv = "https://fiboa.org/code/at/at.csv" diff --git a/build/lib/fiboa_cli/datasets/at_block.py b/build/lib/fiboa_cli/datasets/at_block.py new file mode 100644 index 00000000..8ec3806f --- /dev/null +++ b/build/lib/fiboa_cli/datasets/at_block.py @@ -0,0 +1,44 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = { + "https://inspire.lfrz.gv.at/009501/ds/inspire_referenzen_2021_polygon.gpkg.zip": [ + "INSPIRE_REFERENZEN_2021_POLYGON.gpkg" + ] + } + id = "at_block" + country = "AT" + short_name = "Austria (parcels)" + title = "Field boundaries for Austria" + description = """ +**Field boundaries for Austria - INVEKOS Referenzen Österreich 2021.** + +The layer includes all reference parcels ("Referenzparzellen") defined by the paying agency Agrarmarkt Austria and recorded landscape elements (landscape element layers) within the meaning of Art. 5 of Regulation (EU) No. 640/2014 and Regulation of the competent federal ministry with horizontal rules for the area of the Common Agricultural Policy (Horizontal CAP Regulation) StF: Federal Law Gazette II No. 100/2015. + +Reference parcel: is the physical block that can be clearly delimited from the outside (e.g. forest, roads, water bodies) and is formed by contiguous agricultural areas that are recognizable in nature. + """ + provider = "Agrarmarkt Austria " + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "RFL_ID": "id", + "REF_ART": "ref_art", + "BRUTTOFLAECHE_HA": "metrics:area", + "INSPIRE_ID": "inspire:id", + "REF_ART_BEZEICHNUNG": "ref_art_bezeichnung", + "REFERENZ_KENNUNG": "referenz_kennung", + "FART_ID": "fart_id", + "GEO_DATERF": "determination:datetime", + } + extensions = {"https://fiboa.org/inspire-extension/v0.3.0/schema.yaml"} + missing_schemas = { + "properties": { + "ref_art": {"type": "string"}, + "ref_art_bezeichnung": {"type": "string"}, + "referenz_kennung": {"type": "uint64"}, + "fart_id": {"type": "uint32"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/be_vlg.py b/build/lib/fiboa_cli/datasets/be_vlg.py new file mode 100644 index 00000000..17be52e0 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/be_vlg.py @@ -0,0 +1,68 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import AddHCATMixin + +PREFIX = "https://www.landbouwvlaanderen.be/bestanden/gis/" + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + str(k): { + PREFIX + v: [v.replace("_GPKG.zip", ".gpkg") if v.endswith("_GPKG.zip") else "*.gpkg"] + } + for k, v in ( + (2026, "agpa_2026_2026-06-02_public.zip"), + (2025, "Landbouwgebruikspercelen_2025_-_Voorlopig_(extractie_02-06-2025)_GPKG.zip"), + (2024, "Landbouwgebruikspercelen_2024_-_Definitief_(extractie_27-03-2025)_GPKG.zip"), + (2023, "Landbouwgebruikspercelen_2023_-_Definitief_(extractie_28-03-2024)_GPKG.zip"), + (2022, "Landbouwgebruikspercelen_2022_-_Definitief_(extractie_26-06-2023)_GPKG.zip"), + (2021, "Landbouwgebruikspercelen_2021_-_Definitief_(extractie_15-03-2022)_GPKG.zip"), + (2020, "Landbouwgebruikspercelen_2020_uitgebreid_toestand_19-03-2021_GPKG.zip"), + (2019, "Landbouwgebruikspercelen_2019_-_Definitief_(extractie_20-03-2020)_GPKG.zip"), + (2018, "Landbouwgebruikspercelen_2018_-_Definitief_(extractie_23-03-2022)_GPKG.zip"), + ) + } + id = "be_vlg" + short_name = "Belgium, Flanders" + admin_subdivision_code = "VLG" + title = "Field boundaries for Flanders, Belgium" + description = """ +Since 2020, the Department of Agriculture and Fisheries has been publishing a more extensive set of data related to agricultural use plots (from the 2008 campaign). +From 2023, the downloadable dataset of agricultural use plots will also include the specialization given by the company (= company typology) and that is given to the plots of the company. Based on the typology, the companies are divided into 4 major specializations: arable farming, horticulture, livestock farming and mixed farms. The specialization of each company is calculated annually according to a European method and is based on the standard output of the various agricultural productions on the company. It is therefore an economic specialization and not a reflection of all agricultural production on the company. + """ + + provider = "Agentschap Landbouw & Zeevisserij (Government) " + + attribution = "Bron: Dept. LV" + license = "Licentie modellicentie-gratis-hergebruik/v1.0 " + + # the 2026 "agpa" edition renamed every column to English + RENAMES_2026 = { + "reference_id": "REF_ID", + "maincrop_code": "GWSCOD_H", + "maincrop_title": "GWSNAM_H", + "area_ha": "GRAF_OPP", + } + + def migrate(self, gdf): + if "maincrop_code" in gdf.columns: + gdf = gdf.rename(columns=self.RENAMES_2026) + if "BT_OMSCH" not in gdf.columns: # no farm-typology column any more + gdf["BT_OMSCH"] = None + return super().migrate(gdf) + + columns = { + "geometry": "geometry", + "BT_OMSCH": "typology", + "GRAF_OPP": "metrics:area", + "REF_ID": "id", + "GWSCOD_H": "crop:code", + "GWSNAM_H": "crop:name", + } + # Each edition is the campaign year of its variant; the old constant + # "2024-03-28" was the extraction date of one edition applied to all of them. + use_variant_as_determination = True + ec_mapping_csv = "be_vlg_2021.csv" + + missing_schemas = {"properties": {"typology": {"type": "string"}}} diff --git a/build/lib/fiboa_cli/datasets/be_wal.py b/build/lib/fiboa_cli/datasets/be_wal.py new file mode 100644 index 00000000..99eb957e --- /dev/null +++ b/build/lib/fiboa_cli/datasets/be_wal.py @@ -0,0 +1,65 @@ +import geopandas as gpd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.convert_gml import gml_assure_columns +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = { + "https://geoservices.wallonie.be/geotraitement/spwdatadownload/get/2a0d9be0-ac3d-443e-9db0-a7cfb0f128e2/LU_ExistingLandUse_SIGEC2022.gml.zip?blocksize=0": [ + "LU_ExistingLandUse_SIGEC2022.gml" + ] + } + id = "be_wal" + admin_region_code = "WAL" + short_name = "Belgium, Wallonia" + title = "Belgium Wallonia: Parcellaire Agricole Anonyme" + description = """ +The Crop Fields (PAA) covers land use in agricultural and forestry areas managed as part of the implementation of the +Common Agricultural Policy by the Paying Agency of Wallonia. + +The PAA represents the public version of the agricultural plot. It therefore does not include personal information +allowing the operator to be identified. It is provided on an annual basis. Data from a year of cultivation are made +available to the public during the following year. + +The data is distributed in two ways: either at the source of the paying agency (more attributes +but no public distribution) or at the European Commission data portal (no limitations). We use the +free-licensed version for this converter. + """ + provider = "Inspire Geoportal of the European Commission " + license = "No conditions apply to access and use. Distributed through Inspire guidelines " + columns = { + "geometry": "geometry", + "crop_name": "crop:name", + "crop_code": "crop:code", + "id": "id", + "determination:datetime": "determination:datetime", + } + ec_mapping_csv = "be_wal_all_years.csv" + column_additions = { + "determination:datetime": "2022-01-01T00:00:00Z", + } + index_as_id = True + + def layer_filter(self, layer: str, uri: str) -> bool: + return layer == "ExistingLandUseObject" + + column_migrations = { + "crop_code": lambda col: col.str.extract(r"\.(\d+)$", expand=False), + "crop_name": lambda col: col.str.strip(), + } + + def file_migration( + self, gdf: gpd.GeoDataFrame, path: str, uri: str, layer: str = None + ) -> gpd.GeoDataFrame: + gdf = gml_assure_columns( + gdf, + path, + uri, + layer, + crop_name={"ElementPath": "specificLandUse@title", "Type": "String", "Width": 255}, + crop_code={"ElementPath": "specificLandUse@href", "Type": "String", "Width": 255}, + ) + return gdf diff --git a/build/lib/fiboa_cli/datasets/bg.py b/build/lib/fiboa_cli/datasets/bg.py new file mode 100644 index 00000000..c176a357 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/bg.py @@ -0,0 +1,39 @@ +from geopandas import GeoDataFrame +from vecorel_cli.conversion.admin import AdminConverterMixin + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter +from fiboa_cli.datasets.commons.data import read_data_csv + + +class BGConverter(AdminConverterMixin, FiboaBaseConverter): + sources = { + "http://inspire.mzh.government.bg:8080/geoserver/ows?request=GetFeature&service=WFS&version=2.0.0&outputFormat=SHAPE-ZIP&typeNames=VectorDataSet:Arable_Land_2024": "bg_arable_land_2024.zip" + } + + id = "bg" + short_name = "Bulgaria" + title = "Bulgaria" + license = "CC-BY-4.0" + provider = "Ministry of Health" + description = """ +Bulgarian Agriculture areas. Dataset has been produced from field checks and orthophotos mapping. +Categorized in Arable Land, Greenhouses, Mixed Land Use and Rice fields. + """ + + area_is_in_ha = False + columns = { + "geometry": "geometry", + "PHBIDENT": "id", + "USAGEENG": "crop:name", + "crop:code": "crop:code", + "AREA": "metrics:area", + } + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + column_additions = {"crop:code_list": "https://fiboa.org/code/bg/bg_arable.csv"} + + def migrate(self, gdf) -> GeoDataFrame: + gdf = super().migrate(gdf) + csv = read_data_csv("bg_arable.csv") + crop_to_code = {e["original_name"]: i + 1 for i, e in enumerate(csv)} + gdf["crop:code"] = gdf["USAGEENG"].map(crop_to_code) + return gdf diff --git a/build/lib/fiboa_cli/datasets/br_ba_lem.py b/build/lib/fiboa_cli/datasets/br_ba_lem.py new file mode 100644 index 00000000..d50df8cd --- /dev/null +++ b/build/lib/fiboa_cli/datasets/br_ba_lem.py @@ -0,0 +1,95 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = { + "https://data.mendeley.com/public-files/datasets/vz6d7tw87f/files/57c83c3f-b5a9-45f5-94f8-ac1df8fab923/file_downloaded": [ + "LEM_dataset.shp" + ] + } + id = "br_ba_lem" + country = "BR" + admin_subdivision_code = "BA" + short_name = "West Bahia, Brazil" + title = "Field boundaries for the west of Bahia state, Brazil" + description = """ +This dataset is the supplementary data of a paper published in the Data in Brief Journal. + +The dataset, in ESRI shapefile format (spatial reference system: WGS 84, EPSG: 4326), provides monthly land use +information about 1854 fields from October 2019 to September 2020 from Luís Eduardo Magalhães (LEM) and other +municipalities in the west of Bahia state, Brazil. The majority of the 16 land uses classes are related to crops. + """ + provider = "Mendeley Data " + attribution = "Copyright © 2024 Elsevier inc, its licensors, and contributors." + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "id": "id", + "Oct_2019": "2019-10", + "Nov_2019": "2019-11", + "Dec_2019": "2019-12", + "Jan_2020": "2020-01", + "Feb_2020": "2020-02", + "Mar_2020": "2020-03", + "Apr_2020": "2020-04", + "May_2020": "2020-05", + "Jun_2020": "2020-06", + "Jul_2020": "2020-07", + "Aug_2020": "2020-08", + "Sep_2020": "2020-09", + "note": "note", + } + type_schema = { + "type": "string", + "enum": [ + "Beans", + "Brachiaria", + "Cerrado", + "Coffee", + "Conversion area", + "Corn", + "Cotton", + "Crotalaria", + "Eucalyptus", + "Hay", + "Millet", + "Not identified", + "Pasture", + "Sorghum", + "Soybean", + "Uncultivated soil", + ], + } + missing_schemas = { + "required": [ + "2019-10", + "2019-11", + "2019-12", + "2020-01", + "2020-02", + "2020-03", + "2020-04", + "2020-05", + "2020-06", + "2020-07", + "2020-08", + "2020-09", + ], + "properties": { + "2019-10": type_schema, + "2019-11": type_schema, + "2019-12": type_schema, + "2020-01": type_schema, + "2020-02": type_schema, + "2020-03": type_schema, + "2020-04": type_schema, + "2020-05": type_schema, + "2020-06": type_schema, + "2020-07": type_schema, + "2020-08": type_schema, + "2020-09": type_schema, + "note": {"type": "string"}, + }, + } diff --git a/build/lib/fiboa_cli/datasets/br_conab.py b/build/lib/fiboa_cli/datasets/br_conab.py new file mode 100644 index 00000000..2298946a --- /dev/null +++ b/build/lib/fiboa_cli/datasets/br_conab.py @@ -0,0 +1,107 @@ +import math +from pathlib import Path + +import numpy as np +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + _sources = [ + "Algodao/GO/ALGODAO_GO_Safra_2019_2020.zip", + "Algodao/GO/ALGODAO-GO_Safra_2018_2019.zip", + "Algodao/GO/GO_ALGODAO_2021.zip", + "Algodao/GO/GO_ALGODAO_2223.zip", + "Algodao/MS/MS_ALGODAO_2021.zip", + "Algodao/MS/MS_ALGODAO_2122.zip", + "Arroz_Irrigado/GO/GO_ARROZ_IRRIG_2122.zip", + "Arroz_Irrigado/GO/GO_ARROZ_IRRIG_INUND_2324.zip", + "Arroz_Irrigado/GO/ARROZ-GO_Safra_2018_2019.zip", + "Arroz_Irrigado/MS/ARROZ-MS_Safra_2018_2019.zip", + "Arroz_Irrigado/PR/ARROZ-PR_Safra_2017_2018.zip", + "Arroz_Irrigado/RS/ARROZ-RS_Safra_2019_2020.zip", + "Arroz_Irrigado/SC/ARROZ-SC_Safra_2018_2019.zip", + "Arroz_Irrigado/TO/ARROZ-TO_Safra_2017_2018.zip", + "Arroz_Irrigado/GO/GO_ARROZ_IRRIG_INUND_2324.zip", + "Arroz_Irrigado/TO/TO_ARROZ_IRRIG_2324.zip", + "Cana/GO/CANA-GO_Safra_2011_2012.zip", + "Cafe/BA/CAFE-BA_Safra_2019.zip", + "Cafe/DF/CAFE-DF_Safra_2018.zip", + "Cafe/GO/CAFE-GO_Safra_2018.zip", + "Cafe/GO/CAFE-GO_Safra_2019.zip", + "Cafe/PR/CAFE-PR_Safra_2017.zip", + "Cafe/MG/CAFE-MG_Safra_2017.zip", + "Cafe/DF/DF_CAFE_24.zip", + "Cafe/DF/DF_CAFE_24.zip", + "Cafe/GO/GO_CAFE_21.zip", + "Cafe/RJ/RJ_CAFE_21.zip", + "Culturas_de_Verao_1_Safra/DF/CV-DF_Safra_2013_2014.zip", + "Culturas_de_Verao_1_Safra/DF/CV-DF_Safra_2014_2015.zip", + "Culturas_de_Verao_1_Safra/DF/CV-DF_Safra_2017_2018.zip", + "Culturas_de_Verao_1_Safra/TO/CV-TO_Safra_2019_2020.zip", + ] + sources = { + "https://portaldeinformacoes.conab.gov.br/downloads/mapas/" + k: ["*.shp"] for k in _sources + } + id = "br_conab" + short_name = "Conab" + title = "Brazil Crop Fields (CONAB)" + description = """ +CONAB, Brazil's National Supply Company, is the government agency responsible for providing information on the country's agricultural harvest. + +This subset of 27, after inspecting all boundaries in the CONAB public database, appear to be hand-drawn field boundaries. + +The content of the Mappings comes from Conab, total or partial reproduction without profit motives is authorized, +as long as the source is cited and the integrity of the information is maintained. + +Further information or suggestions can be sent to the email address conab.geote@conab.gov.br + """ + provider = ( + "Conab " + ) + attribution = "CONAB - conab.gov.br" + license = "CC-BY-NC-4.0" + columns = { + "geometry": "geometry", + "id": "id", + "cd_mun": "admin_municipality_code", + "nm_mun": "admin_municipality_name", + "area_ha": "metrics:area", + } + + missing_schemas = { + "properties": { + "admin_municipality_code": {"type": "string"}, + "admin_municipality_name": {"type": "string"}, + } + } + + def file_migration(self, gdf, path, uri, layer=None): + gdf = super().file_migration(gdf, path, uri, layer) + # Create unique IDs + name = Path(path).stem + gdf["id"] = name + "_" + gdf.index.astype(str) + # Harmonize projection or pd.concat will fail + if gdf.crs.srs != "EPSG:4674": + gdf.to_crs(crs="EPSG:4674", inplace=True) + return gdf + + def migrate(self, gdf): + gdf = gdf.reset_index(drop=True) + gdf["area_ha"].combine_first(gdf["Hectares"]).replace(np.nan, None, inplace=True) + gdf.loc[gdf["area_ha"] == 0, "area_ha"] = None + gdf["cd_mun"] = gdf["cd_mun"].combine_first(gdf["CD_MUN"]).apply(fformat) + gdf["nm_mun"] = gdf["nm_mun"].combine_first(gdf["NM_MUN"]).combine_first(gdf["NM_MUNIC"]) + return super().migrate(gdf) + + def get_data(self, paths, **kwargs): + # Set invalid geometries to None in Cafe/MG/CAFE-MG_Safra_2017.zip + kwargs["on_invalid"] = "warn" + return super().get_data(paths, **kwargs) + + +def fformat(x): + if isinstance(x, float) and not math.isnan(x): + return f"{x:.0f}" + return x or None diff --git a/build/lib/fiboa_cli/datasets/ch.py b/build/lib/fiboa_cli/datasets/ch.py new file mode 100644 index 00000000..65c0156b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ch.py @@ -0,0 +1,47 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = None + data_access = """ + Data must be obtained from the Swiss open data portal at https://www.geodienste.ch/services/lwb_nutzungsflaechen . + + One can filter on "Verfügbarkeit" == "Frei erhältlich" to select only the open data. + That leaves out Cantons AR, NW, OW, VD and LI as on this date (2014-11-12). + The downloaded data can be shared with a open_by license. See https://opendata.swiss/de/terms-of-use . + + Use the `-i` CLI parameter to provide the data source. + Download the Open data response to a local gpkg file (use `.gpkg` as file extension). + + fiboa convert ch -o swiss.parquet -i lwb_nutzungsflaechen_lv95/geopackage/lwb_nutzungsflaechen_v2_0_lv95.gpkg + """ + id = "ch" + short_name = "Switzerland" + title = "Field boundaries for Switzerland" + description = "The cropfields of Switzerland (Nutzungsflächen) are published per administrative subdivision called Canton." + provider = ( + "Konferenz der kantonalen Geoinformations- und Katasterstellen " + ) + index_as_id = True + license = "opendata.swiss terms of use " + columns = { + "geometry": "geometry", + "id": "id", + "flaeche_m2": "metrics:area", + "kanton": "admin:subdivision_code", + "nutzung": "crop:name", + "bezugsjahr": "determination:datetime", + } + column_filters = { + "ist_ueberlagernd": lambda col: col == False, # noqa: E712 + } + area_is_in_ha = False + area_calculate_missing = True + column_migrations = { + "bezugsjahr": lambda col: pd.to_datetime(col, format="%Y"), + } + ec_mapping_csv = "https://fiboa.org/code/ch/ch.csv" diff --git a/build/lib/fiboa_cli/datasets/commons/data.py b/build/lib/fiboa_cli/datasets/commons/data.py new file mode 100644 index 00000000..3e61ca3d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/commons/data.py @@ -0,0 +1,8 @@ +from csv import DictReader +from os.path import dirname, join + + +def read_data_csv(name, **kwargs): + path = join(dirname(dirname(__file__)), "data-files", name) + with open(path, "r", encoding="utf-8") as f: + return list(DictReader(f, **kwargs)) diff --git a/build/lib/fiboa_cli/datasets/commons/ec.py b/build/lib/fiboa_cli/datasets/commons/ec.py new file mode 100644 index 00000000..485e485d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/commons/ec.py @@ -0,0 +1,31 @@ +from fiboa_cli.datasets.commons.hcat import AddHCATMixin, ec_url, load_ec_mapping # noqa: F401 + + +class EuroCropsConverterMixin(AddHCATMixin): + """ + Adds HCAT columns to a GeoDataFrame, useful for transforming datasets supplied by the Eurocrops project. + The Eurocrops files have their own column names, so we need to map them to HCAT extension names. + Also modifies the dataset title and provider to reflect the source. + """ + + ec_year = None + hcat_columns = { + "EC_trans_n": "hcat:name_en", + "EC_hcat_n": "hcat:name", + "EC_hcat_c": "hcat:code", + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.id.startswith("ec_"): + self.id = "ec_" + self.id + suffix = " - Eurocrops" + if self.ec_year is not None: + suffix = f"{suffix} {self.ec_year}" + + self.title += suffix + self.short_name += suffix + + provider = "EuroCrops " + self.provider = (f"{self.provider}, {provider}") if self.provider else provider + self.license = "CC-BY-SA-4.0" diff --git a/build/lib/fiboa_cli/datasets/commons/euro_land.py b/build/lib/fiboa_cli/datasets/commons/euro_land.py new file mode 100644 index 00000000..5294c22e --- /dev/null +++ b/build/lib/fiboa_cli/datasets/commons/euro_land.py @@ -0,0 +1,57 @@ +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter +from fiboa_cli.datasets.commons.hcat import AddHCATMixin + + +class EuroLandBaseConverter(AddHCATMixin, FiboaBaseConverter): + """ + Datasets have been published by the + [Euroland project](https://europe-land.eu/news/harmonized-database-of-european-land-use-data-published/) + as open data. See https://zenodo.org/records/14384070 for a list of open data sets. + + Use this base class to create converters based on the euroland repository + Subclasses should still declare the required attributes from BaseConverter + + id = "" + short_name = "" + title = "" + description = "" + provider = "" + """ + + hcat_columns = { + "EC_trans_n": "hcat:name_en", + "EC_hcat_n": "hcat:name", + "EC_hcat_c": "hcat:code", + } + + columns = { + "geometry": "geometry", + "field_id": "id", + "farm_id": "farm_id", + "crop:code_list": "crop:code_list", + "crop_code": "crop:code", + "crop_name": "crop:name", + "organic": "organic", + "field_size": "metrics:area", + # "crop_area": "crop_area", + } + license = "CC-BY-4.0" + missing_schemas = { + "properties": { + "farm_id": {"type": "string"}, + "organic": {"type": "uint8", "enum": [0, 1, 2]}, + } + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + provider = "Europe-LAND HE Project " + self.provider = (f"{self.provider}, {provider}") if self.provider else provider + + def migrate(self, gdf): + # Some Europe-LAND files (e.g. LT 2024) ship an empty crop_code column next to + # a populated crop_name; the name is then the best available crop code. + if "crop_code" in gdf.columns and gdf["crop_code"].isna().all(): + self.warning("crop_code is empty, using crop_name as crop:code") + gdf["crop_code"] = gdf["crop_name"] + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/commons/hcat.py b/build/lib/fiboa_cli/datasets/commons/hcat.py new file mode 100644 index 00000000..4794097b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/commons/hcat.py @@ -0,0 +1,125 @@ +import csv +from io import StringIO +from typing import Optional + +import geopandas as gpd +import numpy as np +import pandas as pd +from vecorel_cli.vecorel.util import load_file + +HCAT_EXTENSION = "https://fiboa.org/hcat-extension/v0.3.0/schema.yaml" +CROP_EXTENSION = "https://fiboa.org/crop-extension/v0.2.0/schema.yaml" + + +class AddHCATMixin: + """ + Adds HCAT columns to a GeoDataFrame, based on the crop-extension crop:code column and a specified csv-mapping + Automatically adds crop:code_list to the columns, and adds HCAT and CROP extensions. + """ + + ec_mapping_csv: Optional[str] = None # TODO rename to hcat_mapping_csv + mapping_file = None + ec_mapping: Optional[list[dict]] = None # TODO rename to hcat_mapping + + hcat_columns = { + "hcat:name_en": "hcat:name_en", + "hcat:name": "hcat:name", + "hcat:code": "hcat:code", + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.columns |= self.hcat_columns | {"crop:code_list": "crop:code_list"} + self.extensions = getattr(self, "extensions", set()) | {CROP_EXTENSION, HCAT_EXTENSION} + + def convert(self, *args, **kwargs): + self.mapping_file = kwargs.get("mapping_file") + if not self.mapping_file: + assert isinstance(self.ec_mapping_csv, str), ( + "Specify proper ec_mapping_csv in Converter, e.g. find them at https://github.com/maja601/EuroCrops/tree/main/csvs/country_mappings" + ) + return super().convert(*args, **kwargs) + + def get_code_column(self, gdf, code="crop:code"): + try: + attribute = next(k for k, v in self.columns.items() if v == code) + except StopIteration: + raise Exception(f"Misssing {code} column in converter {self.__class__.__name__}") + col = gdf[attribute] + # Should be corrected in original parser + return col if col.dtype == "object" else col.astype(str) + + def add_hcat(self, gdf): + # Lookup column that will be renamed after the migration to hcat:code + hcat_code_column = next(k for k, v in self.hcat_columns.items() if v == "hcat:code") + if hcat_code_column not in gdf.columns: + code_sources = [k for k, v in self.columns.items() if v in ("crop:code", "crop:name")] + if not any(k in gdf.columns for k in code_sources): + # this edition carries no crop columns at all: nothing to map + return gdf + # Add HCAT columns based on crop-columns + # Map to HCAT categories by using the mapping from the csv file + + if self.ec_mapping is None: + self.ec_mapping = load_ec_mapping(self.ec_mapping_csv, url=self.mapping_file) + + from_code = "original_code" + if from_code not in self.ec_mapping[0]: + # Some code lists have no code, only a crop_name + from_code = "original_name" + crop_code_col = self.get_code_column(gdf, "crop:name") + else: + crop_code_col = self.get_code_column(gdf) + + def map_to(attribute): + return {e[from_code]: e[attribute] or None for e in self.ec_mapping} + + col = None + for k, v in zip( + self.hcat_columns.keys(), ("translated_name", "HCAT3_name", "HCAT3_code") + ): + if v in self.ec_mapping[0]: + col = crop_code_col.map(map_to(v)) + gdf[k] = col + assert np.unique(col[~col.isna()]).size > 0, "No HCAT crops mapped" + + if col is not None and col.isna().any(): + index = [ + k for k, v in self.columns.items() if v.startswith("crop:") and k in gdf.columns + ] + missing = gdf[col.isna()][index].drop_duplicates() + missing.reset_index(drop=True, inplace=True) + with pd.option_context( + "display.max_colwidth", + None, + "display.max_columns", + None, + "display.max_rows", + None, + ): + self.info(f"Missing codes in HCAT mapping:\n{missing}") + + if "crop:code_list" not in gdf.columns: + gdf["crop:code_list"] = ( + ec_url(self.ec_mapping_csv) if self.ec_mapping_csv else self.mapping_file + ) + return gdf + + def post_migrate(self, gdf) -> gpd.GeoDataFrame: + gdf = super().post_migrate(gdf) + return self.add_hcat(gdf) + + +def ec_url(csv_file): + if csv_file.startswith("https://"): + return csv_file + return f"https://raw.githubusercontent.com/maja601/EuroCrops/refs/heads/main/csvs/country_mappings/{csv_file}" + + +def load_ec_mapping(csv_file=None, url=None): + if not (csv_file or url): + raise ValueError("Either csv_file or url must be specified") + if not url: + url = ec_url(csv_file) + content = load_file(url) + return list(csv.DictReader(StringIO(content.decode("utf-8")))) diff --git a/build/lib/fiboa_cli/datasets/cz.py b/build/lib/fiboa_cli/datasets/cz.py new file mode 100644 index 00000000..ad04aafb --- /dev/null +++ b/build/lib/fiboa_cli/datasets/cz.py @@ -0,0 +1,47 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + +BASE = "https://agrigis.gov.cz/portal/sharing/rest/content/items/{}/data" +# Check data on https://agrigis.gov.cz/portal/apps/storymaps/stories/99ddc665f57a4843b878e86c23e99b31 +ITEMS = { + 2026: "7bcdda9b19724faba447585683c4cfd1", + 2025: "2cac84bb1f5245598f0334c6011ef5a6", + 2024: "1b315e81ce474b3b808b4940808bb106", + 2023: "d9a6e306fe534a059519fdf788da1df6", + 2022: "791cd91c4f354c9085173fc267b2be4d", + 2021: "c662c15b70794a06937096be54c095ab", + 2020: "c843561778b44b308485aafdbb813d76", + 2019: "9cbc2b4429704b73863596fa5f488d27", +} + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + # see https://mze.gov.cz/public/app/eagriapp/lpisdata/ + # the 2026 archive nests the shapefile in a folder, older ones are flat + variants = {str(k): {BASE.format(v): ["**/*.shp"]} for k, v in ITEMS.items()} + id = "cz" + short_name = "Czech" + title = "Field boundaries for Czech" + description = "The cropfields of Czech (Plodina)" + provider = "Czech Ministry of Agriculture (Ministr Zemědělství) " + license = "CC0-1.0" + columns = { + "geometry": "geometry", + "ZAKRES_ID": "id", + "DPB_ID": "block_id", + "PLODINA_ID": "crop:code", + "PLOD_NAZE": "crop:name", + "ZAKRES_VYM": "metrics:area", + "DATUM_REP": "determination:datetime", + # 'OKRES_NAZE': 'admin:subdivision_code', + } + column_migrations = {"DATUM_REP": lambda col: pd.to_datetime(col, format="%d.%m.%Y")} + ec_mapping_csv = "cz_2023.csv" + missing_schemas = { + "properties": { + "block_id": {"type": "string"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/de_bb.py b/build/lib/fiboa_cli/datasets/de_bb.py new file mode 100644 index 00000000..c25d734d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_bb.py @@ -0,0 +1,36 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = "https://data.geobasis-bb.de/geofachdaten/Landwirtschaft/antrag.zip" + id = "de_bb" + admin_subdivision_code = "BB" # TODO Berlin is also in here, check each row + short_name = "Germany, Berlin/Brandenburg" + title = "Field boundaries for Berlin / Brandenburg, Germany" + description = """A Crop Field (German: "Schlaege") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated with a single crop.""" + license = "DL-DE-BY-2.0" + provider = "Land Brandenburg " + ec_mapping_csv = "de.csv" + # The .cpg claims UTF-8 but the DBF is cp1252 (June 2026 download) + open_options = dict(encoding="cp1252") + + columns = { + "geometry": "geometry", + "ref_ident": "farmer_id", + "groesse": "metrics:area", + "guelt_von": "determination:datetime", + "code_bez": "crop:name", + "code": "crop:code", + } + missing_schemas = { + "properties": { + "farmer_id": {"type": "string"}, + } + } + # todo: The dataset has null values for crop code, but the crop extension + # requires a string. We set them to empty strings for now, + # but it should be reconsidered in the future. + column_migrations = {"code": lambda col: col.fillna("").astype(str)} diff --git a/build/lib/fiboa_cli/datasets/de_bb_block.py b/build/lib/fiboa_cli/datasets/de_bb_block.py new file mode 100644 index 00000000..ee48292b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_bb_block.py @@ -0,0 +1,42 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = {"https://data.geobasis-bb.de/geofachdaten/Landwirtschaft/dfbk.zip": ["DFBK_FB.shp"]} + id = "de_bb_block" + admin_subdivision_code = "BB" + short_name = "Germany, Berlin/Brandenburg (parcels)" + title = "Field boundaries for Berlin / Brandenburg, Germany" + description = """A field block (German: "Feldblock") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated by one or more farmers with one or more crops, is fully or partially set aside or is fully or partially taken out of production.""" + license = "DL-DE-BY-2.0" + provider = "Land Brandenburg " + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + + columns = { + "geometry": "geometry", + "FB_ID": ("flik", "id"), + "FGUE_JAHR": "fgue_jahr", + "FL_BRUTTO": "metrics:area", + "FL_NETTO": "net_area", + "GUELTVON_F": "determination:datetime", + "GUELTBIS_F": "expiry_datetime", + "KREIS_NR": "kreis_nr", + "TK10_BLATT": "tk10", + "HBN_KAT": "hbn", + "SHAPE_LEN": "metrics:perimeter", + } + missing_schemas = { + "properties": { + "hbn": {"type": "string"}, + "fgue_jahr": {"type": "string"}, + "net_area": {"type": "float", "exclusiveMinimum": 0}, + "expiry_datetime": {"type": "date-time"}, + "kreis_nr": {"type": "uint16"}, + "tk10": {"type": "string"}, + } + } + + def layer_filter(self, layer: str, uri: str) -> bool: + return layer == "DFBK_FB" diff --git a/build/lib/fiboa_cli/datasets/de_by.py b/build/lib/fiboa_cli/datasets/de_by.py new file mode 100644 index 00000000..70b29262 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_by.py @@ -0,0 +1,41 @@ +import geopandas as gpd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin, load_ec_mapping + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = "https://geodaten.bayern.de/odd/m/3/daten/ln/landnutzung.gpkg" + avoid_range_request = True + + id = "de_by" + admin_subdivision_code = "BY" + short_name = "Germany, Bavaria" + title = "Field boundaries for Bavaria, Germany" + description = """A field block (German: "Feldblock") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated by one or more farmers with one or more crops, is fully or partially set aside or is fully or partially taken out of production.""" + license = "CC-BY-4.0" + attribution = "Datenquelle: Bayerische Vermessungsverwaltung – www.geodaten.bayern.de" + provider = "Bayerische Vermessungsverwaltung " + mapping_file = "https://fiboa.org/code/de/de_by.csv" + ec_mapping_csv = "https://fiboa.org/code/de/de_by.csv" + + columns = { + "geometry": "geometry", + "uuid": "id", + "datumderletztenueberpruefung": "determination:datetime", + "bewirtschaftung": "crop:code", + "crop:name": "crop:name", + } + + def layer_filter(self, layer: str, uri: str) -> bool: + return layer == "ln_landwirtschaft" + + def migrate(self, gdf: gpd.GeoDataFrame): + gdf = super().migrate(gdf) + self.ec_mapping = load_ec_mapping(self.ec_mapping_csv, url=self.mapping_file) + gdf = gdf[gdf["bewirtschaftung"].isin([row["original_code"] for row in self.ec_mapping])] + + mapping_crop = {row["original_code"]: row["original_name"] for row in self.ec_mapping} + gdf["crop:name"] = gdf["bewirtschaftung"].map(mapping_crop) + return gdf diff --git a/build/lib/fiboa_cli/datasets/de_mv.py b/build/lib/fiboa_cli/datasets/de_mv.py new file mode 100644 index 00000000..ce62ff95 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_mv.py @@ -0,0 +1,79 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import CROP_EXTENSION + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = { + "https://www.geodaten-mv.de/dienste/gdimv_feldblock_wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=mv:feldbloecke&OUTPUTFORMAT=shape-zip": "gdimv_feldblock_wfs.zip" + } + id = "de_mv" + admin_subdivision_code = "MV" + short_name = "Germany, Mecklenburg-Western Pomerania" + title = "Field boundaries for Mecklenburg-Western Pomerania, Germany" + description = "Field block register of the Ministry of Agriculture and Environment M-V" + + provider = "Ministerium für Landwirtschaft und Umwelt M-V " + license = "No restrictions apply " + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml", CROP_EXTENSION} + column_additions = {"crop:code_list": "https://fiboa.org/code/de/de_mv.csv"} + + # ec_mapping_csv = "de.csv" + + columns = { + "geometry": "geometry", + "fbid": ("id", "flik"), # make flik id a dedicated column to align with NRW etc. + "dgl_jahr": "dgl_jahr", + "bodennutzu": "crop:code", # Bodennutzungsart + "bez_kreis": "bez_kreis", # Kreisbezeichnung + "groesse_p": "metrics:area", # Produktive Fläche des FB in Hektar (Nettofläche) + "perimeter": "metrics:perimeter", # Polygonumfang + "erwind": "erwind", # Gefährdungsklasse des Feldblockes gegenüber Winderosion nach DIN 19708 + "erwater": "erwater", # Gefährdungsklasse des Feldblockes gegenüber Wassererosion nach DIN 19708 + "erwind_l": "erwind_l", + "erwater_l": "erwater_l", + } + iso_schema = { + "type": "string", + "enum": [ + "Enat0", # keine bis sehr geringe Erosionsgefährdung + "Enat0-EE", + "Enat1", # sehr geringe Erosionsgefährdung + "Enat1-EE", + "Enat2", # geringe Erosionsgefährdung + "Enat2-EE", + "Enat3", # mittlere Erosionsgefährdung + "Enat3-EE", + "Enat4", # hohe Erosionsgefährdung + "Enat4-EE", + "Enat5", # sehr hohe Erosionsgefährdung + "Enat5-EE", + "-", # Keine Angabe + ], + } + missing_schemas = { + "properties": { + "dgl_jahr": {"type": "int16"}, + "bez_kreis": {"type": "string"}, + "erwind": { + "type": "string", + "enum": [ + "0", # nicht relevant für Cross Compliance + "1", # Erosionsgefährdung nach Direktzahlungen-Verpflichtungenverordnung + "-", # keine Angabe + ], + }, + "erwater": { + "type": "string", + "enum": [ + "0", # nicht relevant für Cross Compliance + "1", # Erosionsgefährdung nach Direktzahlungen-Verpflichtungenverordnung (15-27,5 t/ha/a Bodenabtrag durch Wasser) + "2", # hohe Erosionsgefährdung nach Direktzahlungen-Verpflichtungenverordnung (>27,5 t/ha/a Bodenabtrag durch Wasser) + "-", # keine Angabe + ], + }, + "erwind_l": iso_schema, + "erwater_l": iso_schema, + } + } diff --git a/build/lib/fiboa_cli/datasets/de_nds.py b/build/lib/fiboa_cli/datasets/de_nds.py new file mode 100644 index 00000000..732a6d60 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_nds.py @@ -0,0 +1,62 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + """ + https://sla.niedersachsen.de/agrarfoerderung/schlaginfo/ (see download) + The zip contains: + - Schlaege = UD_25_S.shp + - TeilLandschaftElemente = UD_25_TLE.shp + - TeilSchlaege = UD_25_TS.shp + """ + + variants = { + f"{year}": { + f"https://sla.niedersachsen.de/mapbender_sla/download/schlaege_aktuell_{year}.zip": [ + f"UD_{year % 100}_S.shp" + ] + } + for year in range(2025, 2020, -1) + } + id = "de_nds" + admin_subdivision_code = "NI" + short_name = "Germany, Lower Saxony/Bremen/Hamburg" + title = "Crop Fields for Lower Saxony / Bremen / Hamburg, Germany" + description = """A Crop Field (German: "Schlaege") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated with a single crop.""" + provider = "ML/SLA Niedersachsen " + attribution = "© ML/SLA Niedersachsen (2024), DL-DE-BY-2.0 (www.govdata.de/DL-DE-BY-2.0), Daten bearbeitet" + license = "DL-DE-BY-2.0" + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + + # https://www.sla.niedersachsen.de/download/141235/Verzeichnis_Nutzungscodes.xlsx + ec_mapping_csv = "de.csv" + columns = { + "geometry": "geometry", + "FLIK": "flik", + "SCHLAGNR": "subfield_id", + "NC_FESTG": "crop:code", + "ANTRAGSJAH": "determination:datetime", + "AKTUELLEFL": "metrics:area", + } + missing_schemas = { + "properties": { + "subfield_id": {"type": "int64"}, + } + } + column_migrations = {"ANTRAGSJAH": lambda col: pd.to_datetime(col, format="%Y")} + + def migrate(self, gdf): + if "NC_FESTG" not in gdf.columns: + code = {"KULTURARTF", "KULTURCODE", "KC_FESTG"} & set(gdf.columns) + del self.columns["NC_FESTG"] + self.columns[code.pop()] = "crop:code" + + if "AKTUELLEFL" not in gdf.columns: + del self.columns["AKTUELLEFL"] + self.columns["AKT_FL"] = "metrics:area" + + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/de_nds_block.py b/build/lib/fiboa_cli/datasets/de_nds_block.py new file mode 100644 index 00000000..272a9cf1 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_nds_block.py @@ -0,0 +1,33 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = "https://sla.niedersachsen.de/mapbender_sla/download/FB_NDS.zip" + id = "de_nds_block" + admin_subdivision_code = "NI" + short_name = "Germany, Lower Saxony/Bremen/Hamburg (parcels)" + title = "Field boundaries for Lower Saxony / Bremen / Hamburg, Germany" + description = """A field block (German: "Feldblock") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated by one or more farmers with one or more crops, is fully or partially set aside or is fully or partially taken out of production.""" + provider = "ML/SLA Niedersachsen " + attribution = "© ML/SLA Niedersachsen (2024), DL-DE-BY-2.0 (www.govdata.de/DL-DE-BY-2.0), Daten bearbeitet" + license = "DL-DE-BY-2.0" + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + columns = { + "geometry": "geometry", + "FLIK": ("id", "flik"), + "STAND": "determination:datetime", + "ANT_JAHR": "ant_jahr", + "BNK": "bnk", + "BNK_TXT": "bnk_txt", + "FLAECHE": "metrics:area", + "SHAPE_Leng": "metrics:perimeter", + } + missing_schemas = { + "properties": { + "ant_jahr": {"type": "int16"}, + "bnk": {"type": "string"}, + "bnk_txt": {"type": "string"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/de_nrw.py b/build/lib/fiboa_cli/datasets/de_nrw.py new file mode 100644 index 00000000..58f95c86 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_nrw.py @@ -0,0 +1,29 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = "https://www.opengeodata.nrw.de/produkte/umwelt_klima/bodennutzung/landwirtschaft/LWK-TSCHLAG_EPSG25832_Shape.zip" + id = "de_nrw" + admin_subdivision_code = "NW" + short_name = "Germany, North Rhine-Westphalia" + title = "Field boundaries for North Rhine-Westphalia (NRW), Germany" + description = """A field block (German: "Feldblock") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated by one or more farmers with one or more crops, is fully or partially set aside or is fully or partially taken out of production.""" + license = "DL-DE-BY-2.0" + provider = "Land Nordrhein-Westfalen / Open.NRW " + extensions = { + "https://fiboa.org/inspire-extension/v0.3.0/schema.yaml", + "https://fiboa.org/flik-extension/v0.2.0/schema.yaml", + } + ec_mapping_csv = "de_nrw_2021.csv" + columns = { + "geometry": "geometry", + "ID": "id", + "FLIK": "flik", + "BEGINLIFES": "determination:datetime", + "CODE": "crop:code", + "CODE_TXT": "crop:name", + "AREA_HA": "metrics:area", + } diff --git a/build/lib/fiboa_cli/datasets/de_sax.py b/build/lib/fiboa_cli/datasets/de_sax.py new file mode 100644 index 00000000..5412f708 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_sax.py @@ -0,0 +1,70 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = { + "https://www.smul.sachsen.de/gis-online/download/FBZ_ISS_Bereiche/gesamt_2024_RE.zip": [ + "2024_RE_FB_33.shp" + ] + } + id = "de_sax" + admin_subdivision_code = "SN" + short_name = "Germany, Saxony" + title = "Field boundaries for Saxony, Germany" + description = "Feldblöcke und förderfähige Elemente in Sachsen 2024" + provider = "Sächsisches Landesamt für Umwelt, Landwirtschaft und Geologie " + attribution = "Sächsisches Landesamt für Umwelt, Landwirtschaft und Geologie" + license = "DL-DE-BY-2.0" + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + columns = { + "geometry": "geometry", + "FB_FLIK": ("id", "flik"), + "JAHR": "determination:datetime", + "FB_A_FLAE": "metrics:area", + "FB_BN_KAT": "FB_BN_KAT", + "FB_BEZEICH": "FB_BEZEICH", + "ZUSTAENDIG": "ZUSTAENDIG", + "FB_FFH": "FB_FFH", + "FB_SPA": "FB_SPA", + "FB_NB": "FB_NB", + "NITRAT": "NITRAT", + "WT_WRRL": "WT_WRRL", + "NITRAT_TG": "NITRAT_TG", + "KWIND": "KWIND", + "KWASSER": "KWASSER", + "AGROFORST": "AGROFORST", + "AGRIPV": "AGRIPV", + "GLOEZ2": "GLOEZ2", + "OER_UNZUL": "OER_UNZUL", + "REG_SAAT": "REG_SAAT", + "BERG": "BERG", + } + missing_schemas = { + "properties": { + "FB_BN_KAT": {"type": "string"}, + "FB_BEZEICH": {"type": "string"}, + "ZUSTAENDIG": {"type": "uint8"}, + "FB_FFH": {"type": "boolean"}, + "FB_SPA": {"type": "boolean"}, + "FB_NB": {"type": "string"}, + "NITRAT": {"type": "boolean"}, + "WT_WRRL": {"type": "boolean"}, + "NITRAT_TG": {"type": "boolean"}, + "KWIND": {"type": "uint8"}, + "KWASSER": {"type": "uint8"}, + "AGROFORST": {"type": "boolean"}, + "AGRIPV": {"type": "boolean"}, + "GLOEZ2": {"type": "boolean"}, + "OER_UNZUL": {"type": "string"}, + "REG_SAAT": {"type": "string"}, + "BERG": {"type": "uint8"}, + } + } + column_migrations = {"JAHR": lambda col: pd.to_datetime(col, format="%Y")} + # Add boolean column migrations dynamically + for key, schema in missing_schemas["properties"].items(): + if schema["type"] == "boolean": + column_migrations[key] = lambda col: col.map({"J": True, "N": False}).astype(bool) diff --git a/build/lib/fiboa_cli/datasets/de_sh.py b/build/lib/fiboa_cli/datasets/de_sh.py new file mode 100644 index 00000000..5c594fb5 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_sh.py @@ -0,0 +1,28 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + variants = { + str( + y + ): f"https://service.gdi-sh.de/SH_OpenGBD/feeds/Atom_SH_Feldblockfinder_OpenGBD/data/Feldbloecke_{y}_GPKG.zip" + for y in range(2026, 2023 - 1, -1) + } + id = "de_sh" + admin_subdivision_code = "SH" + short_name = "Germany, Schleswig-Holstein" + title = "Field boundaries for Schleswig-Holstein (SH), Germany" + description = """A field block (German: "Feldblock") is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated by one or more farmers with one or more crops, is fully or partially set aside or is fully or partially taken out of production.""" + provider = "Land Schleswig-Holstein " + license = "DL-DE-ZERO-2.0" + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + columns = { + "geometry": "geometry", + "fachguelti": "determination:datetime", + "FLIK": ("flik", "id"), + "Flaeche": "metrics:area", + "HBN": "hbn", + } + missing_schemas = {"properties": {"hbn": {"type": "string"}}} diff --git a/build/lib/fiboa_cli/datasets/de_sl.py b/build/lib/fiboa_cli/datasets/de_sl.py new file mode 100644 index 00000000..5ab3185e --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_sl.py @@ -0,0 +1,57 @@ +import re + +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +def parse_flik(x): + match = re.search(r"flik:\s*([A-Z]{6}\d{10})", x, re.I) + return match.group(1) if match else None + + +def parse_size(x): + match = re.search(r"Size in ha: (\d+(\.\d+)?)+", x, re.I) + return float(match.group(1)) if match else None + + +url = "https://geoportal.saarland.de/gdi-sl/inspirewfs_Existierende_Bodennutzung_Antragsschlaege?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&typeNames=elu:ExistingLandUseObject&outputFormat=application/gml%2Bxml;%20version=3.2&EPSG=4258&BBOX={bbox}" +bboxes = [ + [49.1, 6.5423790007724, 49.332379000772, 6.7747580015449], + [49.1, 6.7747580015449, 49.332379000772, 7.0071370023173], + [49.1, 7.0071370023173, 49.216189500386, 7.2395160030898], + [49.216189500386, 7.0071370023173, 49.332379000772, 7.2395160030898], + [49.1, 7.2395160030898, 49.332379000772, 7.4718950038622], + [49.332379000772, 6.31, 49.564758001545, 6.5423790007724], + # Add more bboxes if needed +] + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = { + url.format(bbox=",".join(map(str, bbox))): f"{i}.gml" + for i, bbox in enumerate(bboxes, start=1) + } + + id = "de_sl" + admin_subdivision_code = "SL" + short_name = "Germany, Saarland" + title = "Field boundaries for Saarland, Germany" + description = """This dataset contains data transformed into the INSPIRE data model “Land Use” of the IACS areas applied for within the framework of agricultural land promotion (GIS application) from the Saarland.""" + provider = "Ministerium für Umwelt, Klima, Mobilität, Agrar und Verbraucherschutz " + attribution = "©GDI-SL 2024" + license = "cc-by-4.0" + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + columns = { + "geometry": "geometry", + "identifier": "id", + "flik": "flik", + "area": "metrics:area", + "name": "name", + } + missing_schemas = {"properties": {"name": {"type": "string"}}} + + def migrate(self, gdf): + gdf["flik"] = gdf["description"].apply(parse_flik) + gdf["area"] = gdf["description"].apply(parse_size) + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/de_th.py b/build/lib/fiboa_cli/datasets/de_th.py new file mode 100644 index 00000000..215bda13 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/de_th.py @@ -0,0 +1,107 @@ +import re + +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = "https://www.geoproxy.geoportal-th.de/download-service/opendata/agrar/DGK_Thue.zip" + # https://www.geoproxy.geoportal-th.de/inspire-dl/ + # http://www.geoproxy.geoportal-th.de/inspire-dl/atom/DataSet/DataSet_06cd3e2f-ed4a-4507-b5e7-14973d4d6968.xml + + id = "de_th" + admin_subdivision_code = "TH" + short_name = "Germany, Thuringia" + title = "Field boundaries for Thuringia, Germany" + description = """ +For use in the application procedure of the Integrated Administration and Control System (IACS), digital data layers are required that represent the current situation of agricultural use with the required accuracy. The field block is a contiguous agricultural area of one or more farmers surrounded by permanent boundaries. The field block thus contains information on the geographical location of the outer boundaries of the agricultural area. Reference parcels are uniquely numbered throughout Germany (Feldblockident - FBI). They also have a field block size (maximum eligible area) and a land use category. + +The following field block types exist: + +- Utilized agricultural area (UAA) +- Landscape elements (LE) +- Special use areas (SF) +- Forest areas (FF) + +The field blocks are classified separately according to the main land uses of arable land (`AL`), grassland (`GL`), permanent crops (`DA`, `OB`, `WB`), including agroforestry systems with an approved utilization concept and according to the BNK for no "agricultural land" (`NW`, `EF` and `PK`) and others. + +Landscape elements (LE) are considered part of the eligible agricultural area under defined conditions. In Thuringia, these permanent conditional features are designated as a separate field block (FB) and are therefore part of the Thuringian area reference system (field block reference). They must have a clear reference to an UAA (agricultural land), i.e. they are located within an arable, permanent grassland or permanent crop area or border directly on it. + +To produce the DGK-Lw, (official) orthophotos from the Thuringian Land Registry and Surveying Administration (TLBG) and orthophotos from the TLLLR's own aerial surveys are interpreted. The origin of this image data is 50% of the state area each year, so that up-to-date image data is available for the entire Thuringian state area every year. + """ + + provider = "Thüringer Landesamt für Landwirtschaft und Ländlichen Raum " + attribution = "© GDI-Th" + license = "DL-DE-BY-2.0" + + extensions = {"https://fiboa.org/flik-extension/v0.2.0/schema.yaml"} + + columns = { + "geometry": "geometry", + "BEZUGSJAHR": "valid_year", + "FBI": "flik", + "FBI_KURZ": "id", + "FB_FLAECHE": "metrics:area", + "FBI_VJ": "flik_last_year", + "FB_FL_VJ": "area_last_year", + "TK10": "tk10", + "AFO": "afo", + # Don't add LF, all values are 'LF' after the filter below + # 'LF': 'lf', + "BNK": "bnk", + "KOND_LE": "kond_le", + "AENDERUNG": "change", + "GEO_UPDAT": "determination:datetime", + } + + delim = re.compile(r"\s*,\s*") + column_migrations = { + "AFO": lambda column: column.map({"J": True}).fillna(value=False).astype(bool), + "KOND_LE": lambda column: column.map({"J": True}).fillna(value=False).astype(bool), + "AENDERUNG": lambda column: column.map( + {"Geaendert": True, "Unveraendert": False, "Neu": None} + ), + "FBI_VJ": lambda column: column.str.split(Converter.delim, regex=True), + } + + def migrate(self, gdf): + col = "GEO_UPDAT" + gdf[col] = pd.to_datetime(gdf[col], format="%d.%m.%Y", utc=True) + return super().migrate(gdf) + + column_filters = {"LF": lambda col: col == "LF"} + + # Schemas for the fields that are not defined in fiboa + # Keys must be the values from the COLUMNS dict, not the keys + missing_schemas = { + "required": ["valid_year", "area_last_year", "tk10", "bnk"], + "properties": { + "valid_year": { + # could also be uint16 or string + "type": "int16" + }, + "flik_last_year": { + "type": "array", + "items": { + # as defined in the flik extension schema + "type": "string", + "minLength": 16, + "maxLength": 16, + "pattern": "^[A-Z]{2}[A-Z0-9]{2}[A-Z0-9]{2}[A-Z0-9]{10}$", + }, + }, + "area_last_year": { + # as define in the area schema + "type": "float", + "exclusiveMinimum": 0, + "maximum": 100000, + }, + "tk10": {"type": "string"}, + "afo": {"type": "boolean"}, + "bnk": {"type": "string"}, + "kond_le": {"type": "boolean"}, + "change": {"type": "boolean"}, + }, + } diff --git a/build/lib/fiboa_cli/datasets/digifarm.py b/build/lib/fiboa_cli/datasets/digifarm.py new file mode 100644 index 00000000..297364b1 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/digifarm.py @@ -0,0 +1,31 @@ +# Digifarm converter for fiboa +# First draft just takes in a file saved from the API. +# It'd be ideal if there was a CLI that took a Digifarm token and BBOX and would save the file locally and +# convert it. Not sure if we want to keep loading functionality into the converter, if we have a new +# CLI tool that could query field boundary API's - I'd see DigiFarm and Onesoil as options, where you could +# do like 'fiboa api-request digifarm --bbox 4,12,5,13 --token blah | fiboa convert digifarm -i -'. + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(FiboaBaseConverter): + sources = None + data_access = """ + Data must be obtained from the Digifarm API, see https://api-docs.digifarm.io/. + Use the `-i` CLI parameter to provide the data source. + Provide a URL to an API request (e.g. `https://api.digifarm.io/v1/delineated-fields?token=...&bbox=11.13,60.72,11.21,60.76`) + or download the API response to a local GeoJSON file (use `.json` as file extension). + """ + id = "digifarm" + short_name = "DigiFarm" + title = "Field boundaries created by DigiFarm Automatic Field Delineation Model" + description = """These field boundaries are created by DigiFarm using a state-of-the-art deep neural network model for Field Delineation + from super-resolved satellite imagery. The results are available through an API, covering over 200 million hectares across 30+ countries. + The data is provided through the DigiFarm API at https://api-docs.digifarm.io/, as GeoJSON. For more information see https://digifarm.io/products/field-boundaries + """ + provider = "DigiFarm " + attribution = "© 2024 digifarm.io" + license = "DigiFarm Terms and Conditions " + columns = {"id": "id", "geometry": "geometry", "area": "metrics:area"} + area_is_in_ha = False + column_additions = {"determination_method": "auto-imagery"} diff --git a/build/lib/fiboa_cli/datasets/dk.py b/build/lib/fiboa_cli/datasets/dk.py new file mode 100644 index 00000000..0cf4e7d6 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/dk.py @@ -0,0 +1,34 @@ +import geopandas as gpd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class DKConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + str(variant): f"https://landbrugsgeodata.fvm.dk/Download/Marker/Marker_{variant}.zip" + for variant in range(2026, 2008 - 1, -1) + } + id = "dk" + short_name = "Denmark" + title = "Denmark Crop Fields (Marker)" + description = "The Danish Ministry of Food, Agriculture and Fisheries publishes Crop Fields (Marker) for each year." + + provider = "Danish Agricultural Agency " + ec_mapping_csv = "dk_2019.csv" + license = "CC0-1.0" + columns = { + "geometry": "geometry", + "Marknr": "id", + "IMK_areal": "metrics:area", + "Afgkode": "crop:code", + "Afgroede": "crop:name", + } + use_variant_as_determination = True + + def migrate(self, gdf) -> gpd.GeoDataFrame: + if "Afgkode" in gdf.columns: + gdf["Afgkode"] = gdf["Afgkode"].astype(float).fillna(value=0).astype(int).astype(str) + # the 2008 and 2009 editions carry no crop columns (boundaries only) + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/ec_be_vlg.py b/build/lib/fiboa_cli/datasets/ec_be_vlg.py new file mode 100644 index 00000000..5f1c048c --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_be_vlg.py @@ -0,0 +1,15 @@ +from .be_vlg import Converter as BEVLGBaseConverter +from .commons.ec import EuroCropsConverterMixin + + +class ECConverter(EuroCropsConverterMixin, BEVLGBaseConverter): + id = "ec_be_vlg" + sources = { + "https://zenodo.org/records/10118572/files/BE_VLG_2021.zip?download=1": [ + "BE_VLG_2021/BE_VLG_2021_EC21.shp" + ] + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + del self.columns["BT_OMSCH"] diff --git a/build/lib/fiboa_cli/datasets/ec_ee.py b/build/lib/fiboa_cli/datasets/ec_ee.py new file mode 100644 index 00000000..682df4d8 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_ee.py @@ -0,0 +1,57 @@ +import pandas as pd + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import EuroCropsConverterMixin + +# todo: The dataset doesn't validate due to a self intersecting polygon +# How do we want to handle this? + + +class Convert(EuroCropsConverterMixin, FiboaBaseConverter): + ec_mapping_csv = "ee_2021.csv" + ec_year = 2021 + sources = "https://zenodo.org/records/14094196/files/EE_2021.zip?download=1" + id = "ec_ee" + short_name = "Estonia" + title = "Field boundaries for Estonia" + description = """ +Geospatial Aid Application Estonia Agricultural parcels. +The original dataset is provided by ARIB and obtained from the INSPIRE theme GSAA (specifically Geospaial Aid Application Estonia Agricultural parcels) through which the data layer Fields and Eco Areas (GSAA) is made available. +The data comes from ARIB's database of agricultural parcels. + """ + provider = "Põllumajanduse Registrite ja Informatsiooni Amet " + attribution = "© Põllumajanduse Registrite ja Informatsiooni Amet" + + columns = { + "geometry": "geometry", + "pollu_id": "id", + "taotlusaas": "determination:datetime", # year + "pindala_ha": "metrics:area", # area (in ha) + "taotletud_": "crop:code", # requested crop culture + "taotletu_1": "taotletud_maakasutus", # requested land use + "taotletu_2": "taotletud_toetus", # requested support + "niitmise_t": "niitmise_tuvastamise_staatus", # mowing detection status + "niitmise_1": "niitmise_tuvast_ajavahemik", # mowing detection period + "viimase_mu": "viimase_muutmise_aeg", # Last edit time (date-date) + "taotleja_n": "taotleja_nimi", # name of applicant + "taotleja_r": "taotleja_registrikood", # applicant's registration code + } + column_migrations = {"JAHR": lambda col: pd.to_datetime(col, format="%Y")} + missing_schemas = { + "required": [ + "taotletud_kultuur", + "taotletud_maakasutus", + "viimase_muutmise_aeg", + "taotleja_nimi", + ], + "properties": { + "taotletud_kultuur": {"type": "string"}, + "taotletud_maakasutus": {"type": "string"}, + "niitmise_tuvastamise_staatus": {"type": "string"}, + "niitmise_tuvast_ajavahemik": {"type": "string"}, + "viimase_muutmise_aeg": {"type": "string"}, + "taotletud_toetus": {"type": "string"}, + "taotleja_nimi": {"type": "string"}, + "taotleja_registrikood": {"type": "string"}, + }, + } diff --git a/build/lib/fiboa_cli/datasets/ec_lt.py b/build/lib/fiboa_cli/datasets/ec_lt.py new file mode 100644 index 00000000..dd8e3d35 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_lt.py @@ -0,0 +1,50 @@ +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import EuroCropsConverterMixin + + +class Converter(EuroCropsConverterMixin, FiboaBaseConverter): + area_is_in_ha = False + ec_mapping_csv = "lt_2021.csv" + ec_year = 2021 + sources = {"https://zenodo.org/records/6868143/files/LT_2021.zip": ["LT/LT_2021_EC.shp"]} + + id = "ec_lt" + short_name = "Lithuania" + title = "Field boundaries for Lithuania" + description = """ +Collection of data on agricultural land and crop areas, cultivated crops in the territory of the Republic of Lithuania. + +The download service is a set of personalized spatial data of agricultural land and crop areas, cultivated crops. The service provides object geometry with descriptive (attributive) data. + """ + provider = "Construction Sector Development Agency " + # license = "Non-commercial use only " + + columns = { + "NMA_ID": "id", + "GRUPE": "crop:name", + "Shape_Leng": "metrics:perimeter", + "Shape_Area": "metrics:area", + "geometry": "geometry", + } + add_columns = {"determination:datetime": "2021-10-08T00:00:00Z"} + column_filters = { + "GRUPE": lambda col: ( + col.isin( + [ + "Darþovës", + "Grikiai", + "Ankðtiniai javai", + "Aviþos", + "Þieminiai javai", + "Summer Cereals", + "Vasariniai javai", + "Cukriniai runkeliai", + "Uogynai", + "Kukurûzai", + ] + ), + False, + ) + } + + missing_schemas = {"required": [], "properties": {"crop_name": {"type": "string"}}} diff --git a/build/lib/fiboa_cli/datasets/ec_lv.py b/build/lib/fiboa_cli/datasets/ec_lv.py new file mode 100644 index 00000000..9e023e8c --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_lv.py @@ -0,0 +1,59 @@ +import pandas as pd + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import EuroCropsConverterMixin + + +class Converter(EuroCropsConverterMixin, FiboaBaseConverter): + ec_mapping_csv = "lv_2021.csv" + ec_year = 2021 + sources = {"https://zenodo.org/records/8229128/files/LV_2021.zip": ["LV_2021/LV_2021_EC21.shp"]} + id = "ec_lv" + short_name = "Latvia" + title = "Field boundaries for Latvia" + description = "This dataset contains the field boundaries for all of Latvia in 2021. The data was collected by the Latvian government." + + provider = "Lauku atbalsta dienests " + attribution = "Lauku atbalsta dienests" + + columns = { + "geometry": "geometry", + "OBJECTID": "id", + "AREA_DECLA": "metrics:area", + "DATA_CHANG": "determination:datetime", + "PERIOD_COD": "year", + "PARCEL_ID": "parcel_id", + "PRODUCT_CO": "crop:code", + "AID_FORMS": "subsidy_type", + "EC_NUTS3": "EC_NUTS3", # should this be HCAT? + # 'PRODUCT_DE': 'PRODUCT_DE', + } + + column_migrations = { + "DATA_CHANG": lambda column: pd.to_datetime(column, format="%Y/%m/%d %H:%M:%S.%f", utc=True) + } + + missing_schemas = { + "required": [ + "year", + "parcel_id", + "subsidy_type", + "EC_NUTS3", + # 'PRODUCT_DE', + ], + "properties": { + "year": {"type": "uint16", "minLength": 4, "maxLength": 4}, + "parcel_id": {"type": "uint64", "minLength": 8, "maxLength": 8}, + "subsidy_type": {"type": "string"}, + "EC_NUTS3": { + "type": "string", + "minLength": 5, + "maxLength": 5, + "pattern": "^[A-Z]{2}[0-9]{3}", + }, + }, + } + + def add_hcat(self, gdf): + # skip adding hcat + return gdf diff --git a/build/lib/fiboa_cli/datasets/ec_nl_crop.py b/build/lib/fiboa_cli/datasets/ec_nl_crop.py new file mode 100644 index 00000000..c81c57d1 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_nl_crop.py @@ -0,0 +1,9 @@ +from .commons.ec import EuroCropsConverterMixin +from .nl import NLCropConverter + + +class NLEuroCropConverter(EuroCropsConverterMixin, NLCropConverter): + ec_mapping_csv = "nl_2020.csv" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) diff --git a/build/lib/fiboa_cli/datasets/ec_ro.py b/build/lib/fiboa_cli/datasets/ec_ro.py new file mode 100644 index 00000000..7b24626c --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_ro.py @@ -0,0 +1,43 @@ +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import EuroCropsConverterMixin, ec_url + + +class Convert(EuroCropsConverterMixin, FiboaBaseConverter): + # See https://data.europa.eu/data/datasets/092425a1-90c6-4461-b1a6-6f5b0f72748f?locale=ro + ec_mapping_csv = "ro_no_year.csv" + sources = {"https://zenodo.org/records/14094196/files/RO_ny.zip?download=1": ["RO/*.shp"]} + id = "ec_ro" + short_name = "Romania" + title = "Field boundaries for Romania" + description = """ +The dataset includes the land cover layer from the Romanian side of the Romania-Bulgaria cross-border area (Mehedinți, Dolj, Olt, Teleorman, Giurgiu, Călărași, Constanța counties), developed within the project "Common strategy for territorial development of the cross-border area Romania-Bulgaria", code MIS-ETC 171, funded by the Romania-Bulgaria Cross-Border Cooperation Programme 2007-2013. + +The dataset is published in the WGS 84 / UTM zone 35N coordinate system (to be compatible with the similar dataset on the Bulgarian side). + +The dataset is in line with the conceptual framework described in the Land Cover Data Specifications for the Implementation of the INSPIRE Directive (version 3.0). The information layer was developed based on a methodology developed within the project, which was carried out as follows: - analysis and harmonisation of the land cover classification system; - acquisition and processing of the reference data, listed below; - verification and validation of the quality of the spatial data produced; + """ + provider = ( + "Ministry of Regional Development and Public Administration " + ) + license = "CC0-1.0" + column_additions = { + "determination:datetime": "2017-01-01T00:00:00Z", + "crop:code_list": ec_url("ro_no_year.csv"), + } + index_as_id = True + columns = { + "id": "id", + "geometry": "geometry", + "AREA_HA": "metrics:area", + "SOURCE": "source", + "LC_MAPCODE": "crop:code", + "LC_CLASS_N": "crop:name", + } + area_is_in_ha = False + missing_schemas = {"properties": {"source": {"type": "string"}}} + column_filters = { + # Fields + # A=Arable Land, CAG=Covered Agricultural Land, N+G=Grassland, P=Trees, R=Rice, T=Trees + "LC_MAPCODE": lambda col: col.isin(["A", "CAG", "G", "N", "P", "R", "T"]) + } + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} diff --git a/build/lib/fiboa_cli/datasets/ec_si.py b/build/lib/fiboa_cli/datasets/ec_si.py new file mode 100644 index 00000000..e0103ab2 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ec_si.py @@ -0,0 +1,47 @@ +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import EuroCropsConverterMixin + + +class Converter(EuroCropsConverterMixin, FiboaBaseConverter): + area_is_in_ha = False + ec_mapping_csv = "si_2021.csv" + ec_year = 2021 + sources = { + "https://zenodo.org/records/10118572/files/SI_2021.zip?download=1": ["SI_2021_EC21.shp"] + } + + id = "ec_si" + short_name = "Slovenia" + title = "Field boundaries for Slovenia" + description = "This dataset contains the field boundaries for all of Slovenia in 2021. The data was collected by the Slovenian government." + + provider = "Ministrstvo za kmetijstvo, gozdarstvo in prehrano " + attribution = "Ministrstvo za kmetijstvo, gozdarstvo in prehrano" + + columns = { + "geometry": "geometry", + "ID": "id", + "AREA": "metrics:area", + "GERK_PID": "gerk_pid", + "SIFRA_KMRS": "crop_type_class", + "RASTLINA": "rastlina", + "CROP_LAT_E": "crop_lat_e", + "COLOR": "color", + "EC_NUTS3": "EC_NUTS3", + } + + missing_schemas = { + "required": ["gerk_pid", "crop_type_class", "rastlina", "crop_lat_e", "color"], + "properties": { + "gerk_pid": {"type": "uint64"}, + "crop_type_class": {"type": "string"}, + "rastlina": {"type": "string"}, + "crop_lat_e": {"type": "string"}, + "color": {"type": "string"}, + "EC_NUTS3": {"type": "string"}, + }, + } + + def add_hcat(self, gdf): + # skip adding hcat + return gdf diff --git a/build/lib/fiboa_cli/datasets/ee.py b/build/lib/fiboa_cli/datasets/ee.py new file mode 100644 index 00000000..1ec6d66b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ee.py @@ -0,0 +1,40 @@ +import pandas as pd + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + +COLUMNS = { + "geometry": "geometry", + "pollu_id": "id", + "taotlusaasta": "determination:datetime", # year + "pindala_ha": "metrics:area", # area (in ha) + "taotletud_kultuur": "crop:name", # requested crop culture +} +ATTRIBUTES = ",".join(["geom" if k == "geometry" else k for k in COLUMNS.keys()]) + + +class Convert(AddHCATMixin, FiboaBaseConverter): + # explicit cache names: the WFS URL has no usable file name + variants = { + str(year): { + f"https://kls.pria.ee/geoserver/inspire_gsaa/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=inspire_gsaa:LU.GSAA.AGRICULTURAL_PARCELS_{year}&propertyName={ATTRIBUTES}": f"ee_gsaa_{year}.gml" + } + for year in range(2024, 2009, -1) + } + ec_mapping_csv = "https://fiboa.org/code/ee/ee.csv" + id = "ee" + short_name = "Estonia" + title = "Field boundaries for Estonia" + description = """ +Geospatial Aid Application Estonia Agricultural parcels. +The original dataset is provided by ARIB and obtained from the INSPIRE theme GSAA (specifically Geospaial Aid Application Estonia Agricultural parcels) through which the data layer Fields and Eco Areas (GSAA) is made available. +The data comes from ARIB's database of agricultural parcels. + """ + provider = "Põllumajanduse Registrite ja Informatsiooni Amet " + attribution = "© Põllumajanduse Registrite ja Informatsiooni Amet" + license = "CC-BY-SA-3.0" + columns = COLUMNS + column_migrations = {"taotlusaasta": lambda col: pd.to_datetime(col, format="%Y")} + + def file_migration(self, gdf, path: str, uri: str, layer=None): + return gdf.set_crs(3301) diff --git a/build/lib/fiboa_cli/datasets/es.py b/build/lib/fiboa_cli/datasets/es.py new file mode 100644 index 00000000..4870d93d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es.py @@ -0,0 +1,114 @@ +import re + +import requests +from vecorel_cli.conversion.admin import AdminConverterMixin +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from fiboa_cli.datasets.commons.hcat import AddHCATMixin + +from ..conversion.per_file import PerFileBaseConverter + + +class Converter(AdminConverterMixin, AddHCATMixin, PerFileBaseConverter): + id = "es" + short_name = "Spain" + title = "Spain Declared Crops (Cultivos Declarados SIGPAC)" + description = """ +National declared-crop dataset (Cultivos Declarados SIGPAC) published by the Spanish Agricultural Guarantee Fund +(FEGA) via the unified SIGPAC Hub Cloud portal (sigpac-hubcloud.es). Each record is a declaration line within a +farmer's Single Application (Solicitud Única) for Common Agricultural Policy (CAP) direct payments, mapped onto +SIGPAC cadastral divisions. Data is distributed as one GeoPackage per Spanish province, harmonised across the +country since the 2025 campaign year. + +This is a high-value dataset (HVD) under EU Implementing Regulation 2023/138. + """ + provider = "Fondo Español de Garantía Agraria (FEGA) " + attribution = "©FEGA / Ministerio de Agricultura, Pesca y Alimentación" + license = "CC-BY-4.0" + + variants = {"2025": "2025"} + + # FEGA declared-crop codelist (PARC_PRODUCTO) — separate from the SIGPAC land-use list. + # Reference list shipped inside each provincial GPKG as the `cod_producto` layer. + ec_mapping_csv = "https://fiboa.org/code/es/es.csv" + + columns = { + "geometry": "geometry", + "id": "id", + "provincia": "admin:subdivision_code", + "dn_surface": "metrics:area", + "parc_producto": "crop:code", + "parc_sistexp": "irrigation_system", + } + + area_is_in_ha = False + + extensions = { + "https://fiboa.org/crop-extension/v0.2.0/schema.yaml", + ADMIN_DIVISION, + } + + column_migrations = { + "parc_producto": lambda col: col.astype("Int64").fillna(0).astype(str), + "provincia": lambda col: col.astype("Int64").astype(str).str.zfill(2), + } + + missing_schemas = { + "properties": { + "admin_municipality_code": {"type": "string"}, + "irrigation_system": {"type": "string"}, + } + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.variant: + self.variant = next(iter(self.variants)) + self.column_additions = { + **self.column_additions, + "determination:datetime": f"{self.variant}-01-01T00:00:00Z", + } + + def layer_filter(self, layer: str, uri: str) -> bool: + # GPKG contains the data layer plus several codelist tables (cod_*) — only read the data. + return layer == "cultivo_declarado" + + def migrate(self, gdf): + # The source has no globally unique row identifier. Build one from the SIGPAC cadastral key + # plus the declaration-line index, which is unique per record. + def part(col): + return gdf[col].astype("Int64").astype(str) + + gdf["id"] = ( + part("provincia").str.zfill(2) + + "-" + + part("municipio") + + "-" + + part("agregado") + + "-" + + part("zona") + + "-" + + part("poligono") + + "-" + + part("parcela") + + "-" + + part("recinto") + + "-" + + part("ld_recinto") + ) + return super().migrate(gdf) + + def get_urls(self): + if self.variant not in self.variants: + opts = ", ".join(self.variants.keys()) + raise ValueError(f"Unknown variant '{self.variant}', choose from {opts}") + + year = self.variant + base = f"https://sigpac-hubcloud.es/geopackages/{year}/cultivo_declarado/" + response = requests.get(base, timeout=60) + response.raise_for_status() + # The directory listing is a classic Apache-style HTML index; parse out the .zip hrefs. + zip_paths = re.findall(r'HREF="(/geopackages/[^"]+\.zip)"', response.text) + if not zip_paths: + raise RuntimeError(f"No GeoPackage archives found at {base}") + return {f"https://sigpac-hubcloud.es{p}": ["*.gpkg"] for p in zip_paths} diff --git a/build/lib/fiboa_cli/datasets/es_an.py b/build/lib/fiboa_cli/datasets/es_an.py new file mode 100644 index 00000000..6fcf1fa9 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_an.py @@ -0,0 +1,77 @@ +from loguru import logger + +from .commons.data import read_data_csv +from .es_base import ESBaseConverter + + +class ANConverter(ESBaseConverter): + variants = { + "2025": "https://www.juntadeandalucia.es/ssdigitales/festa/agriculturapescaaguaydesarrollorural/2025/SP25_REC_PROV_{code}.zip", + "2024": "https://www.juntadeandalucia.es/ssdigitales/festa/agriculturapescaaguaydesarrollorural/2024/SP24_REC_{code}.zip", + "2023": "https://www.juntadeandalucia.es/ssdigitales/festa/agriculturapescaaguaydesarrollorural/2023/SP23_REC_{code}.zip", + "2022": "https://www.juntadeandalucia.es/export/drupaljda/01_SP22_REC_PROV_{code}.zip", + "2021": "https://www.juntadeandalucia.es/export/drupaljda/V1_01_SP21_REC_PROV_{code}.zip", + "2020": "https://www.juntadeandalucia.es/export/drupaljda/SP20_REC_PROV_{filename}.zip", + "2019": "https://www.juntadeandalucia.es/export/drupaljda/SIGPAC2019_REC_PROV_{filename}.zip", + "2018": "https://www.juntadeandalucia.es/export/drupaljda/SIGPAC2018_REC_PROV_{filename}.zip", + "2017": "https://www.juntadeandalucia.es/export/drupaljda/sp17_rec_{code}.zip", + } + + id = "es_an" + short_name = "Spain Andalusia" + title = "Spain Andalusia Crop fields" + description = """ +SIGPAC is the Geographic Information System for the Identification of Agricultural Plots , +created through collaboration between the Spanish Agricultural Guarantee Fund (FEGA) and +the different Autonomous Communities, within the scope of their territories, as an element +of the Integrated Management and Control System of the direct aid regimes. It has the character +of a public register of administrative profile, and contains updated information on the +plots that may benefit from community aid related to the surface area, providing graphic +support for these and their subdivisions (ENCLOSURES) with defined agricultural uses or +developments. + """ + provider = "Junta de Andalucía " + attribution = "©Junta de Andalucía" + # The end user is required to be informed, ..., that the cartography and geographic information is available free of charge on the website of the Ministry of Agriculture, Fisheries and Rural Development. + license = "Pursuant to Law 37/2007 of 16 November on the reuse of public sector information and Law 3/2013 of 24 July approving the Statistical and Cartographic Plan of Andalusia 2013-2017, the geographic information of SIGPAC is made available to the public. " + columns = { + "geometry": "geometry", + "ID_RECINTO": "id", + "CD_PROV": "admin_province_code", + "CD_MUN": "admin_municipality_code", + "NU_AREA": "metrics:area", + "CD_USO": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + + use_code_attribute = "CD_USO" + area_is_in_ha = False + area_calculate_missing = True + use_variant_as_determination = True + + column_migrations = { + "ID_RECINTO": lambda col: col.astype("int64"), + } + + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + + def get_urls(self): + if not self.variant: + self.variant = next(iter(self.variants)) + logger.warning(f"Choosing first year {self.variant}") + else: + assert self.variant in self.variants, f"Wrong year {self.variant}" + + url = self.variants[self.variant] + data = read_data_csv("es_an_prv.csv") + + def fname(line): + return f"SP{int(self.variant) % 100}_REC_{line['code']}.shp" + + return {url.format(**line): [fname(line)] for line in data} diff --git a/build/lib/fiboa_cli/datasets/es_ar.py b/build/lib/fiboa_cli/datasets/es_ar.py new file mode 100644 index 00000000..96262e8c --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_ar.py @@ -0,0 +1,106 @@ +import json + +import requests + +from .es_base import ESBaseConverter + +# IDEAragon lists every product of a collection that intersects a province +# (https://idearagon.aragon.es/descargas, collection "SIGPAC"). The per-province +# files (rec22/rec44/rec50_sigpac.shp.zip, ~1 GB RAR archives) are unreliable: +# the Teruel file disappeared from the server in 2026, so the much smaller +# per-municipality shapefiles are used instead. +PRODUCTS_URL = "https://idearagon.aragon.es/BD_GIS/getProductosColeccionIntersect.jsp" +DOWNLOAD_URL = ( + "https://icearagon.aragon.es/datosdescarga/descarga.php" + "?file=/CartoTema/sigpac/{name}.shp.zip&blocksize=0" +) +PROVINCES = ("22", "44", "50") # Huesca, Teruel, Zaragoza + + +class ARConverter(ESBaseConverter): + # https://idearagon.aragon.es/descargas -> SIGPAC + # The download list is fetched at runtime (see get_urls); the files are + # overwritten in place every campaign, the product list carries the year. + id = "es_ar" + short_name = "Spain Aragon" + title = "Spain Aragon Crop fields" + description = """ +SIGPAC - Sistema de Información Geográfica de la Política Agrícola común (ejercicio actual) + +Crop Fields of Spain province Aragon + """ + provider = "Gobierno de Aragon " + + # License: https://idearagon.aragon.es/portal/politica-privacidad.jsp + license = "CC-BY-4.0" + attribution = "(c) Gobierno de Aragon" + columns = { + "geometry": "geometry", + "DN_OID": "id", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "SUPERFICIE": "metrics:area", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + "determination:datetime": "determination:datetime", + } + area_is_in_ha = False + use_code_attribute = "USO_SIGPAC" + + column_migrations = { + "DN_OID": lambda col: col.astype("int64"), + } + + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + + # Campaign year of the downloaded files, taken from the product list. + # Falls back to the --variant when the files are given explicitly. + edition_year = None + + @staticmethod + def list_products(province): + response = requests.post( + PRODUCTS_URL, + data={ + "idesquema": f"{province}provincia", + "coleccion": "SIGPAC", + "esquema": "provincia", + }, + timeout=120, + ) + response.raise_for_status() + # the service emits a trailing comma before the closing bracket + text = response.text.replace("\n", "").replace("},]}", "}]}").strip() + return json.loads(text)["productos"] + + def get_urls(self): + urls = {} + years = set() + for province in PROVINCES: + for product in self.list_products(province): + name = product["name"] + # the intersection also returns neighbouring municipalities + if product["esquema"] != "Municipio" or not name.startswith(province): + continue + urls[DOWNLOAD_URL.format(name=name)] = f"es_ar_{name}.shp.zip" + years.add(str(product["fecha"])[:4]) + if not urls: + raise ValueError("No SIGPAC municipality files listed by IDEAragon") + self.edition_year = max(years) + self.info(f"{len(urls)} municipality files, campaign {self.edition_year}") + return urls + + def post_migrate(self, gdf): + gdf = super().post_migrate(gdf) + year = self.edition_year or self.variant + if year: + gdf["determination:datetime"] = f"{year}-01-01T00:00:00Z" + else: + self.warning("Unknown campaign year, determination:datetime is not set") + return gdf diff --git a/build/lib/fiboa_cli/datasets/es_base.py b/build/lib/fiboa_cli/datasets/es_base.py new file mode 100644 index 00000000..4d2fd9d4 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_base.py @@ -0,0 +1,50 @@ +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter +from fiboa_cli.datasets.commons.data import read_data_csv + + +class ESBaseConverter(FiboaBaseConverter): + """ + Base Converter for Spain + Asssumes a source column with the SIGPAC-Land Use code + The Land Use code is filtered for agricultural use and transformed into a high-level crop type + + "Cultivo Declarado" is what we would prefer, but the "Recinto" is the best to be found so far + + For Spanish Sources, see https://www.cartodruid.es/en/-/descargar-sigpac-comunidad-autonoma + There seems to be a National Layer; https://inspire-geoportal.ec.europa.eu/srv/api/records/87ce5171-d713-4eec-a1f3-2b9dd94cad91 + """ + + use_code_attribute = "uso_sigpac" + + extensions = { + "https://fiboa.org/crop-extension/v0.2.0/schema.yaml", + ADMIN_DIVISION, + } + column_additions = { + # https://www.euskadi.eus/contenidos/informacion/pac2015_pagosdirectos/es_def/adjuntos/Anexos_PAC_marzo2015.pdf + # https://www.fega.gob.es/sites/default/files/files/document/AD-CIRCULAR_2-2021_EE98293_SIGC2021.PDF + # Very generic list + "admin:country_code": "ES", + "crop:code_list": "https://fiboa.org/code/es/sigpac/land_use.csv", + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.id.startswith("es_"), "Assuming Spanish subclass" + + def code_filter(col): + return ~col.isin("AG/CA/ED/FO/IM/IS/IV/TH/ZC/ZU/ZV/MT".split("/") + [None]) + + self.column_filters = {self.use_code_attribute: code_filter} + self.column_additions["admin:subdivision_code"] = self.id[len("es_") :].upper() + + def migrate(self, gdf): + # This actually is a land use code. Not sure if we should put this in crop:code + rows = read_data_csv("es_coda_uso.csv") + mapping = {row["original_code"]: row["original_name"] for row in rows} + mapping_en = {row["original_code"]: row["name_en"] for row in rows} + gdf["crop:name"] = gdf[self.use_code_attribute].map(mapping) + gdf["crop:name_en"] = gdf[self.use_code_attribute].map(mapping_en) + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/es_cat.py b/build/lib/fiboa_cli/datasets/es_cat.py new file mode 100644 index 00000000..11fa36c5 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_cat.py @@ -0,0 +1,80 @@ +import pandas as pd + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.data import read_data_csv + + +class ESCatConverter(FiboaBaseConverter): + # Catalonia has its own coding list, not sublass of ESBaseConverter + variants = { + "2024": { + "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/d90f5fca-ddd8-405d-a0d5-90609985e98e?download=true&filename=Cultius_DUN2024_SHP.zip": [ + "Cultius_DUN2024_SHP/Cultius_DUN2024_SHP.shp" + ] + }, + "2023": { + "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/b4299961-52ee-4fa0-a276-4594c8c094bc?download=true&filename=Cultius_DUN2023_GPKG.zip": [ + "Cultius_DUN2023_GPKG/CULTIUS_DUN2023.gpkg" + ] + }, + "2022": { + "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/f1c8c463-ef4a-4821-8516-ff1884c0386a?download=true&filename=Cultius_DUN2022_SHP.zip": "Cultius_DUN2022.zip" + }, + "2021": { + "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/aef79c3c-c663-46ed-a535-ceb03a64b46b?download=true&filename=Cultius_DUN2021_SHP.zip": "Cultius_DUN2021.zip" + }, + "2020": { + "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/b47b0ab9-8324-40c7-b553-1015793a38a4?download=true&filename=Cultius_DUN2020_SHP.zip": "Cultius_DUN2020.zip" + }, + "2019": { + "https://analisi.transparenciacatalunya.cat/api/views/yh94-j2n9/files/58d46b1e-522f-428e-b089-aa8e4668fae9?download=true&filename=Cultius_DUN2019.zip": "Cultius_DUN2019.zip" + }, + # More data at https://agricultura.gencat.cat/ca/ambits/desenvolupament-rural/sigpac/mapa-cultius/ + } + id = "es_cat" + short_name = "Catalonia" + title = "Catalonia Crop Fields (Mapa de cultius)" + description = """ +The Department of Agriculture, Livestock, Fisheries and Food makes available to the public the data from the crop map of Catalonia. +This map allows you to locate the crops declared in the Agrarian Declaration - DUN submitted to the DACC. + """ + provider = "Catalonia Department of Agriculture, Livestock, Fisheries and Food " + attribution = "Catalonia Department of Agriculture, Livestock, Fisheries and Food" + license = "The Open Information Use License - Catalonia " + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + column_additions = { + "crop:code_list": "https://fiboa.org/code/es/cat/crop.csv", + } + columns = { + "geometry": "geometry", + "id": "id", + "campanya": "determination:datetime", + "ha": "metrics:area", + "cultiu": "crop:name", + "crop:code": "crop:code", + "crop:name_en": "crop:name_en", + } + open_options = dict(encoding="utf-8") + column_migrations = { + "campanya": lambda col: pd.to_datetime(col, format="%Y"), + } + + index_as_id = True + + def layer_filter(self, layer, uri): + return "cultius" in layer.lower() + + def migrate(self, gdf): + # In 2023 gpkg, names are lowercase. But in 2022 shapefile, case is mixed + to_lower = {k: k.lower() for k in gdf.columns if k != k.lower()} + if to_lower: + gdf.rename(columns=to_lower, inplace=True) + + rows = read_data_csv("es_cat.csv") + mapping = {row["original_name"]: row["original_code"] for row in rows} + mapping_en = {row["original_name"]: row["translated_name"] for row in rows} + missing = {k for k in gdf["cultiu"].unique() if k not in mapping} + assert len(missing) == 0, f"Can not map crops {missing}" + gdf["crop:code"] = gdf["cultiu"].map(mapping) + gdf["crop:name_en"] = gdf["cultiu"].map(mapping_en) + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/es_cb.py b/build/lib/fiboa_cli/datasets/es_cb.py new file mode 100644 index 00000000..f6e0a52f --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_cb.py @@ -0,0 +1,52 @@ +import re + +from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin +from fiboa_cli.datasets.es_base import ESBaseConverter + + +class ESCBConverter(EsriRESTConverterMixin, ESBaseConverter): + id = "es_cb" + short_name = "Spain Cantabria" + title = "Spain Cantabria Crop fields" + description = "SIGPAC Crop fields of Spain - Cantabria" + # https://www.caib.es/sites/M170613081930629/f/463418 + # see https://intranet.caib.es/opendatacataleg/dataset/sigpac-2024/resource/3a0bc2e0-3f37-45b7-a7d4-1e8c7cf09bc8 + # "Our licenses allow the reproduction or redistribution of the licensed digital information to third parties. In such cases, it is essential that when redistributing or transferring the data to said third parties, they clearly and explicitly accept the conditions of our non-commercial use license." + license = "CC-BY-NC-4.0" # http://www.opendefinition.org/licenses/cc-by + attribution = ( + "©Government of Cantabria. Free information available at https://mapas.cantabria.es" + ) + provider = "" + columns = { + "DN_OID": "id", + "geometry": "geometry", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "DN_SURFACE": "metrics:area", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + area_is_in_ha = False + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + + variants = {str(year): str(year) for year in range(2024, 2010 - 1, -1)} + use_code_attribute = "USO_SIGPAC" + use_variant_as_determination = True + + # "https://geoservicios.cantabria.es/inspire/rest/services/SIGPAC/MapServer?f=json" + # "https://geoservicios.cantabria.es/inspire/rest/services/SIGPAC/MapServer/63/query?f=json&where=1%3D1&spatialRel=esriSpatialRelIntersects&geometry=%7B%22xmin%22%3A407913.2828037373%2C%22ymin%22%3A4804384.359524686%2C%22xmax%22%3A411054.4224193499%2C%22ymax%22%3A4805366.49482229%2C%22spatialReference%22%3A%7B%22wkid%22%3A25830%2C%22latestWkid%22%3A25830%7D%7D&geometryType=esriGeometryEnvelope&inSR=25830&outFields=OBJECTID%2CPROVINCIA%2CMUNICIPIO%2CAGREGADO%2CZONA%2CPOLIGONO%2CPARCELA%2CRECINTO%2CUSO_SIGPAC%2CSHAPE_Area&orderByFields=OBJECTID%20ASC&outSR=25830" + + rest_base_url = "https://geoservicios.cantabria.es/inspire/rest/services/SIGPAC/MapServer" + # rest_params = {"where": "USO_SIGPAC NOT IN ('AG','CA','ED','FO','IM','IS','IV','TH','ZC','ZU','ZV','MT')"} + + def rest_layer_filter(self, layers): + if not self.variant: + self.variant = next(iter(self.variants)) + regex = re.compile("Recintos SIGPAC " + self.variant) + return next(layer for layer in layers if regex.match(layer["name"])) diff --git a/build/lib/fiboa_cli/datasets/es_cl.py b/build/lib/fiboa_cli/datasets/es_cl.py new file mode 100644 index 00000000..c0624076 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_cl.py @@ -0,0 +1,67 @@ +import os +import re + +import requests +from loguru import logger + +from .es_base import ESBaseConverter + +regex = re.compile(r"\d+_(RECFE|BURGOS).*\.shp$") + + +class ESCLConverter(ESBaseConverter): + id = "es_cl" + short_name = "Spain Castilla y León" + title = "Spain Castile and León Crop fields" + description = """ +Official SIGPAC land plan for the year 2024. (reference date 02-01-2024) + +Source: SIGPAC (FEGA) database. The Land Consolidation Replacement Farms are included, +not updated in the SIGPAC published in the Viewer. +Data manager: Ministry of Agriculture, Fisheries and Food. +Data provided by: Department of Agriculture, Livestock and Rural Development. Regional Government of Castile and Leon. +Free use of the data is permitted, but commercial exploitation is prohibited. + """ + provider = "Junta de Castilla y León " + license = "CC-NC: Free use of the data is permitted, but commercial exploitation is prohibited " + + columns = { + "DN_OID": "id", + "geometry": "geometry", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + use_code_attribute = "USO_SIGPAC" + use_variant_as_determination = True + + def download_files(self, uris, cache_folder=None): + paths = super().download_files(uris, cache_folder) + new = [] + for path, uri in paths: + directory = os.path.dirname(path) + # the 2025 archives nest the shapefiles in a province folder + ps = [ + os.path.join(root, z) + for root, _, files in os.walk(directory) + for z in files + if regex.search(z) + ] + assert len(ps), f"Missing matching shapefile in {directory}" + for p in ps: + new.append((p, uri)) + return new + + def get_urls(self): + if not self.variant: + self.variant = "2025" + logger.warning(f"Choosing first year {self.variant}") + else: + assert 2019 <= int(self.variant) <= 2025, f"Wrong year {self.variant}" + base = f"https://ftp.itacyl.es/cartografia/05_SIGPAC/{self.variant}_ETRS89/Parcelario_SIGPAC_CyL_Provincias/" + response = requests.get(base) + assert response.status_code == 200, f"Error getting urls {response}\n{response.content}" + uris = { + f"{base}{g}": ["replaceme.zip"] for g in re.findall(r'href="(\w+.zip)"', response.text) + } + return uris diff --git a/build/lib/fiboa_cli/datasets/es_cm.py b/build/lib/fiboa_cli/datasets/es_cm.py new file mode 100644 index 00000000..f04cfee4 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_cm.py @@ -0,0 +1,56 @@ +import re + +import requests + +from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin +from fiboa_cli.datasets.es_base import ESBaseConverter + + +class ESCMConverter(EsriRESTConverterMixin, ESBaseConverter): + id = "es_cm" + short_name = "Spain " + title = "Spain Castilla-La Mancha Crop fields" + description = """ +SIGPAC is a Geographic Information System dedicated to the control of agricultural aid under +the CAP (Common Agricultural Policy). This tool is mandatory for the management of community aid, and is +the identification basis for any type of aid related to the surface area. + """ + license = "CC-BY-SA-4.0" # see https://datosabiertos.castillalamancha.es/dataset/sistema-de-informaci%C3%B3n-geogr%C3%A1fica-de-parcelas-agr%C3%ADcolas-de-castilla-la-mancha-sigpac # https://mapas.xunta.gal/gl/aviso-legal + attribution = "Unidad de Cartografía. Secretaría General. Consejería de Agricultura, Ganadería y Desarrollo Rural." + provider = "Unidad de Cartografía. Secretaría General. Consejería de Agricultura, Ganadería y Desarrollo Rural. " + columns = { + "DN_OID": "id", + "geometry": "geometry", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "DN_SURFACE": "metrics:area", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + use_code_attribute = "USO_SIGPAC" + area_is_in_ha = False + use_variant_as_determination = True + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + variants = {str(year): str(year) for year in range(2024, 2018 - 1, -1)} + + rest_base_url = "https://geoservicios.castillalamancha.es/arcgis/rest/services/Vector" + rest_attribute = "OBJECTID_1" + + def get_urls(self): + if not self.variant: + self.variant = next(iter(self.variants)) + # Always use the year-named service: the unnamed "Recintos_sigpac" service is + # whatever year is current (2025 in August 2026) and keys on OBJECTID instead. + services = requests.get(self.rest_base_url, {"f": "pjson"}).json()["services"] + layer = next( + s["name"] + for s in services + if re.search(f"Recintos_sigpac_{self.variant}$", s["name"], re.IGNORECASE) + ) + return {"REST": self.rest_base_url.replace("Vector", layer + "/MapServer")} diff --git a/build/lib/fiboa_cli/datasets/es_cn.py b/build/lib/fiboa_cli/datasets/es_cn.py new file mode 100644 index 00000000..84561c1a --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_cn.py @@ -0,0 +1,63 @@ +import pandas as pd +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter + + +class ESCNConverter(FiboaBaseConverter): + id = "es_cn" + short_name = "Spain Canary Islands" + title = "Spain Crop fields of Canary Islands" + description = """ +The Canary Islands Crop Map is a cartographic dataset developed by the Department of Agriculture, Livestock, +Fisheries and Water of the Government of the Canary Islands, to understand the reality of the available +agricultural surface of the Canary Islands. This tool has been developed from 1998 to the present. + +There are several crop maps for each of the islands, which allow us to see the temporal and spatial evolution +of the cultivated areas of the islands in recent years. All this means that the Canary Islands Crop Map is a +basic tool for decision-making in present and future regional agricultural policy, as well as being a basic +source for the preservation of agricultural land in the field of territorial planning. + +The data of the Canary Islands Crop Map have been published on the open data portal of +the Government of the Canary Islands (https://datos.canarias.es/catalogos/general/dataset/mapa-de-cultivos-de-canarias) +and in datos gob (https://datos.gob.es/es/catalogo/a05003638-mapa-de-cultivos-de-canarias1), +this work having been addressed within the Strategic Plan for Innovation and Continuous Improvement +of the Ministry of Agriculture, Livestock and Fisheries. + """ + provider = "Gobierno de Canarias - Consejería de Agricultura, Ganadería, Pesca y Soberanía Alimentaria " + license = "CC-BY-4.0" # as stated in https://datos.canarias.es/portal/aviso-legal-y-condiciones-de-uso + attribution = "Gobierno de Canarias" + extensions = { + "https://fiboa.org/crop-extension/v0.2.0/schema.yaml", + ADMIN_DIVISION, + } + columns = { + "id": "id", + "geometry": "geometry", + "FECHA": "determination:datetime", + "ISLA_NA": "admin_island", + "CULTIVO_CO": "crop:code", + "CULTIVO_NA": "crop:name", + "AREA_M2": "metrics:area", + } + area_is_in_ha = False + column_migrations = { + "FECHA": lambda column: pd.to_datetime(column, format="%d/%m/%Y"), + } + column_additions = { + "admin:country_code": "ES", + "admin:subdivision_code": "CB", + "crop:code_list": "https://fiboa.org/code/es/cn/crop.csv", + } + missing_schemas = { + "properties": { + "admin_island": {"type": "string"}, + } + } + index_as_id = True + sources = { + f"https://opendata.sitcan.es/upload/medio-rural/gobcan_mapa-cultivos_{island}_shp.zip": f"gobcan_mapa-cultivos_{island}_shp.zip" + for island in "lz eh lp lg tf gc fv".split() + } + # Create code list with: + # duckdb -c 'SELECT distinct "crop:code", "crop:name" FROM "es_cn.parquet" order by "crop:code"' diff --git a/build/lib/fiboa_cli/datasets/es_ex.py b/build/lib/fiboa_cli/datasets/es_ex.py new file mode 100644 index 00000000..867c08e3 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_ex.py @@ -0,0 +1,70 @@ +import re +from datetime import datetime + +import requests + +from fiboa_cli.datasets.es_base import ESBaseConverter + + +class EXConverter(ESBaseConverter): + id = "es_ex" + short_name = "Spain Extremadura" + title = "Spain Extremadura Crop fields" + description = """SIGPAC Crop fields of Spain - Extremadura""" + license = "CC-BY-4.0" # See http://sitex.gobex.es/SITEX/files/CondicionesUsoCICTEX.pdf + attribution = "Junta de Extremadura" + provider = "Junta de Extremadura " + columns = { + "geometry": "geometry", + "id": "id", + "provincia": "admin_province_code", + "municipio": "admin_municipality_code", + "uso_sigpac": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + "dn_surface": "metrics:area", + "determination:datetime": "determination:datetime", + } + + area_is_in_ha = False + + def migrate(self, gdf): + gdf = super().migrate(gdf) + gdf["determination:datetime"] = datetime(year=int(self.variant), month=1, day=1) + return gdf + + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + + def get_urls(self): + if not self.variant: + self.variant = next(iter(self.variants)) + + from bs4 import BeautifulSoup + + base = "http://sitex.gobex.es/SITEX/centrodescargas/" + soup = BeautifulSoup(requests.get(f"{base}viewsubcategoria/45").content, "html.parser") + result = {} + + headers = {"X-Requested-With": "XMLHttpRequest", "X-Update": "resultadosdebusqueda"} + values = [ + e.get("value") + for e in soup.find("select", id="municipio").find_all("option") + if e.get("value") + ] + for value in values: + form = { + "_method": "POST", + "data[Datos][subcategoria_id]": 45, + "data[Datos][nucleospoblacion_id]": value, + } + response = requests.post(f"{base}listadoresultados", data=form, headers=headers) + soup = BeautifulSoup(response.content, "html.parser") + matches = soup.find_all("a", href=re.compile(r"/SITEX/centrodescargas/descargar/")) + m = matches[1 if self.variant == "2023" else 0].get("href") + result[f"http://sitex.gobex.es/{m}"] = ["*.shp"] # The single shapefile + return result diff --git a/build/lib/fiboa_cli/datasets/es_ga.py b/build/lib/fiboa_cli/datasets/es_ga.py new file mode 100644 index 00000000..0e2889a6 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_ga.py @@ -0,0 +1,47 @@ +from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin +from fiboa_cli.datasets.es_base import ESBaseConverter + + +class ESGAConverter(EsriRESTConverterMixin, ESBaseConverter): + id = "es_ga" + short_name = "Spain " + title = "Spain Galicia Crop fields" + description = """ +**Galician Crop Fields**: The Geographic Information System for Agricultural Plots (SIXPAC) is an official reference database for the identification of agricultural plots, which is mandatory in Spain for making applications for direct CAP aid that require declaring surface areas. +SIXPAC information is relevant to farmers applying for these aid schemes, so that they can indicate the location of the farm surfaces that may be eligible for subsidies, as well as to facilitate the submission of requests for changes to data relating to land uses contained in the system. + """ + license = "CC-BY-4.0" # https://mapas.xunta.gal/gl/aviso-legal + attribution = "Información procedente do FOGGA" + provider = "Virtual Office for Rural Environment " + columns = { + "DN_OID": "id", + "geometry": "geometry", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "DN_SURFACE": "metrics:area", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + area_is_in_ha = False + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + + variants = {str(year): str(year) for year in range(2024, 2010 - 1, -1)} + use_code_attribute = "USO_SIGPAC" + + rest_base_url = ( + "https://ideg.xunta.gal/servizos/rest/services/ParcelasCatastrais/SIXPAC_{year}/MapServer" + ) + + def rest_layer_filter(self, layers): + return next(layer for layer in layers if "recintos" in layer["name"].lower()) + + def get_urls(self): + if not self.variant: + self.variant = next(iter(self.variants)) + return {"REST": self.rest_base_url.format(year=self.variant)} diff --git a/build/lib/fiboa_cli/datasets/es_ib.py b/build/lib/fiboa_cli/datasets/es_ib.py new file mode 100644 index 00000000..ef09dffa --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_ib.py @@ -0,0 +1,65 @@ +import pandas as pd + +from fiboa_cli.conversion.converter_rest import EsriRESTConverterMixin +from fiboa_cli.datasets.es_base import ESBaseConverter + +CATALAN_MONTHS = ( + "gener febrer març abril maig juny juliol agost setembre octubre novembre desembre".split() +) + + +def snapshot_date(catxe): + """'maig 2026' -> 2026-05-01""" + try: + month, year = str(catxe).strip().lower().split() + return pd.Timestamp(year=int(year), month=CATALAN_MONTHS.index(month) + 1, day=1, tz="UTC") + except (ValueError, AttributeError): + return pd.NaT + + +class ESIBConverter(EsriRESTConverterMixin, ESBaseConverter): + id = "es_ib" + short_name = "Spain Balearic Islands" + title = "Spain Balearic Islands Crop fields" + description = "SIGPAC Crop fields of Spain - Balearic Islands" + # https://www.caib.es/sites/M170613081930629/f/463418 + # see https://intranet.caib.es/opendatacataleg/dataset/sigpac-2024/resource/3a0bc2e0-3f37-45b7-a7d4-1e8c7cf09bc8 + license = "CC-BY-4.0" # http://www.opendefinition.org/licenses/cc-by + attribution = "Govern de les Illes Balears" + provider = "Govern de les Illes Balears " + columns = { + "DN_OID": "id", + "geometry": "geometry", + "MUNICIPIO": "admin_municipality_code", + "DN_SURFACE": "metrics:area", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + "determination:datetime": "determination:datetime", + } + column_additions = ESBaseConverter.column_additions | {"admin_province_code": "07"} + area_is_in_ha = False + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + use_code_attribute = "USO_SIGPAC" + + # Since 2026 the service publishes a single layer with the current state + # ("Recintes SIGPAC màxima actualitat"); the Catxe field names the month of + # the snapshot, e.g. "maig 2026". The layer is a join, so the fields come + # prefixed (SIGPAC_FOGAIBA.DN_OID, COD_Municipis.NOM, ...). + rest_base_url = "https://ideib.caib.es/geoserveis/rest/services/public/GOIB_SIGPAC_IB/MapServer" + rest_params = { + "where": "USO_SIGPAC NOT IN ('AG','CA','ED','FO','IM','IS','IV','TH','ZC','ZU','ZV','MT')" + } + + def rest_layer_filter(self, layers): + return next(layer for layer in layers if "SIGPAC" in layer["name"].upper()) + + def file_migration(self, gdf, path, uri, layer): + gdf = gdf.rename(columns={c: c.rsplit(".", 1)[-1] for c in gdf.columns if "." in c}) + gdf["determination:datetime"] = gdf["Catxe"].map(snapshot_date) + return gdf diff --git a/build/lib/fiboa_cli/datasets/es_md.py b/build/lib/fiboa_cli/datasets/es_md.py new file mode 100644 index 00000000..86cd9407 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_md.py @@ -0,0 +1,30 @@ +from .es_base import ESBaseConverter + + +class ESCLConverter(ESBaseConverter): + sources = { + "https://idem.comunidad.madrid/recursos_cat_geo/Catalogo/recursos/UsoDelSuelo/spacm_sigpac.cm.zip": [ + "**/RECINTO.shp" + ] + } + id = "es_md" + short_name = "Spain Comunidad de Madrid" + title = "Spain Madrid Crop fields" + description = "SIGPAC is the Agricultural Parcel Identification System implemented throughout the European Union for the application of CAP (Common Agricultural Policy) aid to farmers and ranchers." + provider = "Comunidad de Madrid " + license = "CC0-1.0" # No-limits + + columns = { + "DN_OID": "id", + "geometry": "geometry", + "determination:datetime": "determination:datetime", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + "DN_SURFACE": "metrics:area", + } + use_code_attribute = "USO_SIGPAC" + column_additions = ESBaseConverter.column_additions | { + "determination:datetime": "2024-01-01T00:00:00Z" + } + area_is_in_ha = False diff --git a/build/lib/fiboa_cli/datasets/es_nc.py b/build/lib/fiboa_cli/datasets/es_nc.py new file mode 100644 index 00000000..b84cf58f --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_nc.py @@ -0,0 +1,68 @@ +import re +from os import makedirs, path + +import pandas as pd +import requests +from loguru import logger +from vecorel_cli.vecorel.util import name_from_uri + +from .es_base import ESBaseConverter + + +class NCConverter(ESBaseConverter): + # sources = "https://filescartografia.navarra.es/2_CARTOGRAFIA_TEMATICA/2_6_SIGPAC/" # FULL Download timeout + id = "es_nc" + short_name = "Spain Navarra" + title = "Spain Navarra Crop fields" + description = "SIGPAC Crop fields of Spain - Navarra" + license = "CC-BY-4.0" # https://sigpac.navarra.es/descargas/ + attribution = "Comunidad Foral de Navarra" + provider = "Comunidad Foral de Navarra " + columns = { + "id": "id", + "geometry": "geometry", + "BEGINLIFE": "determination:datetime", + "IDUSO24": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + column_migrations = { + "BEGINLIFE": lambda col: pd.to_datetime(col, format="%d/%m/%Y"), + } + use_code_attribute = "IDUSO24" + index_as_id = True + + def get_urls(self): + # scrape HTML page for sources + content = requests.get("https://sigpac.navarra.es/descargas/", verify=False).text + base = re.search('var rutaBase = "(.*?)";', content).group(1) + last = base.rsplit("/", 1)[-1] + return { + f"https://sigpac.navarra.es/descargas/{base}{src}.zip": [f"{last}{src}.shp"] + for src in re.findall(r'value:"(\d+)"', content) + } + + def prefill_cache(self, uris, cache_folder=None): + if cache_folder is None: + logger.warning("Use -c to prefill the cache dir, working around SSL errors") + return + + makedirs(cache_folder, exist_ok=True) + logger.warning("Suppressing SSL-errors, filling cache with unverified SSL requests") + requests.packages.urllib3.disable_warnings() # Suppress InsecureRequestWarning + for uri in list(uris): + target = path.join(cache_folder, name_from_uri(uri)) + if not path.exists(target): + r = requests.get(uri, verify=False) + if r.status_code == 200: + with open(target, "wb") as f: + f.write(r.content) + else: + logger.error(f"Skipping url {uri}, status_code={r.status_code}") + uris.pop(uri) + + def download_files(self, uris, cache_folder=None): + # Hostname has invalid SSL, prefill cache and avoid ssl-errors + self.prefill_cache(uris, cache_folder) + + return super().download_files(uris, cache_folder=cache_folder) diff --git a/build/lib/fiboa_cli/datasets/es_pv.py b/build/lib/fiboa_cli/datasets/es_pv.py new file mode 100644 index 00000000..1ff293bd --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_pv.py @@ -0,0 +1,62 @@ +import pandas as pd +import requests +from loguru import logger + +from .es_base import ESBaseConverter + + +class ESPVConverter(ESBaseConverter): + variants = { + str( + year + ): f"https://www.geo.euskadi.eus/cartografia/DatosDescarga/Agricultura/SIGPAC/SIGPAC_CAMPA%C3%91A_{year}_V1/" + for year in range(2025, 2015, -1) + } + id = "es_pv" + short_name = "Spain Basque Country" + title = "Spain Basque Country Crop fields" + description = """ +SIGPAC, the geographic information system for the identification of agricultural plots, is the system +that farmers and ranchers must use to apply for community aid related to the area. The reason for +putting this system into effect was the result of a requirement imposed by the European Union on +all Member States. Sigpac began to be used from February 1, 2005, together with the beginning of +the 2005 community aid application period. + """ + provider = "Gobierno Vasco " + attribution = "Basque Government / Gobierno Vasco" + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "id": "id", + "CAMPANA": "determination:datetime", + "USO": "crop:code", + "crop:name": "crop:name", + } + + column_migrations = {"CAMPANA": lambda col: pd.to_datetime(col, format="%Y")} + use_code_attribute = "USO" + index_as_id = True + + def get_urls(self): + if not self.variant: + self.variant = "2024" + logger.warning(f"Choosing first year {self.variant}") + else: + assert self.variant in self.variants + + from bs4 import BeautifulSoup + + # Parse list of zips in two steps from source url + host = "https://www.geo.euskadi.eus" + base = ( + f"/cartografia/DatosDescarga/Agricultura/SIGPAC/SIGPAC_CAMPA%C3%91A_{self.variant}_V1/" + ) + soup = BeautifulSoup(requests.get(f"{host}/{base}").content, "html.parser") + pages = [p["href"] for p in soup.find_all("a") if p["href"].startswith(base)] + parsed = [ + BeautifulSoup(requests.get(f"{host}/{page}").content, "html.parser") for page in pages + ] + zips = [ + p["href"] for soup in parsed for p in soup.find_all("a") if p["href"].endswith(".zip") + ] + return {f"{host}{z}": z.rsplit("/", 1)[1] for z in zips} diff --git a/build/lib/fiboa_cli/datasets/es_vc.py b/build/lib/fiboa_cli/datasets/es_vc.py new file mode 100644 index 00000000..9289babd --- /dev/null +++ b/build/lib/fiboa_cli/datasets/es_vc.py @@ -0,0 +1,54 @@ +import re +from datetime import datetime + +import requests + +from .es_base import ESBaseConverter + + +class ESVCConverter(ESBaseConverter): + variants = {str(year): str(year) for year in range(2024, 2016 - 1, -1)} + id = "es_vc" + short_name = "Spain Valencia" + title = "Spain Valencia Crop Fields" + description = """ +Graphic layer of the plots and enclosures with defined agricultural uses that accompany the information of the +Geographic Information System (SIGPAC) in the Valencian Community valid for the SIGPAC 2024 campaign +(data dated 15-01-2024). + """ + provider = "Spanish Agricultural Guarantee Fund (FEGA) of the Ministry of Agriculture, Fisheries and Food " + attribution = "© Institut Cartogràfic Valencià, Generalitat" + license = "CC-BY-4.0" # see http://www.icv.gva.es/condiciones-de-uso-de-la-geoinformacion-icv + columns = { + "DN_OID": "id", + "geometry": "geometry", + "PROVINCIA": "admin_province_code", + "MUNICIPIO": "admin_municipality_code", + "DN_SURFACE": "metrics:area", + "USO_SIGPAC": "crop:code", + "crop:name": "crop:name", + "crop:name_en": "crop:name_en", + } + area_is_in_ha = False + missing_schemas = { + "properties": { + "admin_province_code": {"type": "string"}, + "admin_municipality_code": {"type": "string"}, + } + } + use_code_attribute = "USO_SIGPAC" + + def get_urls(self): + if not self.variant: + self.variant = next(iter(self.variants)) + self.column_additions["determination:datetime"] = datetime(int(self.variant), 1, 1) + + from bs4 import BeautifulSoup + + base = f"https://descargas.icv.gva.es/dcd/14_mediorural/03_pac/{self.variant}_SIGPAC_0050" + soup = BeautifulSoup(requests.get(f"{base}").content, "html.parser") + result = { + f"{base}/{e.get('href')}": ["*/RECINTO.shp"] + for e in soup.find_all("a", href=re.compile("1403_.*RECINTOS.*")) + } + return result diff --git a/build/lib/fiboa_cli/datasets/fi.py b/build/lib/fiboa_cli/datasets/fi.py new file mode 100644 index 00000000..ed79c059 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/fi.py @@ -0,0 +1,43 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = "https://download.inspire.ruokavirasto-awsa.com/data/2023/LandUse.ExistingLandUse.GSAAAgriculturalParcel.gpkg" + id = "fi" + short_name = "Finland" + title = "Finnish Crop Fields (Maatalousmaa)" + description = """ +The Finnish Food Authority (FFA) since 2020 produces spatial data sets, +more specifically in this context the "Field parcel register" and "Agricultural parcel containing spatial data". +A set called "Agricultural land: arable land, permanent grassland or permanent crop (land use)". + """ + provider = "Finnish Food Authority " + attribution = "Finnish Food Authority" + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "PERUSLOHKOTUNNUS": "id", + "LOHKONUMERO": "block_id", + "area": "metrics:area", + "VUOSI": "determination:datetime", + "KASVIKOODI": "crop:code", + "KASVIKOODI_SELITE_FI": "crop:name", + } + column_migrations = { + # Make year (1st January) from column "VUOSI" + "VUOSI": lambda col: pd.to_datetime(col, format="%Y"), + } + ec_mapping_csv = "https://fiboa.org/code/fi/fi_2023.csv" + + area_is_in_ha = False + area_calculate_missing = True + + missing_schemas = { + "properties": { + "block_id": {"type": "int64"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/fr.py b/build/lib/fiboa_cli/datasets/fr.py new file mode 100644 index 00000000..8f790a0b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/fr.py @@ -0,0 +1,122 @@ +import os +import re + +import multivolumefile +import py7zr +from geopandas import GeoDataFrame +from vecorel_cli.conversion.admin import AdminConverterMixin +from vecorel_cli.vecorel.util import name_from_uri + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import AddHCATMixin + + +class FRConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + # TODO, 2022 works, check (or discover) paths for other years + variants = { + "2024": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.001": [ + "**/RPG_Parcelles.gpkg" # RPG 3.0 renamed PARCELLES_GRAPHIQUES.gpkg + ], + "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.002": [], + "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.003": [], + "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.004": [], + "https://data.geopf.fr/telechargement/download/RPG/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01/RPG_3-0__GPKG_LAMB93_FXX_2024-01-01.7z.005": [], + }, + "2023": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-2__GPKG_LAMB93_FXX_2023-01-01/RPG_2-2__GPKG_LAMB93_FXX_2023-01-01.7z": [ + "**/PARCELLES_GRAPHIQUES.gpkg" + ] + }, + "2022": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FXX_2022-01-01/RPG_2-0__GPKG_LAMB93_FXX_2022-01-01.7z.001": [ + "**/PARCELLES_GRAPHIQUES.gpkg" + ] + }, + "2021": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FXX_2021-01-01/RPG_2-0__GPKG_LAMB93_FXX_2021-01-01.7z": [ + "**/PARCELLES_GRAPHIQUES.gpkg" + ] + }, + "2020": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FR_2020-01-01/RPG_2-0__GPKG_LAMB93_FR_2020-01-01.7z.001": [], + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__GPKG_LAMB93_FR_2020-01-01/RPG_2-0__GPKG_LAMB93_FR_2020-01-01.7z.002": [], + }, + "2019": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0_GPKG_LAMB93_FR-2019/RPG_2-0_GPKG_LAMB93_FR-2019.7z": [ + "**/PARCELLES_GRAPHIQUES.gpkg" + ] + }, + # the newest SHP edition on the download server is 2017; there is no 2018 archive + "2017": { + "https://data.geopf.fr/telechargement/download/RPG/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01/RPG_2-0__SHP_LAMB93_FR-2017_2017-01-01.7z": [ + "**/PARCELLES_GRAPHIQUES.shp" + ] + }, + } + + def download_files(self, uris, cache_folder=None): + """Multi-volume 7z archives (.7z.001, .7z.002, ...) are one 7z stream split + into parts; py7zr reads them through multivolumefile, vecorel-cli does not.""" + volumes = [uri for uri in uris if re.search(r"\.7z\.\d{3}$", uri)] + if not volumes: + return super().download_files(uris, cache_folder) + others = {uri: target for uri, target in uris.items() if uri not in volumes} + # download the parts as plain files (no extraction by the base class) + parts = super().download_files({uri: name_from_uri(uri) for uri in volumes}, cache_folder) + name = name_from_uri(volumes[0]) # .7z.001 + archive = parts[0][0][: -len(".001")] + _, cache_dir = self.get_cache(cache_folder) + folder = os.path.join(cache_dir, "extracted." + os.path.splitext(name)[0]) + if not os.path.exists(folder): + self.info(f"Extracting {len(parts)} volumes of {os.path.basename(archive)}") + with multivolumefile.MultiVolume(archive, mode="rb", ext_digits=3) as volume: + with py7zr.SevenZipFile(volume, "r") as sz: + sz.extractall(folder) + targets = next( + (uris[uri] for uri in volumes if uris[uri]), ["**/PARCELLES_GRAPHIQUES.gpkg"] + ) + paths = [(os.path.join(folder, target), volumes[0]) for target in targets] + if others: + paths.extend(super().download_files(others, cache_folder)) + return paths + + id = "fr" + short_name = "France" + title = "Registre Parcellaire Graphique; Crop Fields France" + description = """ +France has published Crop Field data for many years. Crop fields are declared by farmers within the Common Agricultural Policy (CAP) subsidy scheme. + +The anonymized version is distributed as part of the public service for making reference data available contains graphic data for plots (basic land unit for farmers' declaration) with their main crop. This data has been produced by the Services and Payment Agency (ASP) since 2007. + """ + + provider = "Anstitut National de l'Information Géographique et Forestière " + # Attribution example as described in the open license + attribution = "IGN - Original data from https://geoservices.ign.fr/rpg" + license = "Licence Ouverte / Open Licence " + ec_mapping_csv = "fr_2018.csv" + use_variant_as_determination = True + + columns = { + "geometry": "geometry", + "id_parcel": "id", + "surf_parc": "metrics:area", + "code_cultu": "crop:code", + "code_group": "group_code", + } + + def migrate(self, gdf) -> GeoDataFrame: + if "ID_PARCEL" in gdf.columns: + # Make column names lowercase, harmonize for different years + gdf = gdf.rename(columns={k: k.lower() for k in gdf.columns}) + return super().migrate(gdf) + + column_filters = { + "surf_parc": lambda col: col > 0.0 # fiboa validator requires area > 0.0 + } + + missing_schemas = { + "properties": { + "group_code": {"type": "string"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/hr.py b/build/lib/fiboa_cli/datasets/hr.py new file mode 100644 index 00000000..834deb5b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/hr.py @@ -0,0 +1,108 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + +base = "https://www.apprrr.hr/wp-content/uploads/nipp" + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = "https://www.apprrr.hr/wp-content/uploads/nipp/land_parcels.gpkg" + variants = { + "2024": f"{base}/land_parcels.gpkg", + **{str(y): f"{base}/arkod_31_12_{y}.gpkg" for y in range(2023, 2010, -1)}, + } + id = "hr" + short_name = "Croatia" + title = "Croatian Field Boundaries" + description = """ +Field boundary data for Croatia, provided as part of national agricultural datasets. + +This dataset contains spatial data related to agricultural land use in Croatia, including ARKOD parcel information, +environmentally sensitive areas, High Nature Value Grasslands, protective buffer strips around watercourses, and vineyard +classifications. The data is crucial for managing agricultural activities, ensuring compliance with environmental regulations, +and supporting sustainable land use practices. + """ + + provider = "Agencija za plaćanja u poljoprivredi, ribarstvu i ruralnom razvoju " + + attribution = ( + "copyright © 2024. Agencija za plaćanja u poljoprivredi, ribarstvu i ruralnom razvoju" + ) + + license = "Prostorni podaci i servisi " + index_as_id = True + + column_migrations = {"land_use_id": lambda col: col.astype(int)} + + columns = { + "id": "id", + "land_use_id": "crop:code", + "area": "metrics:area", + "geometry": "geometry", + "home_name": "home_name", + "perim": "metrics:perimeter", + "slope": "slope", + "z_avg": "height", + "eligibility_coef": "eligibility_coef", + "mines_status": "mines_status", + "mines_year_removed": "mines_year_removed", + "water_protect_zone": "water_protect_zone", + "natura2000": "natura2000", + "natura2000_ok": "natura2000_ok", + "natura2000_pop": "natura2000_pop", + "natura2000_povs": "natura2000_povs", + "anc": "anc", + "anc_area": "anc_area", + "rp": "rp", + "sanitary_protection_zone": "sanitary_protection_zone", + "tvpv": "tvpv", + "ot_nat": "ot_nat", + "ot_nat_area": "ot_nat_area", + "irrigation": "irrigation", + "irrigation_source": "irrigation_source", + "irrigation_type": "irrigation_type", + "jpaid": "jpaid", + } + + ec_mapping_csv = "hr_2020.csv" + + missing_schemas = { + "required": [ + "mines_status", + "water_protect_zone", + "natura2000", + "sanitary_protection_zone", + "irrigation", + "jpaid", + ], + "properties": { + "land_use_id": {"type": "integer"}, + "home_name": {"type": "string"}, + "slope": {"type": "double"}, + "height": {"type": "double"}, + "eligibility_coef": {"type": "double"}, + "mines_status": {"type": "string", "enum": ["N", "M", "R"]}, + "mines_year_removed": {"type": "int32"}, + "water_protect_zone": {"type": "string"}, + "natura2000": {"type": "double"}, + "natura2000_ok": {"type": "string"}, + "natura2000_pop": {"type": "double"}, + "natura2000_povs": {"type": "double"}, + "anc": {"type": "int32"}, + "anc_area": {"type": "double"}, + "rp": {"type": "int32"}, + "sanitary_protection_zone": {"type": "string"}, + "tvpv": {"type": "int32"}, + "ot_nat": {"type": "int32"}, + "ot_nat_area": {"type": "double"}, + "irrigation": {"type": "int32"}, + "irrigation_source": {"type": "int32"}, + "irrigation_type": {"type": "int32"}, + "jpaid": {"type": "string"}, + }, + } + + area_is_in_ha = False + area_calculate_missing = True + use_variant_as_determination = True diff --git a/build/lib/fiboa_cli/datasets/ie.py b/build/lib/fiboa_cli/datasets/ie.py new file mode 100644 index 00000000..a66a0cee --- /dev/null +++ b/build/lib/fiboa_cli/datasets/ie.py @@ -0,0 +1,64 @@ +import geopandas as gpd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.convert_gml import gml_assure_columns +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.data import read_data_csv +from .commons.hcat import AddHCATMixin + + +class IEConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + str(year): { + f"https://dafm-inspire-atom.s3.eu-west-1.amazonaws.com/files/LU/GSAA_{year}.zip": [ + f"GSAA_{year}.gml" + ] + } + for year in range(2024, 2021, -1) + } + + id = "ie" + short_name = "Ireland" + title = "Ireland INSPIRE Geospatial aid application (GSAA) dataset" + description = "This data represents the outline shape of LPIS parcels as claimed under area based schemes. The dataset includes the crops claimed as part of the annual GSAA. Yearly information provided through the beneficiary declaration." + + provider = "Department of Agriculture, Food and the Marine " + attribution = "Ireland Department of Agriculture, Food and the Marine" + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "crop_name": "crop:name", + "crop_code": "crop:code", + "localId": "id", + "observationDate": "determination:datetime", + } + ec_mapping_csv = "https://fiboa.org/code/ie/ie.csv" + + column_migrations = { + "observationDate": lambda col: col.str.replace("+01:00", "T00:00:00Z"), + } + + def migrate(self, gdf) -> gpd.GeoDataFrame: + # crop_name can be multiple: "crop1, crop2, crop3". We only read the main crop (first). + gdf["crop_name"] = gdf["crop_name"].str.split(", ").str.get(0) + gdf = gdf[gdf["crop_name"] != "Void"] # Exclude non-agriculture fields + + rows = read_data_csv("ie_2023.csv") + mapping = {row["original_name"]: index + 1 for index, row in enumerate(rows)} + gdf["crop_code"] = gdf["crop_name"].map(mapping) + + return super().migrate(gdf) + + def file_migration( + self, gdf: gpd.GeoDataFrame, path: str, uri: str, layer: str = None + ) -> gpd.GeoDataFrame: + return gml_assure_columns( + gdf, + path, + uri, + layer, + crop_name={"ElementPath": "specificLandUse@title", "Type": "String", "Width": 255}, + ) + + def layer_filter(self, layer: str, uri: str) -> bool: + return layer == "ExistingLandUseObject" diff --git a/build/lib/fiboa_cli/datasets/india_10k.py b/build/lib/fiboa_cli/datasets/india_10k.py new file mode 100644 index 00000000..843b091d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/india_10k.py @@ -0,0 +1,29 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter + + +class IndiaConverter(AdminConverterMixin, FiboaBaseConverter): + area_is_in_ha = False + sources = {"https://zenodo.org/api/records/7315090/files-archive": "india_10k.zip"} + id = "in_10k" + short_name = "India 10k" + title = "10,000 Crop Field Boundaries across India" + description = """ +Release of dataset and neural network weights accompanying the paper +"Unlocking large-scale crop field delineation in smallholder farming systems with transfer learning and weak supervision" +(forthcoming in Remote Sensing). Ten thousand crop fields in India were delineated manually through inspection +of high-resolution satellite imagery (Airbus SPOT). We also provide the weights of the highest performing +neural network (FracTAL ResUNet architecture) pre-trained in France and fine-tuned on Airbus SPOT images in India. +The model was trained in MXNet 1.6.0 and can be loaded with the "model.load_parameters()" function. + """ + index_as_id = True + provider = "Zenodo " + attribution = "https://doi.org/10.5281/zenodo.7315090" + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "id": "id", + "area": "metrics:area", + } + column_additions = {"determination:datetime": "2022-11-12T00:00:00Z"} diff --git a/build/lib/fiboa_cli/datasets/it_1.py b/build/lib/fiboa_cli/datasets/it_1.py new file mode 100644 index 00000000..46f3387a --- /dev/null +++ b/build/lib/fiboa_cli/datasets/it_1.py @@ -0,0 +1,52 @@ +"""EuroCropsV2 — Italy, Tuscany (NUTS-2: ITI1). + +Single-region Italian converter using the harmonised, multi-year GeoParquet +release published by the JRC at +``https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/DRLL/EuroCropsV2/gpqtv201/``. + +EuroCropsV2 itself is a separate dataset from the original (TUM) EuroCrops; it +covers Italy via Tuscany only at the moment (NUTS-2 ``ITI1``). HCAT3 names +and codes are joined in at conversion time from the EuroCropsV2 mapping table. + +See the wider survey at +``fiboa-data-survey/data/EU-EuroCropsV2.md`` for the full picture. +""" + +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + +JRC_BASE = "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/DRLL/EuroCropsV2/gpqtv201" +NUTS = "iti1" + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + id = "it_1" + short_name = "Italy, Tuscany" + title = "Italy Tuscany (ITI1) Crop Fields — EuroCropsV2" + description = """ +EuroCropsV2 harmonised Geo-Spatial Application (GSA) declarations for Tuscany (NUTS-2: ITI1), Italy. Produced by +the JRC, EUROSTAT and Technical University of Munich from the Italian paying agency's parcel-level crop +declarations. + +The source is distributed as one GeoParquet per year (2016-2023) in EPSG:3035 (LAEA Europe). HCAT3 names and +codes are joined in from the EuroCropsV2 NUTS mapping table at conversion time. + """ + provider = ( + "Joint Research Centre, European Commission " + "" + ) + attribution = "European Commission, Joint Research Centre — EuroCropsV2" + license = "CC-BY-4.0" + variants = {str(y): f"{JRC_BASE}/{NUTS}_{y}.parquet" for y in reversed(range(2016, 2024))} + + admin_country_code = "IT" + admin_subdivision_code = "1" + ec_mapping_csv = "https://fiboa.org/code/it/iti1.csv" + columns = { + "geometry": "geometry", + "cropfield": "id", + "area_ha": "metrics:area", + "original_code": "crop:code", + } diff --git a/build/lib/fiboa_cli/datasets/jecam.py b/build/lib/fiboa_cli/datasets/jecam.py new file mode 100644 index 00000000..45693ae1 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/jecam.py @@ -0,0 +1,77 @@ +import geopandas as gpd +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter +from fiboa_cli.datasets.commons.data import read_data_csv +from fiboa_cli.datasets.commons.hcat import CROP_EXTENSION + +CODE_LIST = "https://fiboa.org/code/jecam/crop.csv" + + +class JecamConvert(FiboaBaseConverter): + sources = {"https://dataverse.cirad.fr/api/access/datafile/17993": ["*.shp"]} + id = "jecam" + short_name = "Jecam Sirad" + title = "Harmonized in situ JECAM datasets for agricultural land use mapping and monitoring in tropical countries" + description = """ +Harmonized in situ JECAM datasets for agricultural land use mapping and monitoring in tropical countries + +This database contains nine land use / land cover datasets collected in a standardized manner between 2013 and 2022 in seven tropical countries within the framework of the international JECAM initiative: Burkina Faso (Koumbia), Madagascar (Antsirabe), Brazil (São Paulo and Tocantins), Senegal (Nioro, Niakhar, Mboro, Tattaguine and Koussanar), Kenya (Muranga), Cambodia (Kandal) and South Africa (Mpumalanga) (cf Study_sites‧kml). +These quality-controlled datasets are distinguished by ground data collected at field scale by local experts, with precise geographic coordinates, and following a common protocol. This database, which contains 31879 records (24 287 crop and 7 592 non-crop) is a geographic layer in Shapefile format in a Geographic Coordinates System with Datum WGS84. +Field surveys were conducted yearly in each study zone, either around the growing peak of the cropping season, for the sites with a main growing season linked to the rainy season such as Burkina Faso, or seasonally, for the sites with multiple cropping (e‧g. São Paulo site). +The GPS waypoints were gathered following an opportunistic sampling approach along the roads or tracks according to their accessibility, while ensuring the best representativity of the existing cropping systems in place. GPS waypoints were also recorded on different types of non-crop classes (e‧g. natural vegetation, settlement areas, water bodies) to allow differentiating crop and non-crop classes. Waypoints were only recorded for homogenous fields/entities of at least 20 x 20 m². +To facilitate the location of sampling areas and the remote acquisition of waypoints, field operators were equipped with GPS tablets providing access to a QGIS project with Very High Spatial Resolution (VHSR) images ordered just before the surveys. For each waypoint, a set of attributes, corresponding to the cropping practices (crop type, cropping pattern, management techniques) were recorded (for more informations about data, see data paper being published). +These datasets can be used to validate existing cropland and crop types/practices maps in the tropics, but also, to assess the performances and the robustness of classification methods of cropland and crop types/practices in a large range of Southern farming systems. + +Citation: Jolivot, Audrey; Lebourgeois, Valentine; Ameline, Mael; Andriamanga, Valerie; Bellon, Beatriz; Castets, M +athieu; Crespin-Boucaud, Arthur; Defourny, Pierre; Diaz, Santiana; Dieye, Mohamadou; Dupuy, Stephane; Ferraz, Rodrigo; +Gaetano, Raffaele; Gely, Marie; Jahel, Camille; Kabore, Bertin; Lelong, Camille; Le Maire, Guerric; Leroux, Louise; +Lo Seen, Danny; Muthoni, Martha; Ndao, Babacar; Newby, Terry; De Oliveira Santos, Cecilia Lira Melo; Rasoamalala, Eloise; +Simoes, Margareth; Thiaw, Ibrahima; Timmermans, Alice; Tran, Annelise; Begue, Agnes, 2021, +"Harmonized in situ JECAM datasets for agricultural land use mapping and monitoring in tropical countries", +https://doi.org/10.18167/DVN1/P7OLAP, CIRAD Dataverse, V4 + """ + provider = "IEEE GRSS " + attribution = "JECAM SIRAD, https://doi.org/10.18167/DVN1/P7OLAP" + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "Id": "id", + "Area_ha": "metrics:area", + "AcquiDate": "determination:datetime", + "admin:country_code": "admin:country_code", + "SiteName": "site_name", + "crop:code": "crop:code", + "CropType1": "crop:name", + "Irrigated": "irrigated", + } + column_additions = { + "crop:code_list": CODE_LIST, + } + extensions = {ADMIN_DIVISION, CROP_EXTENSION} + missing_schemas = { + "properties": { + "site_name": {"type": "string"}, + "crop:name": {"type": "string"}, + "crop:code_list": {"type": "string"}, + "irrigated": {"type": "boolean"}, + } + } + + def migrate(self, gdf) -> gpd.GeoDataFrame: + gdf = super().migrate(gdf) + rows = read_data_csv("country_codes.csv") + mapping = {row["name"]: row["alpha-2"] for row in rows} + gdf["admin:country_code"] = gdf["Country"].map(mapping) + + rows = read_data_csv("jecam_crop.csv") + mapping = {row["crop_name"]: index + 1 for index, row in enumerate(rows)} + # todo: The dataset has null values for crop code, but the crop extension + # requires a string. We set them to empty strings for now, + # but it should be reconsidered in the future + # (i.e. the removal of .fillna("").astype(str) ) + gdf["crop:code"] = gdf["CropType1"].map(mapping).fillna("").astype(str) + + gdf.loc[gdf["Area_ha"] == 0, "Area_ha"] = None + gdf["Irrigated"] = gdf["Irrigated"].astype(bool) + return gdf diff --git a/build/lib/fiboa_cli/datasets/jp.py b/build/lib/fiboa_cli/datasets/jp.py new file mode 100644 index 00000000..aed27487 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/jp.py @@ -0,0 +1,43 @@ +from fiboa_cli.conversion.duckdb import FiboaDuckDBBaseConverter + + +class JPConverter(FiboaDuckDBBaseConverter): + variants = { + "2024": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2024.parquet", + "2023": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2023.parquet", + "2022": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2022.parquet", + "2021": "https://data.source.coop/pacificspatial/field-polygon-jp/parquet/jp_field_polygons_2021.parquet", + "test": "./tests/data-files/convert/jp/jp_field_polygons_2024.parquet", + } + + id = "jp" + short_name = "Japan" + title = "Japan Fude Parcels" + description = """ +Japanese Farmland Parcel Polygons (Fude Polygons in Japanese) represent parcel information of farmland. +The polygons are manually digitized data derived from aerial imagery, such as satellite images. Since no +on-site verification or similar procedures have been conducted, the data may not necessarily match the actual +current conditions. Fude Polygons are created for the purpose of roughly indicating the locations of farmland. + """ + + provider = "Japanese Ministry of Agriculture, Forestry and Fisheries (MAFF, 農林水産省) " + attribution = "Fude Polygon Data (2021-2024). Japanese Ministry of Agriculture, Forestry and Fisheries. Processed by Pacific Spatial Solutions, Inc" + license = "CC-BY-4.0" + + columns = { + "GEOM": "geometry", + "polygon_uuid": "id", + "land_type_en": "land_type_en", + "local_government_cd": "admin_local_code", + "issue_year": "determination:datetime", + } + # SQL migrations (DuckDB converter): per-feature determination date from the issue year + column_migrations = { + "issue_year": "make_timestamp(CAST(issue_year AS INTEGER), 1, 1, 0, 0, 0) AT TIME ZONE 'UTC'", + } + missing_schemas = { + "properties": { + "land_type_en": {"type": "string"}, + "admin_local_code": {"type": "string"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/lacuna_labels.py b/build/lib/fiboa_cli/datasets/lacuna_labels.py new file mode 100644 index 00000000..30240e05 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/lacuna_labels.py @@ -0,0 +1,60 @@ +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class LacunaLabelsConverter(FiboaBaseConverter): + sources = "https://africa-field-boundary-labels.s3.us-west-2.amazonaws.com/mapped_fields_final.parquet" + id = "lacuna" + short_name = "Lacuna Labels" + title = "A region-wide, multi-year set of crop field boundary labels for Africa" + description = """ +The [Lacunalabels](https://github.com/agroimpacts/lacunalabels/) repository hosts +the analytical code and pointers to datasets +resulting from a project to generate a continent-wide set of crop field +labels for Africa covering the years 2017-2023. The data are intended +for training and assessing machine learning models that can be used to +map agricultural fields over large areas and multiple years. + +The project was funded by the [Lacuna Fund](https://lacunafund.org/), +and led by [Farmerline](https://farmerline.co/), in collaboration with +[Spatial Collective](https://spatialcollective.com/) and the +[Agricultural Impacts Research Group](agroimpacts.info) at +[Clark University](https://www.clarku.edu/departments/geography/). + +Please refer to the [technical report](docs/report/technical-report.pdf) +for more details on the methods used to develop the dataset, an analysis +of label quality, and usage guidelines, and the publication: + +Estes, L. D., Wussah, A., Asipunu, M., Gathigi, M., Kovačič, P., Muhando, J., +Yeboah, B. V., Addai, F. K., Akakpo, E. S., Allotey, M. K., Amkoya, P., Amponsem, E., +Donkoh, K. D., Ha, N., Heltzel, E., Juma, C., Mdawida, R., Miroyo, A., Mucha, J., +Mugami, J., Mwawaza, F., Nyarko, D. A., Oduor, P., Ohemeng, K. N., Segbefia, S. I. D., +Tumbula, T., Wambua, F., Xeflide, G. H., Ye, S., Yeboah, F.(2024). A region-wide, +multi-year set of crop field boundary labels for Africa. arXiv:2412.18483. + +Data is published at https://zenodo.org/records/11060871 and can be used in accordance with +[Planet’s participant license agreement for the NICFI contract](https://go.planet.com/nicfi-pla-2024). + """ + provider = "Planet Labs PBC " + attribution = "Planet Labs Inc." + + license = "Planet NICFI participant license agreement " + + columns = { + "id": "id", + "name": "name", + "assignment_id": "assignment_id", + "image_date": "image_date", + "completion_time": "completion_time", + "category": "land_type", + "geometry": "geometry", + } + + missing_schemas = { + "properties": { + "name": {"type": "string"}, + "land_type": {"type": "string", "enum": ["annualcropland"]}, + "assignment_id": {"type": "string"}, + "image_date": {"type": "string"}, + "completion_time": {"type": "date-time"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/lt.py b/build/lib/fiboa_cli/datasets/lt.py new file mode 100644 index 00000000..42a00b3a --- /dev/null +++ b/build/lib/fiboa_cli/datasets/lt.py @@ -0,0 +1,13 @@ +from fiboa_cli.datasets.commons.euro_land import EuroLandBaseConverter + + +class LTConverter(EuroLandBaseConverter): + id = "lt" + short_name = "Lithuania" + title = "Lithuania crop fields" + description = "Collection of data on agricultural land and crop areas, cultivated crops in the territory of the Republic of Lithuania" + + provider = "Nacionalinė mokėjimo agentūra prie Žemės ūkio ministerijos " + attribution = "Nacionalinė mokėjimo agentūra prie Žemės ūkio ministerijos" + ec_mapping_csv = "lt_2021.csv" + sources = {"https://zenodo.org/records/14384070/files/LT_2024.zip": ["GSA-LT-2024.geoparquet"]} diff --git a/build/lib/fiboa_cli/datasets/lu.py b/build/lib/fiboa_cli/datasets/lu.py new file mode 100644 index 00000000..d7d5a738 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/lu.py @@ -0,0 +1,25 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = { + "https://data.public.lu/fr/datasets/r/b4ae6690-7e4c-4454-8b60-9fa33ba6a61b": "lu.zip" + } + id = "lu" + short_name = "Luxembourg" + title = "Luxembourg FLIK Parcels" + description = """ +The Land Parcel Identification System (LPIS) is a reference database of the agriculture parcels used as a basis for area-related payments to farmers in relation to the Common Agricultural Policy (CAP). These payments are (co)financed by the European Agricultural Guarantee Fund (‘EAGF’) and the European Agricultural Fund for Rural Development (‘EAFRD’). + +To ensure that payments are regular, the CAP relies on the Integrated Administration and Control System (IACS), a set of comprehensive administrative and on-the-spot checks on subsidy applications, which is managed by the Member States. The Land Parcel Identification System (LPIS) is a key component of the IACS. It is an IT system based on ortho imagery (aerial or satellite photographs) which records all agricultural parcels in the Member States. + """ + provider = "Administration des services techniques de l'agriculture " + attribution = "Luxembourg ministry of Agriculture" + license = "CC-BY-4.0" + columns = { + "geometry": "geometry", + "FLIK": "id", + "determination:datetime": "determination:datetime", + } diff --git a/build/lib/fiboa_cli/datasets/lv.py b/build/lib/fiboa_cli/datasets/lv.py new file mode 100644 index 00000000..aa62293f --- /dev/null +++ b/build/lib/fiboa_cli/datasets/lv.py @@ -0,0 +1,53 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import AddHCATMixin + +count = 3000 + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = { + "https://karte.lad.gov.lv/arcgis/services/lauki/MapServer/WFSServer" + f"?request=GetFeature&service=wfs&version=2.0.0&typeNames=Lauki&count={count}&startindex={count * i}": f"lv_{i}_{count}.xml" + for i in range( + 500000 // count + ) # TODO number should be dynamic, stop reading with 0 results + } + + id = "lv" + short_name = "Latvia" + title = "Latvia Lauki Parcels" + description = """ +Latvia offers parcel data on a [public map, available to any user](https://www.lad.gov.lv/lv/lauku-registra-dati). + +The land register is a geographic information system (GIS) that gathers information about agricultural land eligible for state and European Union support from direct support scheme payments or environmental, climate, and rural landscape improvement payments. + +The GIS of the field register contains a database of field blocks with interconnected spatial cartographic data and information of attributes subordinate to them: geographic attachment, identification numbers, and area information. + +Relevant datasets are: Country blocks (Lauku Bloki), Fields (Lauki), and Landscape elements. + """ + provider = "Rural Support Service Republic of Latvia (Lauku atbalsta dienests) " + attribution = "Lauku atbalsta dienests" + license = "CC-BY-SA-4.0" # Not sure, taken from Eurocrops. It is "public" and free and "available to any user" + columns = { + "OBJECTID": "id", + "PARCEL_ID": "parcel_id", + "geometry": "geometry", + "DATA_CHANGED_DATE": "determination:datetime", + "area": "metrics:area", + "PRODUCT_CODE": "crop:code", + "PRODUCT_DESCRIPTION": "crop:name", + } + missing_schemas = { + "properties": { + "parcel_id": { + "type": "uint64", + } + } + } + ec_mapping_csv = "lv_2021.csv" + column_migrations = { + "PRODUCT_CODE": lambda col: col.fillna(0).astype(int).astype(str), + } + area_calculate_missing = True diff --git a/build/lib/fiboa_cli/datasets/nl.py b/build/lib/fiboa_cli/datasets/nl.py new file mode 100644 index 00000000..42bd0ba7 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/nl.py @@ -0,0 +1,89 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + +# see https://service.pdok.nl/rvo/gewaspercelen/atom/basisregistratie_gewaspercelen_brp.xml +# (the old feed rvo/brpgewaspercelen/atom/v1_0/ redirects here since 2026) +base = "https://service.pdok.nl/rvo/gewaspercelen/atom/downloads" + + +class NLCropConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + area_calculate_missing = True + variants = { + "2026": f"{base}/gewaspercelen_concept_2026.gpkg", + **{str(y): f"{base}/brpgewaspercelen_definitief_{y}.gpkg" for y in range(2025, 2019, -1)}, + # the zip editions each contain one FileGDB (naming varies per year) + **{ + str(y): {f"{base}/brpgewaspercelen_definitief_{y}.zip": ["*.gdb"]} + for y in range(2019, 2008, -1) + }, + } + + id = "nl" + short_name = "Netherlands (Crops)" + title = "BRP Crop Field Boundaries for The Netherlands (CAP-based)" + description = """ +BasisRegistratie Percelen (BRP) combines the location of +agricultural plots with the crop grown. The data set +is published by RVO (Netherlands Enterprise Agency). The boundaries of the agricultural plots +are based within the reference parcels (formerly known as AAN). A user an agricultural plot +annually has to register his crop fields with crops (for the Common Agricultural Policy scheme). +A dataset is generated for each year with reference date May 15. +A view service and a download service are available for the most recent BRP crop plots. + + + +Data is currently available for the years 2009 to 2025 (final) and 2026 (concept). + """ + + provider = ( + "RVO / PDOK " + ) + # Both http://creativecommons.org/publicdomain/zero/1.0/deed.nl and http://creativecommons.org/publicdomain/mark/1.0/ + license = "CC0-1.0" + + columns = { + "geometry": "geometry", + "id": "id", + "area": "metrics:area", + "category": "coverage", + "gewascode": "crop:code", + "gewas": "crop:name", + "jaar": "determination:datetime", + } + + def migrate(self, gdf): + if "GWS_GEWASCODE" in gdf.columns: + # 2009-2019 FileGDB editions: prefixed names, no year column, and + # only a m2 shape area (left unmapped; area_calculate_missing + # derives metrics:area from the geometry instead) + gdf = gdf.rename( + columns={ + "GWS_GEWASCODE": "gewascode", + "GWS_GEWAS": "gewas", + "CAT_GEWASCATEGORIE": "category", + } + ) + gdf["jaar"] = int(self.variant) + return super().migrate(gdf) + + column_filters = { + # category = "Grasland" | "Bouwland" | "Sloot" | "Landschapselement" + "category": lambda col: col.isin(["Grasland", "Bouwland"]) + } + + column_migrations = { + # Add 15th of may to original "year" (jaar) column + "jaar": lambda col: pd.to_datetime(col, format="%Y") + pd.DateOffset(months=4, days=14) + } + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + ec_mapping_csv = "https://fiboa.org/code/nl/nl.csv" + index_as_id = True + + missing_schemas = { + "properties": { + "coverage": {"type": "string", "enum": ["Grasland", "Bouwland"]}, + } + } diff --git a/build/lib/fiboa_cli/datasets/nl_block.py b/build/lib/fiboa_cli/datasets/nl_block.py new file mode 100644 index 00000000..23f7f32d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/nl_block.py @@ -0,0 +1,46 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(AdminConverterMixin, FiboaBaseConverter): + sources = ( + "https://service.pdok.nl/rvo/referentiepercelen/atom/downloads/referentiepercelen.gpkg" + ) + + id = "nl_block" + short_name = "Netherlands (parcels)" + title = "Field blocks for The Netherlands" + description = """ +A field block (Dutch: "Referentieperceel"), formerly known as "AAN" (Agrarisch Areaal Nederland), +is a contiguous agricultural area surrounded by permanent boundaries, which is cultivated by one or +more farmers with one or more crops, is fully or partially set aside or is fully or partially +taken out of production. + +The following field block types exist: + +- Woods (Hout) +- Agricultural area (Landbouwgrond) +- Other (Overig) +- Water (Water) + +We filter on "Agricultural area" in this converter. +For crop data, look at BasisRegistratie gewasPercelen (BRP) + """ + + provider = "RVO / PDOK " + # Both http://creativecommons.org/publicdomain/zero/1.0/deed.nl and http://creativecommons.org/publicdomain/mark/1.0/ + license = "CC0-1.0" + column_additions = {"determination:datetime": "2023-06-15T00:00:00Z"} + columns = {"geometry": "geometry", "id": "id", "area": "metrics:area", "versiebron": "source"} + column_filters = { + # type = "Hout" | "Landbouwgrond" | "Overig" | "Water" + "type": lambda col: col == "Landbouwgrond" + } + index_as_id = True + area_calculate_missing = True + missing_schemas = { + "properties": { + "source": {"type": "string"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/nz.py b/build/lib/fiboa_cli/datasets/nz.py new file mode 100644 index 00000000..9f66a50f --- /dev/null +++ b/build/lib/fiboa_cli/datasets/nz.py @@ -0,0 +1,80 @@ +import os + +import pandas as pd +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.data import read_data_csv + + +class NZCropConverter(FiboaBaseConverter): + data_access = """ + Download manually (Koordinates login required) and place the zip in the cache folder: + - 2020: https://data.mfe.govt.nz/layer/105407-irrigated-land-area-raw-2020-update/ (mfe-irrigated-land-area-raw-2020-update-SHP.zip) + - 2017: https://data.mfe.govt.nz/layer/90838-irrigated-land-area-2017/ (mfe-irrigated-land-area-2017-SHP.zip) + Alternatively pass the zip with the `-i` CLI parameter. + """ + variants = { + # keys are the cache filenames vecorel resolves before attempting a download + "2020": {"mfe-irrigated-land-area-raw-2020-update-SHP.zip": ["*.shp"]}, + "2017": {"mfe-irrigated-land-area-2017-SHP.zip": ["*.shp"]}, + } + + id = "nz" + short_name = "New Zealand" + title = "Irrigated land area" + description = """ +This dataset covers Irrigated Land. Adapted by Ministry for the Environment and Statistics +New Zealand to provide for environmental reporting transparency + +The spatial data covers all mainland regions of New Zealand, with the exception of Nelson, which is not believed to +contain significant irrigated areas. The spatial dataset is an update of the national dataset that was first +created in 2017. The current update has incorporated data from the 2019 – 2020 irrigation season. + """ + + def download_files(self, uris, cache_folder=None): + """The sources are manual downloads (Koordinates login): resolve the bare + filenames from ``variants`` against the cache folder instead of the cwd.""" + _, cache_dir = self.get_cache(cache_folder) + resolved = {} + for uri, target in uris.items(): + if "://" not in uri and not os.path.isabs(uri) and not os.path.exists(uri): + cached = os.path.join(cache_dir, uri) + if not os.path.exists(cached): + raise FileNotFoundError( + f"{uri} is a manual download; place it in {cache_dir} (see data_access)" + ) + uri = cached + resolved[uri] = target + return super().download_files(resolved, cache_folder) + + provider = "Aqualinc Research Limited " + license = "CC-BY-4.0" + extensions = {ADMIN_DIVISION} + index_as_id = True + columns = { + "id": "id", + "geometry": "geometry", + "type": "type", + "area_ha": "metrics:area", + "yearmapped": "determination:datetime", + "Region": "admin:subdivision_code", + } + column_migrations = {"yearmapped": lambda col: pd.to_datetime(col, format="%Y")} + column_additions = { + "admin:country_code": "NZ", + } + missing_schemas = { + "properties": { + "type": { + "type": "string", + }, + } + } + + def migrate(self, gdf): + # MAP back; https://www.iso.org/obp/ui/#iso:code:3166:NZ + rows = read_data_csv("nz_region_codes.csv") + mapping = {row["Subdivision name"]: row["3166-2 code"][len("NZ-") :] for row in rows} + gdf["Region"] = gdf["Region"].map(mapping) + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/planet_afb.py b/build/lib/fiboa_cli/datasets/planet_afb.py new file mode 100644 index 00000000..9ebf1f6c --- /dev/null +++ b/build/lib/fiboa_cli/datasets/planet_afb.py @@ -0,0 +1,51 @@ +# Converter for geopackage output of Planet's Field Boundaries dataset. +import os +import re + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(FiboaBaseConverter): + sources = None + data_access = """ + Data must be obtained from the Planet subscriptions API, see + https://developers.planet.com/docs/planetary-variables/field-boundaries/ for additional information. + The output should look something like FIELD_BOUNDARIES_v1.0.0_S2_P1M-20230101T000000Z_fb.gpkg + """ + id = "planet_afb" + short_name = "Planet Field Boundaries" + title = "Field boundaries created by Planet's Automated Field Boundary detection algorithm" + description = """ +These field boundaries are created by Planet Labs, using an automated process using satellite imagery. The algorithm +works on a monthly basis and is available for the entire globe. The data is provided in GeoPackage format. +For more information, see the [field boundaries technical specification](https://planet.widen.net/s/5vq8w5wjvf/2403.08_mar-9444-field-boundaries-technical-specification-sheet-3) + """ + provider = "Planet Labs Inc. " + attribution = "© 2024 Planet Labs, PBC" + license = "Proprietary License " + extensions = {"https://fiboa.org/planet-extension/v0.1.0/schema.yaml"} + columns = { + "polygon_id": "id", # fiboa core field + "area_ha": "metrics:area", # fiboa core field + "geometry": "geometry", # fiboa core field + "determination:datetime": "determination:datetime", # fiboa core field + "ca_ratio": "planet:ca_ratio", # From Planet extension for fiboa + "micd": "planet:micd", # From Planet extension for fiboa + "qa": "planet:qa", # From Planet extension for fiboa + } + column_additions = {"determination_method": "auto-imagery"} + missing_schemas = {} + + def file_migration(self, gdf, path, uri, layer=None): + """ + Perform file-specific migration by extracting a date from the filename and adding it as a column in the GeoDataFrame. + + Assumed filename format: + FIELD_BOUNDARIES_v1.0.0_S2_P1M-20230101T000000Z_fb.gpkg + """ + name = os.path.basename(path) + matches = re.search(r"-(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z_fb.gpkg$", name) + if matches: + dt = matches.groups() + gdf["determination:datetime"] = f"{dt[0]}-{dt[1]}-{dt[2]}T{dt[3]}:{dt[4]}:{dt[5]}Z" + return gdf diff --git a/build/lib/fiboa_cli/datasets/pt.py b/build/lib/fiboa_cli/datasets/pt.py new file mode 100644 index 00000000..25b0769e --- /dev/null +++ b/build/lib/fiboa_cli/datasets/pt.py @@ -0,0 +1,53 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class PTConverter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + id = "pt" + title = "Field boundaries for Portugal" + short_name = "Portugal" + description = "Open field boundaries (identificação de parcelas) from Portugal" + # see https://www.ifap.pt/isip/ows/ + BASE = "https://www.ifap.pt/isip/ows/resources/" + variants = { + "2023": BASE + "2023/Continente.gpkg", + "2022": BASE + "2022/2022.zip", + "2021": BASE + "2021/2021.zip", + "2020": BASE + "2017-2020/2020.zip", + "2019": BASE + "2017-2020/2019.zip", + "2018": BASE + "2017-2020/2018.zip", + "2017": BASE + "2017-2020/2017.zip", + "2016": BASE + "2011_2016/2016.zip", + "2015": BASE + "2011_2016/2015.zip", + # ... + } + + def layer_filter(self, layer, uri): + return layer.startswith("Culturas_") + + provider = ( + "IPAP - Instituto de Financiamento da Agricultura e Pescas " + ) + license = "No conditions apply " + columns = { + "geometry": "geometry", + "OSA_ID": "id", + "CUL_ID": "block_id", + "CUL_CODIGO": "crop:code", + "CT_português": "crop:name", + "Shape_Area": "metrics:area", + "Shape_Length": "metrics:perimeter", + } + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + ec_mapping_csv = "https://fiboa.org/code/pt/pt.csv" + column_additions = { + "determination:datetime": "2023-01-01T00:00:00Z", + } + area_is_in_ha = False + missing_schemas = { + "properties": { + "block_id": {"type": "int64"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/se.py b/build/lib/fiboa_cli/datasets/se.py new file mode 100644 index 00000000..2853b11b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/se.py @@ -0,0 +1,47 @@ +import pandas as pd +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + "2024": { + "http://epub.sjv.se/inspire/inspire/wfs?SERVICE=WFS%20&REQUEST=GetFeature%20&VERSION=1.0.0%20&TYPENAMES=inspire:arslager_skifte%20&outputFormat=shape-zip%20&CQL_FILTER=arslager=%272024%27%20%20and%20geom%20is%20not%20null%20&format_options=CHARSET:UTF-8": "se2024.zip" + }, + "2023": { + "http://epub.sjv.se/inspire/inspire/wfs?SERVICE=WFS%20&REQUEST=GetFeature%20&VERSION=1.0.0%20&TYPENAMES=inspire:arslager_skifte%20&outputFormat=shape-zip%20&CQL_FILTER=arslager=%272023%27%20%20and%20geom%20is%20not%20null%20&format_options=CHARSET:UTF-8": "se2023.zip" + }, + } + id = "se" + short_name = "Sweden" + title = "Swedish Crop Fields (Jordbruksskiften)" + description = """ +A crop field (Jordbruksskift) is a contiguous area of land within a block where a farmer grows a crop or otherwise manages the land. +To receive compensation for agricultural support (EU support), farmers apply for support from the +Swedish Agency for Agriculture via a SAM application. The data set contains parcels where the area +applied for and the area decided on are the same. The data is published at the end of a year. + + Codes found at https://jordbruksverket.se/stod/jordbruk-tradgard-och-rennaring/sam-ansokan-och-allmant-om-jordbrukarstoden/grodkoder + """ + provider = "Jordbruksverket (The Swedish Board of Agriculture) " + attribution = "Jordbruksverket" + license = "CC0-1.0" # "Open Data" + columns = { + "geometry": "geometry", + "id": "id", + "faststalld": "metrics:area", + "grdkod_mar": "crop:code", + "arslager": "determination:datetime", + } + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + ec_mapping_csv = "https://fiboa.org/code/se/se.csv" + column_migrations = { + # Make year (1st January) from column "arslager" + "arslager": lambda col: pd.to_datetime(col, format="%Y") + } + + def migrate(self, gdf): + gdf["id"] = gdf["blockid"] + "_" + gdf["skiftesbet"] + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/si.py b/build/lib/fiboa_cli/datasets/si.py new file mode 100644 index 00000000..ea1784c1 --- /dev/null +++ b/build/lib/fiboa_cli/datasets/si.py @@ -0,0 +1,42 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + str(year): { + f"https://rkg.gov.si/razno/portal_analysis/KMRS_{year}.rar": [f"KMRS_{year}.shp"] + } + for year in range(2024, 2020, -1) + } + id = "si" + short_name = "Slovenia" + title = "Slovenia Crop Fields" + description = """ +The Slovenian government provides slightly different, relevant open data sets called GERK, KMRS, RABA and EKRZ. +This converter uses the KRMS dataset, which includes CAP applications of the last year and discerns +around 150 different crop categories. + """ + provider = "Ministry of Agriculture, Forestry and Food (Ministrstvo za kmetijstvo, gozdarstvo in prehrano) " + + license = "Javno dostopni podatki: Publicly available data " + + columns = { + "geometry": "geometry", + "ID": "id", + "GERK_PID": "block_id", + "AREA": "metrics:area", + "SIFRA_KMRS": "crop:code", + "RASTLINA": "crop:name", + "CROP_LAT_E": "crop:name_en", + } + ec_mapping_csv = "https://fiboa.org/code/si/si.csv" + column_migrations = {"geometry": lambda col: col.make_valid()} + area_is_in_ha = False + missing_schemas = { + "properties": { + "block_id": {"type": "uint64"}, + } + } diff --git a/build/lib/fiboa_cli/datasets/sk.py b/build/lib/fiboa_cli/datasets/sk.py new file mode 100644 index 00000000..6fa84b3e --- /dev/null +++ b/build/lib/fiboa_cli/datasets/sk.py @@ -0,0 +1,54 @@ +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.hcat import AddHCATMixin, load_ec_mapping + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + sources = { + "https://data.slovensko.sk/download?id=e39ad227-1899-4cff-b7c8-734f90aa0b59&blocksize=0": [ + "HU2024_20240917shp/HU2024_20240917.shp" + ] + } + # https://data.slovensko.sk/download?id=626c1181-bc53-40b6-9715-3c10164760ec + id = "sk" + short_name = "Slovakia" + title = "Slovakia Agricultural Land Identification System" + description = """ +Systém identifikácie poľnohospodárskych pozemkov (LPIS) + +LPIS is an agricultural land identification system. It represents the vector boundaries of agricultural land +and carries information about the unique code, acreage, culture/land use, etc., which is used as a reference +for farmers' applications, for administrative and cross-checks, on-site checks and also checks using remote +sensing methods. + +Dataset Hranice užívania contains the use declared by applicants for direct support. + """ + provider = "Pôdohospodárska platobná agentúra " + license = "CC0-1.0" # "Open Data" + ec_mapping_csv = "https://fiboa.org/code/sk/sk.csv" + # KODKD is the LPIS block code, shared by several fields and sometimes empty; + # the row index is the field id and the code is kept as block_id. + index_as_id = True + columns = { + "geometry": "geometry", + "KODKD": "block_id", + "PLODINA": "crop:name", + "KULTURA_NA": "crop_group", + "LOKALITA_N": "municipality", + "VYMERA": "metrics:area", + } + missing_schemas = { + "properties": { + "block_id": {"type": "string"}, + "crop_group": {"type": "string"}, + "municipality": {"type": "string"}, + } + } + + def migrate(self, gdf): + if self.ec_mapping is None: + self.ec_mapping = load_ec_mapping(self.ec_mapping_csv, url=self.mapping_file) + mapping = {row["original_name"]: index + 1 for index, row in enumerate(self.ec_mapping)} + gdf["crop:code"] = gdf["PLODINA"].map(mapping) + return super().migrate(gdf) diff --git a/build/lib/fiboa_cli/datasets/template.py b/build/lib/fiboa_cli/datasets/template.py new file mode 100644 index 00000000..dd3cd08d --- /dev/null +++ b/build/lib/fiboa_cli/datasets/template.py @@ -0,0 +1,123 @@ +# TEMPLATE FOR A FIBOA CONVERTER +# +# Copy this file and rename it to something sensible. +# The name of the file will be the name of the converter in the cli. +# If you name it 'de_abc' you'll be able to run `fiboa convert de_abc` in the cli. + +from ..conversion.fiboa_converter import FiboaBaseConverter + +# You can remove attributes that you don't need. +# Also, please remove all comments that you didn't add yourself from the template. + + +class Converter(FiboaBaseConverter): + # File(s) to read the data from, usually publicly accessible URLs. + # Can read any (zipped) tabular data format that GeoPandas can read through read_file() or read_parquet(). + # Supported protocols: HTTP(S), GCS, S3, or the local file system + + # Multiple options are possible: + # 1. a single URL (filename must be in the URL). The file is read as is. + sources = "https://fiboa.example/data.shp.zip" + + # 2. a dictionary with a mapping of URLs (where the filename can't necessarily be determined from the URL) to filenames. + # sources = { + # "https://fiboa.example/archive/758?download=1": "us.gpkg" + # "https://fiboa.example/archive/355?download=1": "canada.gpkg" + # } + # 3. a dictionary with a mapping of URLs to a list of filenames in ZIP ot 7Z files to read from. + # sources = { + # "https://fiboa.example/north_america.zip": ["us.gpkg", "canaga.gpkg"] + # } + + # 4. if multiple years are available, you can replace sources by years. + # The dict-key can be used on the cli command line, the value will be used as 'sources' + # + # variants = { + # "2023": "https://fiboa.example/file_2023.xyz" + # "2024": "https://fiboa.example/file_2024.xyz" + # } + + # Override filter function for the layer in the file(s) to read. + # def layer_filter(self, layer: str, uri: str) -> bool: + # return True + + # Unique identifier for the collection + id = "abc" + # Geonames for the data (e.g. Country, Region, Source, Year) + short_name = "Country, Region, etc." + # Title of the collection + title = "Field boundaries for Country, Region, etc." + # Description of the collection. + description = """ +Describe the dataset here. + +Can be formatted with [CommonMark](https://commonmark.org/) (a Markdown variant). +The description can be multiple lines long, +but ensure it is _not_ indented within the triple quotes. + """ + + # The provider of the data. + # A string that contains the provider name and optionally a URL. + # Either "Name" or "Name ". + provider = "ABC Corp " + + # Attribution (e.g. copyright or citation statement as requested by provider) as a string. + # The attribution is usually shown on the map, in the lower right corner. + # Can be None if not applicable + attribution = "© 2024 ABC Corp." + + # License of the data, either + # 1. a SPDX license identifier (including "DL-DE-BY-2.0" / "DL-DE-ZERO-2.0"), or + # 2. a string with license name and URL, e.g. "My License " + license = "CC-BY-4.0" + + # Map original column names to fiboa property names + # You also need to list any column that you may have added in the MIGRATION function (see below). + # GeoJSON: Nested objects can be accessed using a dot, e.g. "area.value" for {"area": {"value": 123}} + columns = { + "some_are_col": "metrics:area", + "geom": "geometry", + } + + # Add columns with constant values. + # The key is the column name, the value is a constant value that's used for all rows. + column_additions = {} + + # A set of implemented extension identifiers + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + + # Functions to migrate data in columns to match the fiboa specification. + # Example: You have a column area_m in square meters and want to convert + # to hectares as required for the area field in fiboa. + # requires: func(column: pd.Series) -> pd.Series + column_migrations = {"area_m": lambda column: column * 10_000} + + # Filter columns to only include the ones that are relevant for the collection, + # e.g. only rows that contain the word "agriculture" but not "forest" in the column "land_cover_type". + # Lamda function accepts a Pandas Series and returns a Series or a Tuple with a Series and True to inverse the mask. + column_filters = {"land_cover_type": lambda col: (col.isin(["agrictulture"]), True)} + + # Override to migrate the full GeoDataFrame if the other options are not sufficient + # This should be the last resort! + # def migrate(self, gdf) -> gpd.GeoDataFrame: + # gdf["column"] *= 10 + # return gdf + + # Custom function to execute actions on the the GeoDataFrame that are loaded from individual file or layers. + # This is useful if the data is split into multiple files/layers and columns should be added or changed + # on a per-file/layer basis for example. + # The path contains the local path to the file that was read. + # The uri contains the URL that was read. + # The layer may contain the layer name. + # def file_migration(self, gdf: gpd.GeoDataFrame, path: str, uri: str, layer: str = None) -> gpd.GeoDataFrame: + # return data + + # Schemas for the fields that are not defined in the core or the used extensions + # Keys must be the values from the COLUMNS dict, not the keys + missing_schemas = { + "required": ["my_id"], # i.e. non-nullable properties + "properties": { + "some_col": {"type": "string"}, + "some_category": {"type": "string", "enum": ["A", "B"]}, + }, + } diff --git a/build/lib/fiboa_cli/datasets/us_ca_scm.py b/build/lib/fiboa_cli/datasets/us_ca_scm.py new file mode 100644 index 00000000..9a191ada --- /dev/null +++ b/build/lib/fiboa_cli/datasets/us_ca_scm.py @@ -0,0 +1,62 @@ +from os.path import dirname, join + +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import load_ec_mapping +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + variants = { + "2023": { + "https://data.cnra.ca.gov/dataset/6c3d65e3-35bb-49e1-a51e-49d5a2cf09a9/resource/4e17ca38-268e-4bf5-bbc5-09636d44ed60/download/i15_crop_mapping_2023_provisional_20241127.gdb.zip": [ + "i15_Crop_Mapping_2023_Provisional_20241127.gdb" + ] + }, + "2022": { + "https://data.cnra.ca.gov/dataset/6c3d65e3-35bb-49e1-a51e-49d5a2cf09a9/resource/f38d3f6f-dcf1-4553-9f07-4f381d494320/download/i15_crop_mapping_2022_provisional_gdb.zip": [ + "i15_Crop_Mapping_2022_Provisional_GDB/i15_Crop_Mapping_2022_Provisional.gdb" + ] + }, + } + id = "us_ca_scm" + admin_subdivision_code = "CA" + short_name = "US, California (SCM)" + title = "California (US) Statewide Crop Mapping" + description = """ +For many years, the California Department of Water Resources (DWR) has collected land use data throughout the state +and used this information to develop water use estimates for statewide and regional planning efforts, including water +use projections, water use efficiency evaluation, groundwater model development, and water transfers. These data are +essential for regional analysis and decision making, which has become increasingly important as DWR and other state agencies +seek to address resource management issues, regulatory compliance issues, environmental impacts, ecosystem services, +urban and economic development, and other issues. + """ + provider = "County of Santa Clara " + license = "CC0-1.0" + columns = { + "geometry": "geometry", + "UniqueID": "id", + "MAIN_CROP": "crop:code", + "crop:name": "crop:name", + "COUNTY": "admin_level_2", + } + column_additions = { + "determination:datetime": "2023-05-01T00:00:00Z", + } + ec_mapping_csv = "https://fiboa.org/code/us/ca/scm.csv" + missing_schemas = { + "properties": { + "admin_level_2": {"type": "string"}, + } + } + + def migrate(self, gdf): + """ + Perform migration on the GeoDataFrame to map crop names using a provided mapping file. + """ + gdf = super().migrate(gdf) + mapping = load_ec_mapping(url=join(dirname(__file__), "data-files", "us_ca_scm.csv")) + original_name_mapping = {e["original_code"]: e["original_name"] for e in mapping} + gdf["crop:name"] = gdf["MAIN_CROP"].map(original_name_mapping) + return gdf diff --git a/build/lib/fiboa_cli/datasets/us_usda_cropland.py b/build/lib/fiboa_cli/datasets/us_usda_cropland.py new file mode 100644 index 00000000..58a1e51e --- /dev/null +++ b/build/lib/fiboa_cli/datasets/us_usda_cropland.py @@ -0,0 +1,78 @@ +import pandas as pd +from loguru import logger +from vecorel_cli.conversion.admin import AdminConverterMixin + +from ..conversion.fiboa_converter import FiboaBaseConverter +from .commons.ec import load_ec_mapping +from .commons.hcat import AddHCATMixin + + +class Converter(AdminConverterMixin, AddHCATMixin, FiboaBaseConverter): + # One archive carries the whole sequence: CSB1724.gdb has a CDL crop + # column for every year 2017-2024, so every variant reads the same source. + variants = { + str(y): { + "https://www.nass.usda.gov/Research_and_Science/Crop-Sequence-Boundaries/datasets/NationalCSB_2017-2024_rev23.zip": [ + "NationalCSB_2017-2024_rev23/CSB1724.gdb" + ] + } + for y in range(2024, 2016, -1) + } + id = "us_usda_cropland" + short_name = "US (USDA CSB)" + title = "U.S. Department of Agriculture Crop Sequence Boundaries" + description = """ +The Crop Sequence Boundaries (CSB) developed with USDA's Economic Research Service, produces estimates of field boundaries, crop acreage, and crop rotations across the contiguous United States. It uses satellite imagery with other public data and is open source allowing users to conduct area and statistical analysis of planted U.S. commodities and provides insight on farmer cropping decisions. + +NASS needed a representative field to predict crop planting based on common crop rotations such as corn-soy and ERS is using this product to study changes in farm management practices like tillage or cover cropping over time. + +CSB represents non-confidential single crop field boundaries over a set time frame. It does not contain personal identifying information. The boundaries captured are of crops grown only, not ownership boundaries or tax parcels (unit of property). The data are from satellite imagery and publicly available data, it does not come from producers or agencies like the Farm Service Agency. + """ + extensions = {"https://fiboa.org/crop-extension/v0.2.0/schema.yaml"} + provider = "United States Department of Agriculture " + license = "License and Liability " + columns = { + "geometry": "geometry", + "CSBID": "id", + # "CDL2023": "crop:code", will be added in migrate + "crop:name": "crop:name", + "CNTY": "administrative_area_level_2", + } + use_variant_as_determination = True + missing_schemas = { + "properties": { + "administrative_area_level_2": {"type": "string"}, + } + } + ec_mapping_csv = "https://fiboa.org/code/us/usda/cropland.csv" + + def migrate(self, gdf): + """ + Perform migration on the GeoDataFrame by dissolving polygons by crop code + and mapping crop names. + + "dissolve": merge adjacent polygons with the same crop + geodataframe.Dissolve(method="unary") is **slow** for large datasets + So we're handling this huge dataset in blocks, states are a natural grouping-method + """ + assert self.variant, "Variant must be set" + crop_key = f"CDL{self.variant}" + self.columns[crop_key] = "crop:code" + + gdf = super().migrate(gdf) + states = list(gdf["STATEFIPS"].unique()) + gdfs = [] + for state in states: + logger.info(f"Handling State {state}") + df = gdf[gdf["STATEFIPS"] == state].explode() + df = df.dissolve(by=[crop_key], aggfunc="first", as_index=False).explode() + gdfs.append(df) + gdf = pd.concat(gdfs) + del gdfs + if self.ec_mapping is None: + self.ec_mapping = load_ec_mapping(self.ec_mapping_csv, url=self.mapping_file) + original_name_mapping = { + int(e["original_code"]): e["original_name"] for e in self.ec_mapping + } + gdf["crop:name"] = gdf[crop_key].map(original_name_mapping) + return gdf diff --git a/build/lib/fiboa_cli/datasets/varda.py b/build/lib/fiboa_cli/datasets/varda.py new file mode 100644 index 00000000..b6971b4b --- /dev/null +++ b/build/lib/fiboa_cli/datasets/varda.py @@ -0,0 +1,34 @@ +# Converter for Varda field boundary datasets to Fiboa. +# Aiming to have it work with both direct API access and with bulk download. + +from ..conversion.fiboa_converter import FiboaBaseConverter + + +class Converter(FiboaBaseConverter): + area_is_in_ha = False + data_access = """ + Data must be obtained from the Varda API, saved as .json files. Easiest way to try it out is + to use the UI at https://fieldid.varda.ag/ and find some fields and click 'download .json' file, + or else call the /boundaries endpoint - details at https://developer.varda.ag/reference/get_boundaries_by_spatial_field_relationship_search-1. + Use the `-i` option to provide the file(s) to the converter. + """ + + id = "varda" + short_name = "Varda" + title = "Varda Global FieldID" + description = "Field Boundaries from the Global FieldID system from Varda." + + provider = "Varda " + attribution = "© 2024 Varda" + license = "Varda Terms of use " + + columns = { + "geometry": "geometry", + "id": "id", + "area": "metrics:area", + "perimeter": "metrics:perimeter", + # todo: add more columns? + # "effective_from": "datetime:valid_from", + # "effective_until": "datetime:valid_until", + # 0000-01-01T00:00:00.000Z and 9999-12-31T00:00:00.000Z should be converted to None + } diff --git a/build/lib/fiboa_cli/describe.py b/build/lib/fiboa_cli/describe.py new file mode 100644 index 00000000..6532852b --- /dev/null +++ b/build/lib/fiboa_cli/describe.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import Union + +from vecorel_cli.describe import DescribeFile as Base +from vecorel_cli.vecorel.schemas import CollectionSchemas +from yarl import URL + +from .fiboa.version import get_versions + + +class DescribeFile(Base): + @staticmethod + def get_cli_callback(cmd): + def callback(source, num, properties, verbose): + return DescribeFile(source).run(num=num, properties=properties, verbose=verbose) + + return callback + + def __init__(self, filepath: Union[Path, URL, str]): + super().__init__(filepath) + + def _schema_to_dict(self, schema: CollectionSchemas): + vecorel_version, _, fiboa_version, _, extensions = get_versions(schema) + + obj = { + "Vecorel Version": vecorel_version, + "Fiboa Version": fiboa_version, + "Extensions": extensions if len(extensions) > 0 else None, + } + return obj diff --git a/build/lib/fiboa_cli/fiboa/version.py b/build/lib/fiboa_cli/fiboa/version.py new file mode 100644 index 00000000..5742b2a5 --- /dev/null +++ b/build/lib/fiboa_cli/fiboa/version.py @@ -0,0 +1,28 @@ +from vecorel_cli.vecorel.schemas import CollectionSchemas +from vecorel_cli.vecorel.version import check_versions + +supported_fiboa_versions = ">=0.3.0,<0.4.0" +fiboa_version = "0.3.0" +spec_pattern = r"https://fiboa.org/specification/v([^/]+)/schema.yaml" +spec_schema = "https://fiboa.org/specification/v{version}/schema.yaml" + + +def get_fiboa_uri() -> str: + return spec_schema.format(version=fiboa_version) + + +def is_supported(version, raise_exception=False) -> bool: + result = check_versions(version, supported_fiboa_versions) + if not result and raise_exception: + raise ValueError( + f"Fiboa version {version} is not supported, supported are {supported_fiboa_versions}" + ) + return result + + +def get_versions(schema: CollectionSchemas) -> tuple[str, str, str, str, set[str]]: + vecorel_version, vecorel_uri, vecorel_extensions = schema.get() + fiboa_version, fiboa_uri, extensions = CollectionSchemas.parse_schemas( + vecorel_extensions, spec_pattern + ) + return vecorel_version, vecorel_uri, fiboa_version, fiboa_uri, extensions diff --git a/build/lib/fiboa_cli/improve.py b/build/lib/fiboa_cli/improve.py new file mode 100644 index 00000000..ab7080ff --- /dev/null +++ b/build/lib/fiboa_cli/improve.py @@ -0,0 +1,161 @@ +import json + +import click +import spdx_license_list +from geopandas import GeoDataFrame +from vecorel_cli.basecommand import runnable +from vecorel_cli.encoding.auto import create_encoding +from vecorel_cli.improve import ImproveData as Base +from vecorel_cli.vecorel.collection import Collection +from vecorel_cli.vecorel.extensions import ADMIN_DIVISION +from vecorel_cli.vecorel.version import sdl_uri + +from fiboa_cli.conversion.fiboa_converter import FiboaBaseConverter +from fiboa_cli.datasets.commons.ec import AddHCATMixin +from fiboa_cli.datasets.commons.hcat import CROP_EXTENSION, HCAT_EXTENSION +from fiboa_cli.registry import Registry + + +class ImproveData(Base): + @staticmethod + def get_cli_args(): + return { + **Base.get_cli_args(), + "add-hcat": click.option( + "--add-hcat", + "-hcat", + type=str, + help="Adds hcat-extension and columns to the collection, based on the crop:code column and a mapping file. Requires a mapping file as argument, e.g. 'at_2021.csv', find them at https://github.com/maja601/EuroCrops/tree/main/csvs/country_mappings", + ), + } + + @runnable + def improve_file( + self, source, target=None, compression=None, geoparquet_version=None, indent=None, **kwargs + ): + # Override method to be able to convert input from fiboa-0.2.0 to fiboa-0.3.0 + if not target: + target = source + + input_encoding = create_encoding(source) + geodata = input_encoding.read() + collection = input_encoding.get_collection() + + if not collection: + # Try to migrate from fiboa-0.2.0 to fiboa-0.3.0 + metadata = input_encoding.get_metadata() + if b"fiboa" in metadata: + fiboa_2 = json.loads(metadata[b"fiboa"].decode("utf-8")) + geodata, collection = self.migrate_fiboa_2(geodata, fiboa_2, source.name) + + geodata, collection = self.improve(geodata, collection=collection, **kwargs) + + output_encoding = create_encoding(target) + output_encoding.set_collection(collection) + output_encoding.write( + geodata, + compression=compression, + geoparquet_version=geoparquet_version, + indent=indent, + ) + return target + + def improve( + self, gdf: GeoDataFrame, collection: Collection, add_hcat: str = None, **kwargs + ) -> tuple[GeoDataFrame, Collection]: + gdf, collection = super().improve(gdf, collection, **kwargs) + # Add HCAT + if add_hcat: + gdf, collection = self.add_hcat(gdf, collection, add_hcat) + self.info("Added HCAT columns and extension") + return gdf, collection + + def add_hcat(self, gdf, collection, mapping_file): + if "crop:code" not in gdf.columns: + raise Exception("Missing crop:code column in dataset") + + is_url = "/" in mapping_file + _mapping_file = mapping_file + + # Simplest way to reuse functionality from AddHCATMixin + class HCAT(AddHCATMixin, FiboaBaseConverter): + columns = {"crop:code": "crop:code"} + ec_mapping_csv = None if is_url else _mapping_file + mapping_file = _mapping_file if is_url else None + + for schemas in collection["schemas"].values(): + if HCAT_EXTENSION not in schemas: + schemas.append(HCAT_EXTENSION) + + return HCAT().add_hcat(gdf), collection + + def migrate_fiboa_2( + self, geodata, original: Collection, file_name: str + ) -> tuple[GeoDataFrame, Collection]: + if original["fiboa_version"] != "0.2.0": + self.warning( + f"Not migrating from fiboa version {original['fiboa_version']}, can only migrate fiboa from 0.2.0" + ) + return geodata, original + + self.info(f"Migrating data from fiboa version {original['fiboa_version']}") + schemas = set() + for e in original.get("fiboa_extensions", []): + if e in EXTENSION_MAPPING: + schemas.add(EXTENSION_MAPPING[e]) + + base = {k: original[k] for k in ("title", "description", "attribution") if k in original} + collection_id = original.get("id") or file_name.split(".")[0] + + collection = Collection(Registry.get_default_collection(collection_id, extensions=schemas)) + collection.update(base) + + # Migrate custom schemas + if "fiboa_custom_schemas" in original: + collection["schemas:custom"] = { + "$schema": sdl_uri, + "required": [], + "collection": {}, + } | original["fiboa_custom_schemas"] + + # Take first provider json, make string out of it + if "providers" in original: + provider = next(iter(original["providers"])) + collection["provider"] = f"{provider['name']} <{provider['url']}>" + + # Transform license links to string + if original.get("license"): + if original["license"] in spdx_license_list.LICENSES or "<" in original["license"]: + collection["license"] = original["license"] + if "license" not in collection: + _licenses = [link for link in original.get("links", []) if link.get("rel") == "license"] + if _licenses: + collection["license"] = f"{_licenses[0]['title']} <{_licenses[0]['href']}>" + + # Rename columns + rename = { + k: v + for k, v in ( + ("determination_datetime", "determination:datetime"), + ("determination_method", "determination:method"), + ("perimeter", "metrics:perimeter"), + ) + if k in original + } + + # Transform area from ha to m2 + if "area" in geodata.columns: + geodata["area"] *= 10000 + rename["area"] = "metrics:area" + + geodata.rename(rename, axis=1, inplace=True) + return geodata, collection + + +EXTENSION_MAPPING = { + "https://fiboa.github.io/hcat-extension/v0.2.0/schema.yaml": HCAT_EXTENSION, + "https://fiboa.github.io/crop-extension/v0.1.0/schema.yaml": CROP_EXTENSION, + "https://fiboa.github.io/inspire-extension/v0.1.0/schema.yaml": "https://fiboa.org/inspire-extension/v0.3.0/schema.yaml", + "https://fiboa.github.io/schema/v0.1.0/schema.json": sdl_uri, + "https://fiboa.github.io/administrative-division-extension/v0.1.0/schema.yaml": ADMIN_DIVISION, +} diff --git a/build/lib/fiboa_cli/merge.py b/build/lib/fiboa_cli/merge.py new file mode 100644 index 00000000..41cace5f --- /dev/null +++ b/build/lib/fiboa_cli/merge.py @@ -0,0 +1,5 @@ +from vecorel_cli.merge import MergeDatasets as Base + + +class MergeDatasets(Base): + pass diff --git a/build/lib/fiboa_cli/publish.py b/build/lib/fiboa_cli/publish.py new file mode 100644 index 00000000..9dced76e --- /dev/null +++ b/build/lib/fiboa_cli/publish.py @@ -0,0 +1,289 @@ +import hashlib +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + +import click +from vecorel_cli.basecommand import BaseCommand, runnable +from vecorel_cli.cli.options import VECOREL_TARGET + +from .convert import ConvertData +from .converters import Converters +from .create_stac import CreateStacCollection +from .registry import Registry +from .validate import ValidateData + +FILE_EXTENSION = "https://stac-extensions.github.io/file/v2.1.0/schema.json" +WEB_MAP_LINKS_EXTENSION = "https://stac-extensions.github.io/web-map-links/v1.3.0/schema.json" +PMTILES_MEDIA_TYPE = "application/vnd.pmtiles" +TIPPECANOE_DEFAULT_OPTS = "-zg --drop-densest-as-needed --extend-zooms-if-still-dropping" + +is_windows = os.name == "nt" + + +def multihash_sha256(path: Path, chunk_size: int = 1024 * 1024) -> str: + """ + sha2-256 multihash of a file, hex encoded: 0x12 (sha2-256), 0x20 (32 bytes), digest. + This is the encoding the STAC file extension expects for ``file:checksum``. + """ + digest = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(chunk_size), b""): + digest.update(chunk) + return "1220" + digest.hexdigest() + + +class Publish(BaseCommand): + cmd_name = "publish" + cmd_help = ( + f"Convert a {Registry.project} dataset and prepare it for publication: " + "GeoParquet, PMTiles and a STAC Collection with relative links." + ) + + @staticmethod + def get_cli_args(): + return { + **ConvertData.get_cli_args(), + "target": VECOREL_TARGET(folder=True), + "pmtiles": click.option( + "--pmtiles/--no-pmtiles", + is_flag=True, + help="Generate PMTiles with ogr2ogr and tippecanoe.", + default=True, + show_default=True, + ), + "tippecanoe_opts": click.option( + "--tippecanoe-opts", + type=click.STRING, + help="Additional options passed to tippecanoe.", + default=TIPPECANOE_DEFAULT_OPTS, + show_default=True, + ), + } + + @staticmethod + def get_cli_callback(cmd): + def callback(dataset, *args, **kwargs): + return Publish(dataset).run(*args, **kwargs) + + return callback + + def __init__(self, dataset: str): + super().__init__() + self.cmd_title = f"Publish {dataset}" + self.dataset = dataset + + try: + self.converter = Converters().load(self.dataset) + except (ImportError, NameError, OSError, RuntimeError, SyntaxError) as e: + raise Exception(f"Converter for '{self.dataset}' not available or faulty: {e}") from e + + def check_command(self, cmd, name=None): + if shutil.which(cmd) is None: + self.error(f"Missing command {cmd}. Please install {name or cmd}") + sys.exit(1) + + @runnable + def publish( + self, + target, + pmtiles=True, + tippecanoe_opts=TIPPECANOE_DEFAULT_OPTS, + **kwargs, + ): + """ + Creates the following files in the target folder: + + - [-].parquet: the converted and validated fiboa GeoParquet file + - [-].pmtiles: vector tiles for visualization (ogr2ogr + tippecanoe) + - collection.json: a STAC Collection with relative links to the files above + + Existing files are reused, delete them to regenerate. + PMTiles generation needs GDAL 3.8 or later (for ogr2ogr) and tippecanoe: + - https://gdal.org/ + - https://github.com/felt/tippecanoe + """ + target = Path(target) + target.mkdir(parents=True, exist_ok=True) + + file_name = self.dataset + if not kwargs.get("variant") and self.converter.variants: + kwargs["variant"] = next(iter(self.converter.variants)) + if kwargs.get("variant"): + file_name += f"-{kwargs['variant']}" + parquet_file = target / f"{file_name}.parquet" + pmtiles_file = target / f"{file_name}.pmtiles" + stac_file = target / "collection.json" + + # Create parquet file + if not parquet_file.exists(): + self.info(f"Converting {self.dataset} to {parquet_file}") + ConvertData(self.dataset).run(parquet_file, **kwargs) + self.success(f"Converted {self.dataset} to {parquet_file}") + else: + self.success(f"Using existing file {parquet_file}") + + self.ensure_spatial_order(parquet_file) + + # Validate parquet file, we only want to publish valid files + self.info(f"Validating {parquet_file}") + ValidateData().validate(parquet_file, num=-1) + self.log("\n => VALID\n", "success") + + # Create PMTiles + if pmtiles: + self.generate_pmtiles(parquet_file, pmtiles_file, tippecanoe_opts) + has_pmtiles = pmtiles_file.exists() + + # Create STAC collection.json + self.create_stac_collection(parquet_file, pmtiles_file if has_pmtiles else None, stac_file) + self.success(f"Created {stac_file}") + return stac_file + + def create_stac_collection(self, parquet_file: Path, pmtiles_file, stac_file: Path): + is_current = ( + stac_file.exists() + and stac_file.stat().st_mtime >= parquet_file.stat().st_mtime + and (pmtiles_file is None or stac_file.stat().st_mtime >= pmtiles_file.stat().st_mtime) + ) + if is_current: + self.info(f"Reusing existing {stac_file}") + return + + self.info(f"Creating STAC collection for {parquet_file}") + data = CreateStacCollection().create_from_file( + parquet_file, data_url=f"./{parquet_file.name}" + ) + if data["id"] != self.dataset: + raise Exception( + f"Wrong collection id: {data['id']} != {self.dataset}, for {parquet_file}" + ) + + extensions = data.setdefault("stac_extensions", []) + if FILE_EXTENSION not in extensions: + extensions.append(FILE_EXTENSION) + + asset = data["assets"]["data"] + asset["title"] = f"{data.get('title') or self.dataset} (GeoParquet)" + asset.update(self.file_metadata(parquet_file)) + + if pmtiles_file is not None: + if WEB_MAP_LINKS_EXTENSION not in extensions: + extensions.append(WEB_MAP_LINKS_EXTENSION) + data["links"] = [link for link in data.get("links", []) if link.get("rel") != "pmtiles"] + data["links"].append( + { + "rel": "pmtiles", + "href": f"./{pmtiles_file.name}", + "type": PMTILES_MEDIA_TYPE, + "title": "Web map tiles", + "pmtiles:layers": [self.dataset], + } + ) + data["assets"]["visual"] = { + "href": f"./{pmtiles_file.name}", + "type": PMTILES_MEDIA_TYPE, + "title": f"{data.get('title') or self.dataset} (PMTiles)", + "roles": ["visual"], + **self.file_metadata(pmtiles_file), + } + + with stac_file.open("w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + @staticmethod + def file_metadata(path: Path) -> dict: + return { + "file:size": path.stat().st_size, + "file:checksum": multihash_sha256(path), + } + + # ~50k rows per group: inside gpio's spatial-query sweet spot, and with a + # Hilbert order every small group is finer bbox-skipping granularity + ROW_GROUP_SIZE = 50_000 + + def ensure_spatial_order(self, parquet_file: Path): + """Hilbert-sort the file in place unless it already is sorted. + + Every write path ends up spatially ordered regardless of converter + (plain pandas, per-file merge, DuckDB), and re-running publish over an + existing parquet doubles as the repair tool for published data.""" + import json as _json + + import pyarrow.parquet as _pq + + from .conversion.per_file import _ensure_hilbert_sorted + + try: + from vecorel_cli.vecorel.hilbert import crs_total_bounds + except ImportError: + from .conversion.hilbert import crs_total_bounds + + with _pq.ParquetFile(parquet_file) as pf: + meta = pf.schema_arrow.metadata or {} + if b"geo" not in meta: + self.warning(f"{parquet_file} has no geo metadata; skipping spatial ordering") + return + geo = _json.loads(meta[b"geo"]) + primary = geo["primary_column"] + crs = geo["columns"][primary].get("crs") or "EPSG:4326" + if _ensure_hilbert_sorted( + str(parquet_file), + primary, + crs_total_bounds(crs), + "zstd", + None, + row_group_size=self.ROW_GROUP_SIZE, + ): + self.success(f"Re-sorted {parquet_file} into Hilbert order") + + def generate_pmtiles(self, parquet_file: Path, pmtiles_file: Path, tippecanoe_opts: str): + if is_windows: + self.warning( + "PMTiles generation through tippecanoe is not supported on Windows, skipping." + ) + return + if pmtiles_file.exists(): + self.success(f"Using existing file {pmtiles_file}") + return + + self.check_command("tippecanoe") + self.check_command("ogr2ogr", name="GDAL") + self.info("Running ogr2ogr | tippecanoe") + ogr = subprocess.Popen( + [ + "ogr2ogr", + "-t_srs", + "EPSG:4326", + "-f", + "GeoJSONSeq", + "/vsistdout/", + str(parquet_file), + ], + stdout=subprocess.PIPE, + ) + # tippecanoe ignores $TMPDIR and spills into /tmp, which is often a small partition + tmpdir = os.environ.get("TMPDIR") + tmp_opts = ["-t", tmpdir] if tmpdir else [] + tippecanoe = subprocess.run( + [ + "tippecanoe", + *tmp_opts, + *tippecanoe_opts.split(), + "--projection=EPSG:4326", + "-o", + str(pmtiles_file), + "-l", + self.dataset, + ], + stdin=ogr.stdout, + ) + ogr.stdout.close() + ogr.wait() + if ogr.returncode != 0 or tippecanoe.returncode != 0: + pmtiles_file.unlink(missing_ok=True) + raise Exception("PMTiles generation failed, see output above.") + self.success(f"Created {pmtiles_file}") diff --git a/build/lib/fiboa_cli/registry.py b/build/lib/fiboa_cli/registry.py new file mode 100644 index 00000000..76562bc8 --- /dev/null +++ b/build/lib/fiboa_cli/registry.py @@ -0,0 +1,66 @@ +import re + +from vecorel_cli.registry import Registry, VecorelRegistry + +from fiboa_cli.fiboa.version import get_fiboa_uri, spec_pattern + + +class FiboaRegistry(VecorelRegistry): + name: str = "fiboa-cli" + project: str = "fiboa" + cli_title: str = "fiboa CLI" + src_package: str = "fiboa_cli" + core_properties = [ + "id", + "geometry", + "collection", + "metrics:area", + "metrics:perimeter", + "category", + "determination:datetime", + "determination:method", + "determination:details", + ] + required_extensions = [re.compile(spec_pattern)] + ignored_datasets = VecorelRegistry.ignored_datasets + ["es_base.py"] + + def register_commands(self): + from .convert import ConvertData + from .converters import Converters + from .create_geojson import CreateGeoJson + from .create_geoparquet import CreateGeoParquet + from .create_jsonschema import CreateJsonSchema + from .create_stac import CreateStacCollection + from .describe import DescribeFile + from .improve import ImproveData + from .merge import MergeDatasets + from .publish import Publish + from .rename_extension import RenameExtension + from .validate import ValidateData + from .validate_schema import ValidateSchema + + commands = [ + ConvertData, + Converters, + CreateGeoJson, + CreateGeoParquet, + CreateJsonSchema, + CreateStacCollection, + DescribeFile, + ImproveData, + MergeDatasets, + Publish, + RenameExtension, + ValidateData, + ValidateSchema, + ] + + for command in commands: + self.set_command(command) + + def get_default_collection(self, id: str, extensions: set | list | None = None) -> dict: + extensions = {get_fiboa_uri()} | set(extensions) + return super().get_default_collection(id, extensions=extensions) + + +Registry.instance = FiboaRegistry() diff --git a/build/lib/fiboa_cli/rename_extension.py b/build/lib/fiboa_cli/rename_extension.py new file mode 100644 index 00000000..72737c11 --- /dev/null +++ b/build/lib/fiboa_cli/rename_extension.py @@ -0,0 +1,13 @@ +from vecorel_cli.rename_extension import RenameExtension as Base + + +class RenameExtension(Base): + template_org: str = "fiboa" + template_domain: str = "fiboa.org" + + @staticmethod + def get_cli_callback(cmd): + def callback(folder, title, slug, org, prefix): + return RenameExtension(title, slug, org, prefix).run(folder=folder) + + return callback diff --git a/build/lib/fiboa_cli/validate.py b/build/lib/fiboa_cli/validate.py new file mode 100644 index 00000000..3a66468b --- /dev/null +++ b/build/lib/fiboa_cli/validate.py @@ -0,0 +1,5 @@ +from vecorel_cli.validate import ValidateData as Base + + +class ValidateData(Base): + pass diff --git a/build/lib/fiboa_cli/validate_schema.py b/build/lib/fiboa_cli/validate_schema.py new file mode 100644 index 00000000..72b3b458 --- /dev/null +++ b/build/lib/fiboa_cli/validate_schema.py @@ -0,0 +1,5 @@ +from vecorel_cli.validate_schema import ValidateSchema as Base + + +class ValidateSchema(Base): + pass diff --git a/coverage.json b/coverage.json new file mode 100644 index 00000000..6a99ff1d --- /dev/null +++ b/coverage.json @@ -0,0 +1 @@ +{"meta": {"format": 3, "version": "7.13.4", "timestamp": "2026-08-28T10:21:33.203605", "branch_coverage": true, "show_contexts": false}, "files": {"fiboa_cli/__init__.py": {"executed_lines": [1, 3, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [1, 3, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/cli/__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/cli/setup.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [1, 3], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [1, 3], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [1, 3], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/conversion/__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/conversion/convert_gml.py": {"executed_lines": [1, 3, 4, 7, 14, 34], "summary": {"covered_lines": 6, "num_statements": 21, "percent_covered": 25.925925925925927, "percent_covered_display": "25.93", "missing_lines": 15, "excluded_lines": 0, "percent_statements_covered": 28.571428571428573, "percent_statements_covered_display": "28.57", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 5, "percent_branches_covered": 16.666666666666668, "percent_branches_covered_display": "16.67"}, "missing_lines": [15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 28, 29, 30, 31, 33], "excluded_lines": [], "executed_branches": [[14, 34]], "missing_branches": [[14, 15], [23, 24], [23, 26], [27, 28], [27, 30]], "functions": {"gml_assure_columns": {"executed_lines": [14, 34], "summary": {"covered_lines": 2, "num_statements": 17, "percent_covered": 13.043478260869565, "percent_covered_display": "13.04", "missing_lines": 15, "excluded_lines": 0, "percent_statements_covered": 11.764705882352942, "percent_statements_covered_display": "11.76", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 5, "percent_branches_covered": 16.666666666666668, "percent_branches_covered_display": "16.67"}, "missing_lines": [15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 28, 29, 30, 31, 33], "excluded_lines": [], "start_line": 7, "executed_branches": [[14, 34]], "missing_branches": [[14, 15], [23, 24], [23, 26], [27, 28], [27, 30]]}, "": {"executed_lines": [1, 3, 4, 7], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [1, 3, 4, 7, 14, 34], "summary": {"covered_lines": 6, "num_statements": 21, "percent_covered": 25.925925925925927, "percent_covered_display": "25.93", "missing_lines": 15, "excluded_lines": 0, "percent_statements_covered": 28.571428571428573, "percent_statements_covered_display": "28.57", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 5, "percent_branches_covered": 16.666666666666668, "percent_branches_covered_display": "16.67"}, "missing_lines": [15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 28, 29, 30, 31, 33], "excluded_lines": [], "start_line": 1, "executed_branches": [[14, 34]], "missing_branches": [[14, 15], [23, 24], [23, 26], [27, 28], [27, 30]]}}}, "fiboa_cli/conversion/converter_rest.py": {"executed_lines": [1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 15, 18, 26, 28, 33, 35, 36, 38, 39], "summary": {"covered_lines": 19, "num_statements": 60, "percent_covered": 28.37837837837838, "percent_covered_display": "28.38", "missing_lines": 41, "excluded_lines": 0, "percent_statements_covered": 31.666666666666668, "percent_statements_covered_display": "31.67", "num_branches": 14, "num_partial_branches": 2, "covered_branches": 2, "missing_branches": 12, "percent_branches_covered": 14.285714285714286, "percent_branches_covered_display": "14.29"}, "missing_lines": [16, 19, 24, 29, 30, 41, 42, 43, 45, 46, 47, 48, 49, 56, 57, 58, 59, 60, 61, 62, 65, 66, 67, 68, 69, 71, 72, 73, 74, 76, 77, 78, 80, 81, 82, 83, 87, 95, 97, 99, 100], "excluded_lines": [], "executed_branches": [[28, 33], [36, 38]], "missing_branches": [[28, 29], [36, 41], [61, 62], [61, 76], [65, 66], [65, 74], [71, 72], [71, 73], [80, 81], [80, 82], [99, 58], [99, 100]], "functions": {"EsriRESTConverterMixin.rest_layer_filter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [16], "excluded_lines": [], "start_line": 15, "executed_branches": [], "missing_branches": []}, "EsriRESTConverterMixin.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [19, 24], "excluded_lines": [], "start_line": 18, "executed_branches": [], "missing_branches": []}, "EsriRESTConverterMixin.download_files": {"executed_lines": [28, 33], "summary": {"covered_lines": 2, "num_statements": 4, "percent_covered": 50.0, "percent_covered_display": "50.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 50.0, "percent_statements_covered_display": "50.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [29, 30], "excluded_lines": [], "start_line": 26, "executed_branches": [[28, 33]], "missing_branches": [[28, 29]]}, "EsriRESTConverterMixin.get_data": {"executed_lines": [36, 38, 39], "summary": {"covered_lines": 3, "num_statements": 39, "percent_covered": 7.8431372549019605, "percent_covered_display": "7.84", "missing_lines": 36, "excluded_lines": 0, "percent_statements_covered": 7.6923076923076925, "percent_statements_covered_display": "7.69", "num_branches": 12, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 11, "percent_branches_covered": 8.333333333333334, "percent_branches_covered_display": "8.33"}, "missing_lines": [41, 42, 43, 45, 46, 47, 48, 49, 56, 57, 58, 59, 60, 61, 62, 65, 66, 67, 68, 69, 71, 72, 73, 74, 76, 77, 78, 80, 81, 82, 83, 87, 95, 97, 99, 100], "excluded_lines": [], "start_line": 35, "executed_branches": [[36, 38]], "missing_branches": [[36, 41], [61, 62], [61, 76], [65, 66], [65, 74], [71, 72], [71, 73], [80, 81], [80, 82], [99, 58], [99, 100]]}, "": {"executed_lines": [1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 15, 18, 26, 35], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"EsriRESTConverterMixin": {"executed_lines": [28, 33, 36, 38, 39], "summary": {"covered_lines": 5, "num_statements": 46, "percent_covered": 11.666666666666666, "percent_covered_display": "11.67", "missing_lines": 41, "excluded_lines": 0, "percent_statements_covered": 10.869565217391305, "percent_statements_covered_display": "10.87", "num_branches": 14, "num_partial_branches": 2, "covered_branches": 2, "missing_branches": 12, "percent_branches_covered": 14.285714285714286, "percent_branches_covered_display": "14.29"}, "missing_lines": [16, 19, 24, 29, 30, 41, 42, 43, 45, 46, 47, 48, 49, 56, 57, 58, 59, 60, 61, 62, 65, 66, 67, 68, 69, 71, 72, 73, 74, 76, 77, 78, 80, 81, 82, 83, 87, 95, 97, 99, 100], "excluded_lines": [], "start_line": 9, "executed_branches": [[28, 33], [36, 38]], "missing_branches": [[28, 29], [36, 41], [61, 62], [61, 76], [65, 66], [65, 74], [71, 72], [71, 73], [80, 81], [80, 82], [99, 58], [99, 100]]}, "": {"executed_lines": [1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 15, 18, 26, 35], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/conversion/duckdb.py": {"executed_lines": [1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 14, 20, 21, 32, 33, 37, 38, 40, 41, 42, 46, 47, 48, 50, 54, 55, 58, 59, 60, 61, 63, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81, 82, 86, 88, 91, 94, 95, 96, 97, 100, 102, 103, 104, 106, 107, 109, 111, 112, 113, 114, 133, 134, 136, 137, 138, 140, 141, 142, 147, 148, 151, 152, 153, 154, 155, 158, 159, 160, 161, 165, 167, 168, 169, 182, 185, 188, 190, 197, 198, 199, 200, 201, 203, 204, 208, 210, 212, 213, 215, 217, 221], "summary": {"covered_lines": 103, "num_statements": 117, "percent_covered": 79.88165680473372, "percent_covered_display": "79.88", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 88.03418803418803, "percent_statements_covered_display": "88.03", "num_branches": 52, "num_partial_branches": 18, "covered_branches": 32, "missing_branches": 20, "percent_branches_covered": 61.53846153846154, "percent_branches_covered_display": "61.54"}, "missing_lines": [43, 51, 52, 56, 62, 64, 65, 83, 87, 89, 92, 99, 218, 219], "excluded_lines": [], "executed_branches": [[32, 33], [42, 46], [47, 48], [50, 54], [55, 58], [59, 60], [61, 63], [71, 72], [71, 78], [72, 73], [72, 75], [76, 71], [76, 77], [82, 86], [86, 88], [88, 91], [91, 94], [95, 96], [95, 100], [96, 97], [106, 107], [147, 148], [152, 153], [159, 160], [159, 167], [160, 161], [160, 165], [168, 169], [199, 200], [199, 215], [201, 203], [210, 212]], "missing_branches": [[32, 37], [42, 43], [47, 50], [50, 51], [55, 56], [59, 64], [61, 62], [64, 65], [64, 69], [82, 83], [86, 87], [88, 89], [91, 92], [96, 99], [106, 109], [147, 151], [152, 158], [168, 182], [201, 210], [210, 213]], "functions": {"FiboaDuckDBBaseConverter.convert": {"executed_lines": [32, 33, 37, 38, 40, 41, 42, 46, 47, 48, 50, 54, 55, 58, 59, 60, 61, 63, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81, 82, 86, 88, 91, 94, 95, 96, 97, 100, 102, 103, 104, 106, 107, 109, 111, 112, 113, 114, 133, 134, 136, 137, 138, 140, 141, 142, 147, 148, 151, 152, 153, 154, 155, 158, 159, 160, 161, 165, 167, 168, 169, 182, 185, 188, 190, 197, 198, 199, 200, 201, 203, 204, 208, 210, 212, 213, 215, 217, 221], "summary": {"covered_lines": 89, "num_statements": 103, "percent_covered": 78.06451612903226, "percent_covered_display": "78.06", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 86.40776699029126, "percent_statements_covered_display": "86.41", "num_branches": 52, "num_partial_branches": 18, "covered_branches": 32, "missing_branches": 20, "percent_branches_covered": 61.53846153846154, "percent_branches_covered_display": "61.54"}, "missing_lines": [43, 51, 52, 56, 62, 64, 65, 83, 87, 89, 92, 99, 218, 219], "excluded_lines": [], "start_line": 21, "executed_branches": [[32, 33], [42, 46], [47, 48], [50, 54], [55, 58], [59, 60], [61, 63], [71, 72], [71, 78], [72, 73], [72, 75], [76, 71], [76, 77], [82, 86], [86, 88], [88, 91], [91, 94], [95, 96], [95, 100], [96, 97], [106, 107], [147, 148], [152, 153], [159, 160], [159, 167], [160, 161], [160, 165], [168, 169], [199, 200], [199, 215], [201, 203], [210, 212]], "missing_branches": [[32, 37], [42, 43], [47, 50], [50, 51], [55, 56], [59, 64], [61, 62], [64, 65], [64, 69], [82, 83], [86, 87], [88, 89], [91, 92], [96, 99], [106, 109], [147, 151], [152, 158], [168, 182], [201, 210], [210, 213]]}, "": {"executed_lines": [1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 14, 20, 21], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"FiboaDuckDBBaseConverter": {"executed_lines": [32, 33, 37, 38, 40, 41, 42, 46, 47, 48, 50, 54, 55, 58, 59, 60, 61, 63, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81, 82, 86, 88, 91, 94, 95, 96, 97, 100, 102, 103, 104, 106, 107, 109, 111, 112, 113, 114, 133, 134, 136, 137, 138, 140, 141, 142, 147, 148, 151, 152, 153, 154, 155, 158, 159, 160, 161, 165, 167, 168, 169, 182, 185, 188, 190, 197, 198, 199, 200, 201, 203, 204, 208, 210, 212, 213, 215, 217, 221], "summary": {"covered_lines": 89, "num_statements": 103, "percent_covered": 78.06451612903226, "percent_covered_display": "78.06", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 86.40776699029126, "percent_statements_covered_display": "86.41", "num_branches": 52, "num_partial_branches": 18, "covered_branches": 32, "missing_branches": 20, "percent_branches_covered": 61.53846153846154, "percent_branches_covered_display": "61.54"}, "missing_lines": [43, 51, 52, 56, 62, 64, 65, 83, 87, 89, 92, 99, 218, 219], "excluded_lines": [], "start_line": 20, "executed_branches": [[32, 33], [42, 46], [47, 48], [50, 54], [55, 58], [59, 60], [61, 63], [71, 72], [71, 78], [72, 73], [72, 75], [76, 71], [76, 77], [82, 86], [86, 88], [88, 91], [91, 94], [95, 96], [95, 100], [96, 97], [106, 107], [147, 148], [152, 153], [159, 160], [159, 167], [160, 161], [160, 165], [168, 169], [199, 200], [199, 215], [201, 203], [210, 212]], "missing_branches": [[32, 37], [42, 43], [47, 50], [50, 51], [55, 56], [59, 64], [61, 62], [64, 65], [64, 69], [82, 83], [86, 87], [88, 89], [91, 92], [96, 99], [106, 109], [147, 151], [152, 158], [168, 182], [201, 210], [210, 213]]}, "": {"executed_lines": [1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 14, 20, 21], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/conversion/fiboa_converter.py": {"executed_lines": [1, 2, 4, 6, 9, 12, 13, 14, 15, 17, 19, 20, 21, 22, 25, 27, 28, 31, 32, 33, 34, 35, 36, 48, 49, 51, 54, 56, 57, 58, 62, 63, 65, 67, 68, 69], "summary": {"covered_lines": 36, "num_statements": 41, "percent_covered": 86.88524590163935, "percent_covered_display": "86.89", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 87.8048780487805, "percent_statements_covered_display": "87.80", "num_branches": 20, "num_partial_branches": 1, "covered_branches": 17, "missing_branches": 3, "percent_branches_covered": 85.0, "percent_branches_covered_display": "85.00"}, "missing_lines": [37, 38, 39, 43, 46], "excluded_lines": [], "executed_branches": [[22, -19], [22, 25], [31, 32], [31, 48], [32, 31], [32, 33], [34, 32], [34, 35], [36, 32], [49, 51], [49, 63], [56, 57], [56, 62], [63, 65], [63, 67], [67, 68], [67, 69]], "missing_branches": [[36, 37], [38, 39], [38, 43]], "functions": {"FiboaBaseConverter.__init__": {"executed_lines": [20, 21, 22, 25], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 19, "executed_branches": [[22, -19], [22, 25]], "missing_branches": []}, "FiboaBaseConverter.post_migrate": {"executed_lines": [28, 31, 32, 33, 34, 35, 36, 48, 49, 51, 54, 56, 57, 58, 62, 63, 65, 67, 68, 69], "summary": {"covered_lines": 20, "num_statements": 25, "percent_covered": 81.3953488372093, "percent_covered_display": "81.40", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80.00", "num_branches": 18, "num_partial_branches": 1, "covered_branches": 15, "missing_branches": 3, "percent_branches_covered": 83.33333333333333, "percent_branches_covered_display": "83.33"}, "missing_lines": [37, 38, 39, 43, 46], "excluded_lines": [], "start_line": 27, "executed_branches": [[31, 32], [31, 48], [32, 31], [32, 33], [34, 32], [34, 35], [36, 32], [49, 51], [49, 63], [56, 57], [56, 62], [63, 65], [63, 67], [67, 68], [67, 69]], "missing_branches": [[36, 37], [38, 39], [38, 43]]}, "": {"executed_lines": [1, 2, 4, 6, 9, 12, 13, 14, 15, 17, 19, 27], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"FiboaBaseConverter": {"executed_lines": [20, 21, 22, 25, 28, 31, 32, 33, 34, 35, 36, 48, 49, 51, 54, 56, 57, 58, 62, 63, 65, 67, 68, 69], "summary": {"covered_lines": 24, "num_statements": 29, "percent_covered": 83.6734693877551, "percent_covered_display": "83.67", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 82.75862068965517, "percent_statements_covered_display": "82.76", "num_branches": 20, "num_partial_branches": 1, "covered_branches": 17, "missing_branches": 3, "percent_branches_covered": 85.0, "percent_branches_covered_display": "85.00"}, "missing_lines": [37, 38, 39, 43, 46], "excluded_lines": [], "start_line": 12, "executed_branches": [[22, -19], [22, 25], [31, 32], [31, 48], [32, 31], [32, 33], [34, 32], [34, 35], [36, 32], [49, 51], [49, 63], [56, 57], [56, 62], [63, 65], [63, 67], [67, 68], [67, 69]], "missing_branches": [[36, 37], [38, 39], [38, 43]]}, "": {"executed_lines": [1, 2, 4, 6, 9, 12, 13, 14, 15, 17, 19, 27], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/conversion/hilbert.py": {"executed_lines": [10, 12, 14, 17, 24, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 46, 52, 54, 55, 56, 57], "summary": {"covered_lines": 26, "num_statements": 28, "percent_covered": 88.23529411764706, "percent_covered_display": "88.24", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 92.85714285714286, "percent_statements_covered_display": "92.86", "num_branches": 6, "num_partial_branches": 2, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [29, 42], "excluded_lines": [], "executed_branches": [[28, 31], [32, 33], [32, 34], [41, 43]], "missing_branches": [[28, 29], [41, 42]], "functions": {"crs_total_bounds": {"executed_lines": [24, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.88888888888889, "percent_statements_covered_display": "88.89", "num_branches": 6, "num_partial_branches": 2, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [29, 42], "excluded_lines": [], "start_line": 17, "executed_branches": [[28, 31], [32, 33], [32, 34], [41, 43]], "missing_branches": [[28, 29], [41, 42]]}, "hilbert_distances_from_bounds": {"executed_lines": [52, 54, 55, 56, 57], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 46, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [10, 12, 14, 17, 46], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [10, 12, 14, 17, 24, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 46, 52, 54, 55, 56, 57], "summary": {"covered_lines": 26, "num_statements": 28, "percent_covered": 88.23529411764706, "percent_covered_display": "88.24", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 92.85714285714286, "percent_statements_covered_display": "92.86", "num_branches": 6, "num_partial_branches": 2, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [29, 42], "excluded_lines": [], "start_line": 1, "executed_branches": [[28, 31], [32, 33], [32, 34], [41, 43]], "missing_branches": [[28, 29], [41, 42]]}}}, "fiboa_cli/conversion/per_file.py": {"executed_lines": [1, 2, 3, 5, 6, 7, 9, 11, 12, 17, 18, 30, 31, 32, 33, 34, 41, 42, 55, 56, 57, 58, 59, 64, 65, 76, 84, 86, 113, 114, 116, 117, 121, 122, 123, 125, 126, 127, 128, 129, 130, 131, 132, 133, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 151, 152, 153, 157, 158, 159, 161, 162, 163, 171, 172, 173, 174, 176, 180, 181, 182, 183, 186, 187, 191, 192, 194, 195, 196, 208, 209, 215, 217, 218, 219, 220, 224, 230, 236, 237, 238, 246, 248, 249, 250, 253, 254, 255, 256, 257, 259, 260, 263, 277, 278, 279, 282, 284, 285, 286, 287, 288, 290, 291, 294, 303, 304, 305, 306, 307, 308, 309, 310, 312, 313, 316, 328, 329, 330, 332, 333, 334, 336, 338, 339, 340, 341, 342, 343, 344, 345, 347, 348, 349, 350, 352, 353, 355, 356, 358, 360, 361, 362, 366, 368, 369, 370, 371, 372, 373, 375, 376, 377, 378, 383, 388, 389, 390, 391, 393], "summary": {"covered_lines": 170, "num_statements": 188, "percent_covered": 84.10852713178295, "percent_covered_display": "84.11", "missing_lines": 18, "excluded_lines": 0, "percent_statements_covered": 90.42553191489361, "percent_statements_covered_display": "90.43", "num_branches": 70, "num_partial_branches": 21, "covered_branches": 47, "missing_branches": 23, "percent_branches_covered": 67.14285714285714, "percent_branches_covered_display": "67.14"}, "missing_lines": [36, 37, 38, 60, 63, 154, 210, 221, 222, 283, 289, 311, 346, 357, 374, 380, 381, 386], "excluded_lines": [], "executed_branches": [[32, 33], [41, 42], [41, 55], [56, 57], [56, 76], [59, 64], [113, 114], [113, 121], [116, 117], [121, 122], [121, 123], [128, 129], [128, 130], [139, 140], [142, 143], [142, 161], [145, 146], [145, 151], [153, 157], [157, 158], [162, 163], [182, 183], [182, 191], [183, 186], [191, 192], [209, 215], [217, 218], [218, 219], [218, 224], [236, 237], [236, 246], [282, 284], [288, 290], [306, 307], [308, 309], [310, 312], [345, 347], [352, 353], [352, 355], [356, 358], [361, 362], [361, 393], [370, 371], [370, 383], [373, 375], [377, 378], [383, 388]], "missing_branches": [[32, 36], [37, 38], [37, 41], [59, 60], [116, 121], [139, 141], [153, 154], [157, 159], [162, 171], [183, 182], [191, 194], [209, 210], [217, 224], [282, 283], [288, 289], [306, 308], [308, 310], [310, 311], [345, 346], [356, 357], [373, 374], [377, 380], [383, 386]], "functions": {"PerFileBaseConverter.convert": {"executed_lines": [30, 31, 32, 33, 34, 41, 42, 55, 56, 57, 58, 59, 64, 65, 76, 84], "summary": {"covered_lines": 16, "num_statements": 21, "percent_covered": 70.96774193548387, "percent_covered_display": "70.97", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 76.19047619047619, "percent_statements_covered_display": "76.19", "num_branches": 10, "num_partial_branches": 2, "covered_branches": 6, "missing_branches": 4, "percent_branches_covered": 60.0, "percent_branches_covered_display": "60.00"}, "missing_lines": [36, 37, 38, 60, 63], "excluded_lines": [], "start_line": 18, "executed_branches": [[32, 33], [41, 42], [41, 55], [56, 57], [56, 76], [59, 64]], "missing_branches": [[32, 36], [37, 38], [37, 41], [59, 60]]}, "PerFileBaseConverter.merge_files": {"executed_lines": [113, 114, 116, 117, 121, 122, 123, 125, 126, 127, 128, 129, 130, 131, 132, 133, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 151, 152, 153, 157, 158, 159, 161, 162, 163, 171, 172, 173, 174, 176, 180, 181, 182, 183, 186, 187, 191, 192, 194, 195, 196, 208, 209, 215, 217, 218, 219, 220, 224], "summary": {"covered_lines": 59, "num_statements": 63, "percent_covered": 86.3157894736842, "percent_covered_display": "86.32", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 93.65079365079364, "percent_statements_covered_display": "93.65", "num_branches": 32, "num_partial_branches": 9, "covered_branches": 23, "missing_branches": 9, "percent_branches_covered": 71.875, "percent_branches_covered_display": "71.88"}, "missing_lines": [154, 210, 221, 222], "excluded_lines": [], "start_line": 86, "executed_branches": [[113, 114], [113, 121], [116, 117], [121, 122], [121, 123], [128, 129], [128, 130], [139, 140], [142, 143], [142, 161], [145, 146], [145, 151], [153, 157], [157, 158], [162, 163], [182, 183], [182, 191], [183, 186], [191, 192], [209, 215], [217, 218], [218, 219], [218, 224]], "missing_branches": [[116, 121], [139, 141], [153, 154], [157, 159], [162, 171], [183, 182], [191, 194], [209, 210], [217, 224]]}, "_bounds_array_for_table": {"executed_lines": [236, 237, 238, 246, 248, 249, 250], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 230, "executed_branches": [[236, 237], [236, 246]], "missing_branches": []}, "_hilbert_keys_for_table": {"executed_lines": [254, 255, 256, 257, 259, 260], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 253, "executed_branches": [], "missing_branches": []}, "_ensure_hilbert_sorted": {"executed_lines": [277, 278, 279, 282, 284, 285, 286, 287, 288, 290, 291], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 76.47058823529412, "percent_covered_display": "76.47", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 84.61538461538461, "percent_statements_covered_display": "84.62", "num_branches": 4, "num_partial_branches": 2, "covered_branches": 2, "missing_branches": 2, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [283, 289], "excluded_lines": [], "start_line": 263, "executed_branches": [[282, 284], [288, 290]], "missing_branches": [[282, 283], [288, 289]]}, "_build_output_schema": {"executed_lines": [303, 304, 305, 306, 307, 308, 309, 310, 312, 313], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 76.47058823529412, "percent_covered_display": "76.47", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "90.91", "num_branches": 6, "num_partial_branches": 3, "covered_branches": 3, "missing_branches": 3, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [311], "excluded_lines": [], "start_line": 294, "executed_branches": [[306, 307], [308, 309], [310, 312]], "missing_branches": [[306, 308], [308, 310], [310, 311]]}, "_streaming_merge": {"executed_lines": [328, 329, 330, 332, 333, 334, 336, 352, 353, 355, 356, 358, 360, 361, 362, 366, 368, 369, 370, 371, 372, 373, 375, 376, 377, 378, 383, 388, 389, 390, 391, 393], "summary": {"covered_lines": 32, "num_statements": 37, "percent_covered": 82.3529411764706, "percent_covered_display": "82.35", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 86.48648648648648, "percent_statements_covered_display": "86.49", "num_branches": 14, "num_partial_branches": 4, "covered_branches": 10, "missing_branches": 4, "percent_branches_covered": 71.42857142857143, "percent_branches_covered_display": "71.43"}, "missing_lines": [357, 374, 380, 381, 386], "excluded_lines": [], "start_line": 316, "executed_branches": [[352, 353], [352, 355], [356, 358], [361, 362], [361, 393], [370, 371], [370, 383], [373, 375], [377, 378], [383, 388]], "missing_branches": [[356, 357], [373, 374], [377, 380], [383, 386]]}, "_streaming_merge.refill": {"executed_lines": [338, 339, 340, 341, 342, 343, 344, 345, 347, 348, 349, 350], "summary": {"covered_lines": 12, "num_statements": 13, "percent_covered": 86.66666666666667, "percent_covered_display": "86.67", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 92.3076923076923, "percent_statements_covered_display": "92.31", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [346], "excluded_lines": [], "start_line": 336, "executed_branches": [[345, 347]], "missing_branches": [[345, 346]]}, "": {"executed_lines": [1, 2, 3, 5, 6, 7, 9, 11, 12, 17, 18, 86, 230, 253, 263, 294, 316], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"PerFileBaseConverter": {"executed_lines": [30, 31, 32, 33, 34, 41, 42, 55, 56, 57, 58, 59, 64, 65, 76, 84, 113, 114, 116, 117, 121, 122, 123, 125, 126, 127, 128, 129, 130, 131, 132, 133, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 151, 152, 153, 157, 158, 159, 161, 162, 163, 171, 172, 173, 174, 176, 180, 181, 182, 183, 186, 187, 191, 192, 194, 195, 196, 208, 209, 215, 217, 218, 219, 220, 224], "summary": {"covered_lines": 75, "num_statements": 84, "percent_covered": 82.53968253968254, "percent_covered_display": "82.54", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 89.28571428571429, "percent_statements_covered_display": "89.29", "num_branches": 42, "num_partial_branches": 11, "covered_branches": 29, "missing_branches": 13, "percent_branches_covered": 69.04761904761905, "percent_branches_covered_display": "69.05"}, "missing_lines": [36, 37, 38, 60, 63, 154, 210, 221, 222], "excluded_lines": [], "start_line": 17, "executed_branches": [[32, 33], [41, 42], [41, 55], [56, 57], [56, 76], [59, 64], [113, 114], [113, 121], [116, 117], [121, 122], [121, 123], [128, 129], [128, 130], [139, 140], [142, 143], [142, 161], [145, 146], [145, 151], [153, 157], [157, 158], [162, 163], [182, 183], [182, 191], [183, 186], [191, 192], [209, 215], [217, 218], [218, 219], [218, 224]], "missing_branches": [[32, 36], [37, 38], [37, 41], [59, 60], [116, 121], [139, 141], [153, 154], [157, 159], [162, 171], [183, 182], [191, 194], [209, 210], [217, 224]]}, "": {"executed_lines": [1, 2, 3, 5, 6, 7, 9, 11, 12, 17, 18, 86, 230, 236, 237, 238, 246, 248, 249, 250, 253, 254, 255, 256, 257, 259, 260, 263, 277, 278, 279, 282, 284, 285, 286, 287, 288, 290, 291, 294, 303, 304, 305, 306, 307, 308, 309, 310, 312, 313, 316, 328, 329, 330, 332, 333, 334, 336, 338, 339, 340, 341, 342, 343, 344, 345, 347, 348, 349, 350, 352, 353, 355, 356, 358, 360, 361, 362, 366, 368, 369, 370, 371, 372, 373, 375, 376, 377, 378, 383, 388, 389, 390, 391, 393], "summary": {"covered_lines": 95, "num_statements": 104, "percent_covered": 85.60606060606061, "percent_covered_display": "85.61", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 91.34615384615384, "percent_statements_covered_display": "91.35", "num_branches": 28, "num_partial_branches": 10, "covered_branches": 18, "missing_branches": 10, "percent_branches_covered": 64.28571428571429, "percent_branches_covered_display": "64.29"}, "missing_lines": [283, 289, 311, 346, 357, 374, 380, 381, 386], "excluded_lines": [], "start_line": 1, "executed_branches": [[236, 237], [236, 246], [282, 284], [288, 290], [306, 307], [308, 309], [310, 312], [345, 347], [352, 353], [352, 355], [356, 358], [361, 362], [361, 393], [370, 371], [370, 383], [373, 375], [377, 378], [383, 388]], "missing_branches": [[282, 283], [288, 289], [306, 308], [308, 310], [310, 311], [345, 346], [356, 357], [373, 374], [377, 380], [383, 386]]}}}, "fiboa_cli/convert.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ConvertData": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/converters.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converters": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/create_geojson.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"CreateGeoJson": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/create_geoparquet.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"CreateGeoParquet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/create_jsonschema.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"CreateJsonSchema": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/create_stac.py": {"executed_lines": [1, 2, 3, 4, 5, 6, 8, 11, 12, 14, 15, 33, 34, 35, 36, 39, 40, 43, 44, 45, 47], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95.45", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95.45", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [16], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"CreateStacCollection.get_cli_args": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [16], "excluded_lines": [], "start_line": 15, "executed_branches": [], "missing_branches": []}, "CreateStacCollection.create": {"executed_lines": [34, 35, 36, 39, 40, 43, 44, 45, 47], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 33, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 3, 4, 5, 6, 8, 11, 12, 14, 15, 33], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"CreateStacCollection": {"executed_lines": [34, 35, 36, 39, 40, 43, 44, 45, 47], "summary": {"covered_lines": 9, "num_statements": 10, "percent_covered": 90.0, "percent_covered_display": "90.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.0, "percent_statements_covered_display": "90.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [16], "excluded_lines": [], "start_line": 11, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 3, 4, 5, 6, 8, 11, 12, 14, 15, 33], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ai4sf.py": {"executed_lines": [1, 3, 6, 7, 74, 75, 76, 78, 87, 88, 89, 91, 99, 100, 104, 109, 111, 114, 116], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.migrate": {"executed_lines": [111, 114], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 109, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 74, 75, 76, 78, 87, 88, 89, 91, 99, 100, 104, 109, 116], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [111, 114], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 74, 75, 76, 78, 87, 88, 89, 91, 99, 100, 104, 109, 116], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/at.py": {"executed_lines": [1, 3, 4, 7, 8, 19, 20, 21, 22, 23, 33, 34, 35, 43], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 7, 8, 19, 20, 21, 22, 23, 33, 34, 35, 43], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 19, 20, 21, 22, 23, 33, 34, 35, 43], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/at_block.py": {"executed_lines": [1, 3, 6, 7, 12, 13, 14, 15, 16, 23, 24, 25, 36, 37], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 12, 13, 14, 15, 16, 23, 24, 25, 36, 37], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 12, 13, 14, 15, 16, 23, 24, 25, 36, 37], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/be_vlg.py": {"executed_lines": [1, 3, 4, 6, 9, 10, 26, 27, 28, 29, 30, 35, 37, 38, 41, 48, 49, 53, 55, 65, 66, 68], "summary": {"covered_lines": 22, "num_statements": 25, "percent_covered": 79.3103448275862, "percent_covered_display": "79.31", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 88.0, "percent_statements_covered_display": "88.00", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 3, "percent_branches_covered": 25.0, "percent_branches_covered_display": "25.00"}, "missing_lines": [50, 51, 52], "excluded_lines": [], "executed_branches": [[49, 53]], "missing_branches": [[49, 50], [51, 52], [51, 53]], "functions": {"Converter.migrate": {"executed_lines": [49, 53], "summary": {"covered_lines": 2, "num_statements": 5, "percent_covered": 33.333333333333336, "percent_covered_display": "33.33", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 40.0, "percent_statements_covered_display": "40.00", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 3, "percent_branches_covered": 25.0, "percent_branches_covered_display": "25.00"}, "missing_lines": [50, 51, 52], "excluded_lines": [], "start_line": 48, "executed_branches": [[49, 53]], "missing_branches": [[49, 50], [51, 52], [51, 53]]}, "": {"executed_lines": [1, 3, 4, 6, 9, 10, 26, 27, 28, 29, 30, 35, 37, 38, 41, 48, 55, 65, 66, 68], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [49, 53], "summary": {"covered_lines": 2, "num_statements": 5, "percent_covered": 33.333333333333336, "percent_covered_display": "33.33", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 40.0, "percent_statements_covered_display": "40.00", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 3, "percent_branches_covered": 25.0, "percent_branches_covered_display": "25.00"}, "missing_lines": [50, 51, 52], "excluded_lines": [], "start_line": 9, "executed_branches": [[49, 53]], "missing_branches": [[49, 50], [51, 52], [51, 53]]}, "": {"executed_lines": [1, 3, 4, 6, 9, 10, 26, 27, 28, 29, 30, 35, 37, 38, 41, 48, 55, 65, 66, 68], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/be_wal.py": {"executed_lines": [1, 2, 4, 5, 6, 9, 10, 15, 16, 17, 18, 19, 31, 32, 33, 40, 41, 44, 46, 47, 49, 54, 57, 65], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.layer_filter": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 46, "executed_branches": [], "missing_branches": []}, "Converter.file_migration": {"executed_lines": [57, 65], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 54, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 9, 10, 15, 16, 17, 18, 19, 31, 32, 33, 40, 41, 44, 46, 49, 54], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [47, 57, 65], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 9, 10, 15, 16, 17, 18, 19, 31, 32, 33, 40, 41, 44, 46, 49, 54], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/bg.py": {"executed_lines": [1, 2, 4, 5, 8, 9, 13, 14, 15, 16, 17, 18, 23, 24, 31, 32, 34, 35, 36, 37, 38, 39], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"BGConverter.migrate": {"executed_lines": [35, 36, 37, 38, 39], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 34, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 13, 14, 15, 16, 17, 18, 23, 24, 31, 32, 34], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"BGConverter": {"executed_lines": [35, 36, 37, 38, 39], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 13, 14, 15, 16, 17, 18, 23, 24, 31, 32, 34], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/br_ba_lem.py": {"executed_lines": [1, 3, 6, 7, 12, 13, 14, 15, 16, 17, 24, 25, 26, 27, 44, 65], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 12, 13, 14, 15, 16, 17, 24, 25, 26, 27, 44, 65], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 12, 13, 14, 15, 16, 17, 24, 25, 26, 27, 44, 65], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/br_conab.py": {"executed_lines": [1, 2, 4, 5, 7, 10, 11, 44, 47, 48, 49, 50, 60, 63, 64, 65, 73, 80, 90, 98, 104], "summary": {"covered_lines": 21, "num_statements": 38, "percent_covered": 50.0, "percent_covered_display": "50.00", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 55.26315789473684, "percent_statements_covered_display": "55.26", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [81, 83, 84, 86, 87, 88, 91, 92, 93, 94, 95, 96, 100, 101, 105, 106, 107], "excluded_lines": [], "executed_branches": [], "missing_branches": [[86, 87], [86, 88], [105, 106], [105, 107]], "functions": {"Converter.file_migration": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [81, 83, 84, 86, 87, 88], "excluded_lines": [], "start_line": 80, "executed_branches": [], "missing_branches": [[86, 87], [86, 88]]}, "Converter.migrate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [91, 92, 93, 94, 95, 96], "excluded_lines": [], "start_line": 90, "executed_branches": [], "missing_branches": []}, "Converter.get_data": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [100, 101], "excluded_lines": [], "start_line": 98, "executed_branches": [], "missing_branches": []}, "fformat": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [105, 106, 107], "excluded_lines": [], "start_line": 104, "executed_branches": [], "missing_branches": [[105, 106], [105, 107]]}, "": {"executed_lines": [1, 2, 4, 5, 7, 10, 11, 44, 47, 48, 49, 50, 60, 63, 64, 65, 73, 80, 90, 98, 104], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [81, 83, 84, 86, 87, 88, 91, 92, 93, 94, 95, 96, 100, 101], "excluded_lines": [], "start_line": 10, "executed_branches": [], "missing_branches": [[86, 87], [86, 88]]}, "": {"executed_lines": [1, 2, 4, 5, 7, 10, 11, 44, 47, 48, 49, 50, 60, 63, 64, 65, 73, 80, 90, 98, 104], "summary": {"covered_lines": 21, "num_statements": 24, "percent_covered": 80.76923076923077, "percent_covered_display": "80.77", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 87.5, "percent_statements_covered_display": "87.50", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [105, 106, 107], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": [[105, 106], [105, 107]]}}}, "fiboa_cli/datasets/ch.py": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 22, 23, 24, 25, 26, 29, 30, 31, 39, 42, 43, 44, 47], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 22, 23, 24, 25, 26, 29, 30, 31, 39, 42, 43, 44, 47], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 22, 23, 24, 25, 26, 29, 30, 31, 39, 42, 43, 44, 47], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/commons/data.py": {"executed_lines": [1, 2, 5, 6, 7, 8], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"read_data_csv": {"executed_lines": [6, 7, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [1, 2, 5, 6, 7, 8], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/commons/ec.py": {"executed_lines": [1, 4, 11, 12, 18, 19, 20, 21, 22, 23, 24, 26, 27, 29, 30, 31], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [[20, 21], [20, 22], [23, 24], [23, 26]], "missing_branches": [], "functions": {"EuroCropsConverterMixin.__init__": {"executed_lines": [19, 20, 21, 22, 23, 24, 26, 27, 29, 30, 31], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 18, "executed_branches": [[20, 21], [20, 22], [23, 24], [23, 26]], "missing_branches": []}, "": {"executed_lines": [1, 4, 11, 12, 18], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"EuroCropsConverterMixin": {"executed_lines": [19, 20, 21, 22, 23, 24, 26, 27, 29, 30, 31], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [[20, 21], [20, 22], [23, 24], [23, 26]], "missing_branches": []}, "": {"executed_lines": [1, 4, 11, 12, 18], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/commons/euro_land.py": {"executed_lines": [1, 2, 5, 21, 27, 38, 39, 46, 47, 48, 49, 51, 54, 55, 56, 57], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 94.44444444444444, "percent_covered_display": "94.44", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [[54, 55]], "missing_branches": [[54, 57]], "functions": {"EuroLandBaseConverter.__init__": {"executed_lines": [47, 48, 49], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 46, "executed_branches": [], "missing_branches": []}, "EuroLandBaseConverter.migrate": {"executed_lines": [54, 55, 56, 57], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 51, "executed_branches": [[54, 55]], "missing_branches": [[54, 57]]}, "": {"executed_lines": [1, 2, 5, 21, 27, 38, 39, 46, 51], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"EuroLandBaseConverter": {"executed_lines": [47, 48, 49, 54, 55, 56, 57], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 88.88888888888889, "percent_covered_display": "88.89", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [[54, 55]], "missing_branches": [[54, 57]]}, "": {"executed_lines": [1, 2, 5, 21, 27, 38, 39, 46, 51], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/commons/hcat.py": {"executed_lines": [1, 2, 3, 5, 6, 7, 8, 10, 11, 14, 20, 21, 22, 24, 30, 31, 32, 33, 35, 36, 37, 38, 41, 43, 44, 45, 48, 50, 52, 54, 55, 59, 60, 62, 63, 65, 66, 68, 70, 71, 73, 74, 77, 78, 79, 80, 82, 83, 86, 87, 88, 96, 98, 99, 102, 104, 105, 106, 109, 110, 111, 112, 115, 116, 118, 119, 120, 121], "summary": {"covered_lines": 68, "num_statements": 71, "percent_covered": 94.6236559139785, "percent_covered_display": "94.62", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 95.77464788732394, "percent_statements_covered_display": "95.77", "num_branches": 22, "num_partial_branches": 2, "covered_branches": 20, "missing_branches": 2, "percent_branches_covered": 90.9090909090909, "percent_branches_covered_display": "90.91"}, "missing_lines": [46, 47, 117], "excluded_lines": [], "executed_branches": [[37, 38], [55, 59], [55, 98], [59, 60], [59, 62], [63, 65], [63, 68], [74, 77], [74, 82], [77, 74], [77, 78], [82, 83], [82, 98], [98, 99], [98, 102], [110, 111], [110, 112], [116, 118], [118, 119], [118, 120]], "missing_branches": [[37, 41], [116, 117]], "functions": {"AddHCATMixin.__init__": {"executed_lines": [31, 32, 33], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 30, "executed_branches": [], "missing_branches": []}, "AddHCATMixin.convert": {"executed_lines": [36, 37, 38, 41], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 35, "executed_branches": [[37, 38]], "missing_branches": [[37, 41]]}, "AddHCATMixin.get_code_column": {"executed_lines": [44, 45, 48, 50], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "66.67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "66.67", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [46, 47], "excluded_lines": [], "start_line": 43, "executed_branches": [], "missing_branches": []}, "AddHCATMixin.add_hcat": {"executed_lines": [54, 55, 59, 60, 62, 63, 65, 66, 68, 70, 73, 74, 77, 78, 79, 80, 82, 83, 86, 87, 88, 96, 98, 99, 102], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 14, "num_partial_branches": 0, "covered_branches": 14, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 52, "executed_branches": [[55, 59], [55, 98], [59, 60], [59, 62], [63, 65], [63, 68], [74, 77], [74, 82], [77, 74], [77, 78], [82, 83], [82, 98], [98, 99], [98, 102]], "missing_branches": []}, "AddHCATMixin.add_hcat.map_to": {"executed_lines": [71], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 70, "executed_branches": [], "missing_branches": []}, "AddHCATMixin.post_migrate": {"executed_lines": [105, 106], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 104, "executed_branches": [], "missing_branches": []}, "ec_url": {"executed_lines": [110, 111, 112], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 109, "executed_branches": [[110, 111], [110, 112]], "missing_branches": []}, "load_ec_mapping": {"executed_lines": [116, 118, 119, 120, 121], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 80.0, "percent_covered_display": "80.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83.33", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 1, "percent_branches_covered": 75.0, "percent_branches_covered_display": "75.00"}, "missing_lines": [117], "excluded_lines": [], "start_line": 115, "executed_branches": [[116, 118], [118, 119], [118, 120]], "missing_branches": [[116, 117]]}, "": {"executed_lines": [1, 2, 3, 5, 6, 7, 8, 10, 11, 14, 20, 21, 22, 24, 30, 35, 43, 52, 104, 109, 115], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"AddHCATMixin": {"executed_lines": [31, 32, 33, 36, 37, 38, 41, 44, 45, 48, 50, 54, 55, 59, 60, 62, 63, 65, 66, 68, 70, 71, 73, 74, 77, 78, 79, 80, 82, 83, 86, 87, 88, 96, 98, 99, 102, 105, 106], "summary": {"covered_lines": 39, "num_statements": 41, "percent_covered": 94.73684210526316, "percent_covered_display": "94.74", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 95.1219512195122, "percent_statements_covered_display": "95.12", "num_branches": 16, "num_partial_branches": 1, "covered_branches": 15, "missing_branches": 1, "percent_branches_covered": 93.75, "percent_branches_covered_display": "93.75"}, "missing_lines": [46, 47], "excluded_lines": [], "start_line": 14, "executed_branches": [[37, 38], [55, 59], [55, 98], [59, 60], [59, 62], [63, 65], [63, 68], [74, 77], [74, 82], [77, 74], [77, 78], [82, 83], [82, 98], [98, 99], [98, 102]], "missing_branches": [[37, 41]]}, "": {"executed_lines": [1, 2, 3, 5, 6, 7, 8, 10, 11, 14, 20, 21, 22, 24, 30, 35, 43, 52, 104, 109, 110, 111, 112, 115, 116, 118, 119, 120, 121], "summary": {"covered_lines": 29, "num_statements": 30, "percent_covered": 94.44444444444444, "percent_covered_display": "94.44", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.66666666666667, "percent_statements_covered_display": "96.67", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 5, "missing_branches": 1, "percent_branches_covered": 83.33333333333333, "percent_branches_covered_display": "83.33"}, "missing_lines": [117], "excluded_lines": [], "start_line": 1, "executed_branches": [[110, 111], [110, 112], [116, 118], [118, 119], [118, 120]], "missing_branches": [[116, 117]]}}}, "fiboa_cli/datasets/cz.py": {"executed_lines": [1, 2, 4, 5, 7, 9, 21, 24, 25, 26, 27, 28, 29, 30, 31, 41, 42, 43], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 4, 5, 7, 9, 21, 24, 25, 26, 27, 28, 29, 30, 31, 41, 42, 43], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 21, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 7, 9, 21, 24, 25, 26, 27, 28, 29, 30, 31, 41, 42, 43], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_bb.py": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 28, 36], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 28, 36], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 28, 36], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_bb_block.py": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 30, 41], "summary": {"covered_lines": 15, "num_statements": 16, "percent_covered": 93.75, "percent_covered_display": "93.75", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 93.75, "percent_statements_covered_display": "93.75", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [42], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.layer_filter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [42], "excluded_lines": [], "start_line": 41, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 30, 41], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [42], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 30, 41], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_by.py": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 31, 34], "summary": {"covered_lines": 20, "num_statements": 27, "percent_covered": 74.07407407407408, "percent_covered_display": "74.07", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 74.07407407407408, "percent_statements_covered_display": "74.07", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [32, 35, 36, 37, 39, 40, 41], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.layer_filter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [32], "excluded_lines": [], "start_line": 31, "executed_branches": [], "missing_branches": []}, "Converter.migrate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [35, 36, 37, 39, 40, 41], "excluded_lines": [], "start_line": 34, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 31, 34], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [32, 35, 36, 37, 39, 40, 41], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 31, 34], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_mv.py": {"executed_lines": [1, 3, 4, 7, 8, 11, 12, 13, 14, 15, 17, 18, 19, 20, 24, 37, 55], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 7, 8, 11, 12, 13, 14, 15, 17, 18, 19, 20, 24, 37, 55], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 11, 12, 13, 14, 15, 17, 18, 19, 20, 24, 37, 55], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_nds.py": {"executed_lines": [1, 2, 4, 5, 8, 17, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 37, 45, 50, 52], "summary": {"covered_lines": 20, "num_statements": 28, "percent_covered": 62.5, "percent_covered_display": "62.50", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 71.42857142857143, "percent_statements_covered_display": "71.43", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [53, 54, 55, 56, 58, 59, 60, 62], "excluded_lines": [], "executed_branches": [], "missing_branches": [[53, 54], [53, 58], [58, 59], [58, 62]], "functions": {"Converter.migrate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [53, 54, 55, 56, 58, 59, 60, 62], "excluded_lines": [], "start_line": 52, "executed_branches": [], "missing_branches": [[53, 54], [53, 58], [58, 59], [58, 62]]}, "": {"executed_lines": [1, 2, 4, 5, 8, 17, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 37, 45, 50, 52], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [53, 54, 55, 56, 58, 59, 60, 62], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": [[53, 54], [53, 58], [58, 59], [58, 62]]}, "": {"executed_lines": [1, 2, 4, 5, 8, 17, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 37, 45, 50, 52], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_nds_block.py": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 27], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 27], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 27], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_nrw.py": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_sax.py": {"executed_lines": [1, 2, 4, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 66, 68, 69, 70], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [[68, -7], [68, 69], [69, 68], [69, 70]], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 4, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 66, 68, 69, 70], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [[68, -7], [68, 69], [69, 68], [69, 70]], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 66, 68, 69, 70], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [[68, -7], [68, 69], [69, 68], [69, 70]], "missing_branches": []}}}, "fiboa_cli/datasets/de_sh.py": {"executed_lines": [1, 3, 6, 7, 13, 14, 15, 16, 17, 18, 19, 20, 21, 28], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 13, 14, 15, 16, 17, 18, 19, 20, 21, 28], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 13, 14, 15, 16, 17, 18, 19, 20, 21, 28], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_sl.py": {"executed_lines": [1, 3, 5, 8, 13, 18, 19, 30, 31, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 52, 54], "summary": {"covered_lines": 21, "num_statements": 28, "percent_covered": 75.0, "percent_covered_display": "75.00", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 75.0, "percent_statements_covered_display": "75.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [9, 10, 14, 15, 55, 56, 57], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"parse_flik": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [9, 10], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "parse_size": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [14, 15], "excluded_lines": [], "start_line": 13, "executed_branches": [], "missing_branches": []}, "Converter.migrate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [55, 56, 57], "excluded_lines": [], "start_line": 54, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 5, 8, 13, 18, 19, 30, 31, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 52, 54], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [55, 56, 57], "excluded_lines": [], "start_line": 30, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 5, 8, 13, 18, 19, 30, 31, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 52, 54], "summary": {"covered_lines": 21, "num_statements": 25, "percent_covered": 84.0, "percent_covered_display": "84.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 84.0, "percent_statements_covered_display": "84.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [9, 10, 14, 15], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/de_th.py": {"executed_lines": [1, 3, 4, 6, 9, 10, 14, 15, 16, 17, 18, 35, 36, 37, 39, 41, 59, 60, 69, 74, 78], "summary": {"covered_lines": 21, "num_statements": 24, "percent_covered": 87.5, "percent_covered_display": "87.50", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 87.5, "percent_statements_covered_display": "87.50", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [70, 71, 72], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.migrate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [70, 71, 72], "excluded_lines": [], "start_line": 69, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 9, 10, 14, 15, 16, 17, 18, 35, 36, 37, 39, 41, 59, 60, 69, 74, 78], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [70, 71, 72], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 9, 10, 14, 15, 16, 17, 18, 35, 36, 37, 39, 41, 59, 60, 69, 74, 78], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/digifarm.py": {"executed_lines": [8, 11, 12, 13, 19, 20, 21, 22, 26, 27, 28, 29, 30, 31], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [8, 11, 12, 13, 19, 20, 21, 22, 26, 27, 28, 29, 30, 31], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 11, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [8, 11, 12, 13, 19, 20, 21, 22, 26, 27, 28, 29, 30, 31], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/dk.py": {"executed_lines": [1, 2, 4, 5, 8, 9, 13, 14, 15, 16, 18, 19, 20, 21, 28, 30, 31, 32], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"DKConverter.migrate": {"executed_lines": [31, 32], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 30, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 13, 14, 15, 16, 18, 19, 20, 21, 28, 30], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"DKConverter": {"executed_lines": [31, 32], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 13, 14, 15, 16, 18, 19, 20, 21, 28, 30], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_be_vlg.py": {"executed_lines": [1, 2, 5, 6, 7, 13, 14, 15], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"ECConverter.__init__": {"executed_lines": [14, 15], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 13, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 7, 13], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ECConverter": {"executed_lines": [14, 15], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 7, 13], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_ee.py": {"executed_lines": [1, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 22, 23, 25, 39, 40], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 22, 23, 25, 39, 40], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Convert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 10, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 22, 23, 25, 39, 40], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_lt.py": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 11, 12, 13, 14, 19, 22, 29, 30, 50], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 11, 12, 13, 14, 19, 22, 29, 30, 50], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 11, 12, 13, 14, 19, 22, 29, 30, 50], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_lv.py": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 32, 36, 57, 59], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.add_hcat": {"executed_lines": [59], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 57, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 32, 36, 57], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [59], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 32, 36, 57], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_nl_crop.py": {"executed_lines": [1, 2, 5, 6, 8, 9], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"NLEuroCropConverter.__init__": {"executed_lines": [9], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 8], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"NLEuroCropConverter": {"executed_lines": [9], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 8], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_ro.py": {"executed_lines": [1, 2, 5, 7, 8, 9, 10, 11, 12, 19, 22, 23, 27, 28, 36, 37, 38, 43], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 5, 7, 8, 9, 10, 11, 12, 19, 22, 23, 27, 28, 36, 37, 38, 43], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Convert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 7, 8, 9, 10, 11, 12, 19, 22, 23, 27, 28, 36, 37, 38, 43], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ec_si.py": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 13, 14, 15, 16, 18, 19, 21, 33, 45, 47], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.add_hcat": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 45, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 13, 14, 15, 16, 18, 19, 21, 33, 45], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 13, 14, 15, 16, 18, 19, 21, 33, 45], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ee.py": {"executed_lines": [1, 3, 4, 6, 13, 16, 18, 24, 25, 26, 27, 28, 33, 34, 35, 36, 37, 39], "summary": {"covered_lines": 18, "num_statements": 19, "percent_covered": 94.73684210526316, "percent_covered_display": "94.74", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 94.73684210526316, "percent_statements_covered_display": "94.74", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [40], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Convert.file_migration": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [40], "excluded_lines": [], "start_line": 39, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 13, 16, 18, 24, 25, 26, 27, 28, 33, 34, 35, 36, 37, 39], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Convert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [40], "excluded_lines": [], "start_line": 16, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 13, 16, 18, 24, 25, 26, 27, 28, 33, 34, 35, 36, 37, 39], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es.py": {"executed_lines": [1, 3, 4, 5, 7, 9, 12, 13, 14, 15, 16, 25, 26, 27, 29, 33, 35, 44, 46, 51, 56, 63, 64, 65, 66, 67, 72, 74, 76, 79, 80, 82, 99, 101], "summary": {"covered_lines": 34, "num_statements": 45, "percent_covered": 68.62745098039215, "percent_covered_display": "68.63", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 75.55555555555556, "percent_statements_covered_display": "75.56", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 5, "percent_branches_covered": 16.666666666666668, "percent_branches_covered_display": "16.67"}, "missing_lines": [102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 114], "excluded_lines": [], "executed_branches": [[65, 66]], "missing_branches": [[65, 67], [102, 103], [102, 106], [112, 113], [112, 114]], "functions": {"Converter.__init__": {"executed_lines": [64, 65, 66, 67], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 63, "executed_branches": [[65, 66]], "missing_branches": [[65, 67]]}, "Converter.layer_filter": {"executed_lines": [74], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 72, "executed_branches": [], "missing_branches": []}, "Converter.migrate": {"executed_lines": [79, 82, 99], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 76, "executed_branches": [], "missing_branches": []}, "Converter.migrate.part": {"executed_lines": [80], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 79, "executed_branches": [], "missing_branches": []}, "Converter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 114], "excluded_lines": [], "start_line": 101, "executed_branches": [], "missing_branches": [[102, 103], [102, 106], [112, 113], [112, 114]]}, "": {"executed_lines": [1, 3, 4, 5, 7, 9, 12, 13, 14, 15, 16, 25, 26, 27, 29, 33, 35, 44, 46, 51, 56, 63, 72, 76, 101], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [64, 65, 66, 67, 74, 79, 80, 82, 99], "summary": {"covered_lines": 9, "num_statements": 20, "percent_covered": 38.46153846153846, "percent_covered_display": "38.46", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 45.0, "percent_statements_covered_display": "45.00", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 5, "percent_branches_covered": 16.666666666666668, "percent_branches_covered_display": "16.67"}, "missing_lines": [102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 114], "excluded_lines": [], "start_line": 12, "executed_branches": [[65, 66]], "missing_branches": [[65, 67], [102, 103], [102, 106], [112, 113], [112, 114]]}, "": {"executed_lines": [1, 3, 4, 5, 7, 9, 12, 13, 14, 15, 16, 25, 26, 27, 29, 33, 35, 44, 46, 51, 56, 63, 72, 76, 101], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_an.py": {"executed_lines": [1, 3, 4, 7, 8, 20, 21, 22, 23, 33, 34, 36, 37, 48, 49, 50, 51, 53, 57, 64], "summary": {"covered_lines": 20, "num_statements": 29, "percent_covered": 64.51612903225806, "percent_covered_display": "64.52", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 68.96551724137932, "percent_statements_covered_display": "68.97", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [65, 66, 67, 69, 71, 72, 74, 75, 77], "excluded_lines": [], "executed_branches": [], "missing_branches": [[65, 66], [65, 69]], "functions": {"ANConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [65, 66, 67, 69, 71, 72, 74, 77], "excluded_lines": [], "start_line": 64, "executed_branches": [], "missing_branches": [[65, 66], [65, 69]]}, "ANConverter.get_urls.fname": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [75], "excluded_lines": [], "start_line": 74, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 20, 21, 22, 23, 33, 34, 36, 37, 48, 49, 50, 51, 53, 57, 64], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ANConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [65, 66, 67, 69, 71, 72, 74, 75, 77], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": [[65, 66], [65, 69]]}, "": {"executed_lines": [1, 3, 4, 7, 8, 20, 21, 22, 23, 33, 34, 36, 37, 48, 49, 50, 51, 53, 57, 64], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_ar.py": {"executed_lines": [1, 3, 5, 12, 13, 17, 20, 24, 25, 26, 27, 32, 35, 36, 37, 48, 49, 51, 55, 64, 66, 67, 82, 99, 100, 101, 102, 103, 106], "summary": {"covered_lines": 29, "num_statements": 48, "percent_covered": 51.724137931034484, "percent_covered_display": "51.72", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 60.416666666666664, "percent_statements_covered_display": "60.42", "num_branches": 10, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 9, "percent_branches_covered": 10.0, "percent_branches_covered_display": "10.00"}, "missing_lines": [68, 77, 79, 80, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 94, 95, 96, 97, 105], "excluded_lines": [], "executed_branches": [[102, 103]], "missing_branches": [[85, 86], [85, 93], [86, 85], [86, 87], [89, 90], [89, 91], [93, 94], [93, 95], [102, 105]], "functions": {"ARConverter.list_products": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [68, 77, 79, 80], "excluded_lines": [], "start_line": 67, "executed_branches": [], "missing_branches": []}, "ARConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 8, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 8, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 94, 95, 96, 97], "excluded_lines": [], "start_line": 82, "executed_branches": [], "missing_branches": [[85, 86], [85, 93], [86, 85], [86, 87], [89, 90], [89, 91], [93, 94], [93, 95]]}, "ARConverter.post_migrate": {"executed_lines": [100, 101, 102, 103, 106], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 75.0, "percent_covered_display": "75.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83.33", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [105], "excluded_lines": [], "start_line": 99, "executed_branches": [[102, 103]], "missing_branches": [[102, 105]]}, "": {"executed_lines": [1, 3, 5, 12, 13, 17, 20, 24, 25, 26, 27, 32, 35, 36, 37, 48, 49, 51, 55, 64, 66, 67, 82, 99], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ARConverter": {"executed_lines": [100, 101, 102, 103, 106], "summary": {"covered_lines": 5, "num_statements": 24, "percent_covered": 17.647058823529413, "percent_covered_display": "17.65", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 20.833333333333332, "percent_statements_covered_display": "20.83", "num_branches": 10, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 9, "percent_branches_covered": 10.0, "percent_branches_covered_display": "10.00"}, "missing_lines": [68, 77, 79, 80, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 94, 95, 96, 97, 105], "excluded_lines": [], "start_line": 20, "executed_branches": [[102, 103]], "missing_branches": [[85, 86], [85, 93], [86, 85], [86, 87], [89, 90], [89, 91], [93, 94], [93, 95], [102, 105]]}, "": {"executed_lines": [1, 3, 5, 12, 13, 17, 20, 24, 25, 26, 27, 32, 35, 36, 37, 48, 49, 51, 55, 64, 66, 67, 82, 99], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_base.py": {"executed_lines": [1, 3, 4, 7, 19, 21, 25, 33, 34, 35, 37, 38, 40, 41, 43, 45, 46, 47, 48, 49, 50], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"ESBaseConverter.__init__": {"executed_lines": [34, 35, 37, 40, 41], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 33, "executed_branches": [], "missing_branches": []}, "ESBaseConverter.__init__.code_filter": {"executed_lines": [38], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 37, "executed_branches": [], "missing_branches": []}, "ESBaseConverter.migrate": {"executed_lines": [45, 46, 47, 48, 49, 50], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 43, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 19, 21, 25, 33, 43], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESBaseConverter": {"executed_lines": [34, 35, 37, 38, 40, 41, 45, 46, 47, 48, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 19, 21, 25, 33, 43], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_cat.py": {"executed_lines": [1, 3, 4, 7, 9, 34, 35, 36, 37, 41, 42, 43, 44, 45, 48, 57, 58, 62, 64, 65, 67, 69, 70, 73, 74, 75, 76, 77, 78, 79, 80], "summary": {"covered_lines": 31, "num_statements": 32, "percent_covered": 94.11764705882354, "percent_covered_display": "94.12", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.875, "percent_statements_covered_display": "96.88", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [71], "excluded_lines": [], "executed_branches": [[70, 73]], "missing_branches": [[70, 71]], "functions": {"ESCatConverter.layer_filter": {"executed_lines": [65], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 64, "executed_branches": [], "missing_branches": []}, "ESCatConverter.migrate": {"executed_lines": [69, 70, 73, 74, 75, 76, 77, 78, 79, 80], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 84.61538461538461, "percent_covered_display": "84.62", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "90.91", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [71], "excluded_lines": [], "start_line": 67, "executed_branches": [[70, 73]], "missing_branches": [[70, 71]]}, "": {"executed_lines": [1, 3, 4, 7, 9, 34, 35, 36, 37, 41, 42, 43, 44, 45, 48, 57, 58, 62, 64, 67], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESCatConverter": {"executed_lines": [65, 69, 70, 73, 74, 75, 76, 77, 78, 79, 80], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 85.71428571428571, "percent_covered_display": "85.71", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 91.66666666666667, "percent_statements_covered_display": "91.67", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [71], "excluded_lines": [], "start_line": 7, "executed_branches": [[70, 73]], "missing_branches": [[70, 71]]}, "": {"executed_lines": [1, 3, 4, 7, 9, 34, 35, 36, 37, 41, 42, 43, 44, 45, 48, 57, 58, 62, 64, 67], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_cb.py": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 15, 16, 19, 20, 30, 31, 38, 39, 40, 45, 48], "summary": {"covered_lines": 19, "num_statements": 23, "percent_covered": 76.0, "percent_covered_display": "76.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 82.6086956521739, "percent_statements_covered_display": "82.61", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [49, 50, 51, 52], "excluded_lines": [], "executed_branches": [], "missing_branches": [[49, 50], [49, 51]], "functions": {"ESCBConverter.rest_layer_filter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [49, 50, 51, 52], "excluded_lines": [], "start_line": 48, "executed_branches": [], "missing_branches": [[49, 50], [49, 51]]}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 15, 16, 19, 20, 30, 31, 38, 39, 40, 45, 48], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESCBConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [49, 50, 51, 52], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": [[49, 50], [49, 51]]}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 15, 16, 19, 20, 30, 31, 38, 39, 40, 45, 48], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_cl.py": {"executed_lines": [1, 2, 4, 5, 7, 9, 12, 13, 14, 15, 16, 25, 26, 28, 35, 36, 38, 39, 40, 41, 42, 44, 50, 51, 52, 53, 55], "summary": {"covered_lines": 27, "num_statements": 36, "percent_covered": 73.80952380952381, "percent_covered_display": "73.81", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 75.0, "percent_statements_covered_display": "75.00", "num_branches": 6, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [56, 57, 58, 60, 61, 62, 63, 64, 67], "excluded_lines": [], "executed_branches": [[41, 42], [41, 53], [51, 41], [51, 52]], "missing_branches": [[56, 57], [56, 60]], "functions": {"ESCLConverter.download_files": {"executed_lines": [39, 40, 41, 42, 44, 50, 51, 52, 53], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 38, "executed_branches": [[41, 42], [41, 53], [51, 41], [51, 52]], "missing_branches": []}, "ESCLConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [56, 57, 58, 60, 61, 62, 63, 64, 67], "excluded_lines": [], "start_line": 55, "executed_branches": [], "missing_branches": [[56, 57], [56, 60]]}, "": {"executed_lines": [1, 2, 4, 5, 7, 9, 12, 13, 14, 15, 16, 25, 26, 28, 35, 36, 38, 55], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESCLConverter": {"executed_lines": [39, 40, 41, 42, 44, 50, 51, 52, 53], "summary": {"covered_lines": 9, "num_statements": 18, "percent_covered": 54.166666666666664, "percent_covered_display": "54.17", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 50.0, "percent_statements_covered_display": "50.00", "num_branches": 6, "num_partial_branches": 0, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [56, 57, 58, 60, 61, 62, 63, 64, 67], "excluded_lines": [], "start_line": 12, "executed_branches": [[41, 42], [41, 53], [51, 41], [51, 52]], "missing_branches": [[56, 57], [56, 60]]}, "": {"executed_lines": [1, 2, 4, 5, 7, 9, 12, 13, 14, 15, 16, 25, 26, 28, 35, 36, 38, 55], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_cm.py": {"executed_lines": [1, 3, 5, 6, 9, 10, 11, 12, 13, 18, 19, 20, 21, 31, 32, 33, 34, 40, 42, 43, 45], "summary": {"covered_lines": 21, "num_statements": 26, "percent_covered": 75.0, "percent_covered_display": "75.00", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 80.76923076923077, "percent_statements_covered_display": "80.77", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 50, 51, 56], "excluded_lines": [], "executed_branches": [], "missing_branches": [[46, 47], [46, 50]], "functions": {"ESCMConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 50, 51, 56], "excluded_lines": [], "start_line": 45, "executed_branches": [], "missing_branches": [[46, 47], [46, 50]]}, "": {"executed_lines": [1, 3, 5, 6, 9, 10, 11, 12, 13, 18, 19, 20, 21, 31, 32, 33, 34, 40, 42, 43, 45], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESCMConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 50, 51, 56], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": [[46, 47], [46, 50]]}, "": {"executed_lines": [1, 3, 5, 6, 9, 10, 11, 12, 13, 18, 19, 20, 21, 31, 32, 33, 34, 40, 42, 43, 45], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_cn.py": {"executed_lines": [1, 2, 4, 7, 8, 9, 10, 11, 27, 28, 29, 30, 34, 43, 44, 47, 52, 57, 58], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 4, 7, 8, 9, 10, 11, 27, 28, 29, 30, 34, 43, 44, 47, 52, 57, 58], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESCNConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 7, 8, 9, 10, 11, 27, 28, 29, 30, 34, 43, 44, 47, 52, 57, 58], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_ex.py": {"executed_lines": [1, 2, 4, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 29, 31, 36, 43], "summary": {"covered_lines": 17, "num_statements": 36, "percent_covered": 42.5, "percent_covered_display": "42.50", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 47.22222222222222, "percent_statements_covered_display": "47.22", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [32, 33, 34, 44, 45, 47, 49, 50, 51, 53, 54, 59, 60, 65, 66, 67, 68, 69, 70], "excluded_lines": [], "executed_branches": [], "missing_branches": [[44, 45], [44, 47], [59, 60], [59, 70]], "functions": {"EXConverter.migrate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [32, 33, 34], "excluded_lines": [], "start_line": 31, "executed_branches": [], "missing_branches": []}, "EXConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 16, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 16, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [44, 45, 47, 49, 50, 51, 53, 54, 59, 60, 65, 66, 67, 68, 69, 70], "excluded_lines": [], "start_line": 43, "executed_branches": [], "missing_branches": [[44, 45], [44, 47], [59, 60], [59, 70]]}, "": {"executed_lines": [1, 2, 4, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 29, 31, 36, 43], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"EXConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 19, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 4, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 4, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [32, 33, 34, 44, 45, 47, 49, 50, 51, 53, 54, 59, 60, 65, 66, 67, 68, 69, 70], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": [[44, 45], [44, 47], [59, 60], [59, 70]]}, "": {"executed_lines": [1, 2, 4, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 29, 31, 36, 43], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_ga.py": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 13, 14, 15, 16, 26, 27, 34, 35, 37, 41, 44], "summary": {"covered_lines": 18, "num_statements": 22, "percent_covered": 75.0, "percent_covered_display": "75.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 81.81818181818181, "percent_statements_covered_display": "81.82", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [42, 45, 46, 47], "excluded_lines": [], "executed_branches": [], "missing_branches": [[45, 46], [45, 47]], "functions": {"ESGAConverter.rest_layer_filter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [42], "excluded_lines": [], "start_line": 41, "executed_branches": [], "missing_branches": []}, "ESGAConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [45, 46, 47], "excluded_lines": [], "start_line": 44, "executed_branches": [], "missing_branches": [[45, 46], [45, 47]]}, "": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 13, 14, 15, 16, 26, 27, 34, 35, 37, 41, 44], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESGAConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [42, 45, 46, 47], "excluded_lines": [], "start_line": 5, "executed_branches": [], "missing_branches": [[45, 46], [45, 47]]}, "": {"executed_lines": [1, 2, 5, 6, 7, 8, 9, 13, 14, 15, 16, 26, 27, 34, 35, 37, 41, 44], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_ib.py": {"executed_lines": [1, 3, 4, 6, 11, 20, 21, 22, 23, 24, 27, 28, 29, 30, 40, 41, 42, 48, 54, 55, 59, 62], "summary": {"covered_lines": 22, "num_statements": 31, "percent_covered": 70.96774193548387, "percent_covered_display": "70.97", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 70.96774193548387, "percent_statements_covered_display": "70.97", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [13, 14, 15, 16, 17, 60, 63, 64, 65], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"snapshot_date": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [13, 14, 15, 16, 17], "excluded_lines": [], "start_line": 11, "executed_branches": [], "missing_branches": []}, "ESIBConverter.rest_layer_filter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [60], "excluded_lines": [], "start_line": 59, "executed_branches": [], "missing_branches": []}, "ESIBConverter.file_migration": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [63, 64, 65], "excluded_lines": [], "start_line": 62, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 11, 20, 21, 22, 23, 24, 27, 28, 29, 30, 40, 41, 42, 48, 54, 55, 59, 62], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESIBConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [60, 63, 64, 65], "excluded_lines": [], "start_line": 20, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 11, 20, 21, 22, 23, 24, 27, 28, 29, 30, 40, 41, 42, 48, 54, 55, 59, 62], "summary": {"covered_lines": 22, "num_statements": 27, "percent_covered": 81.48148148148148, "percent_covered_display": "81.48", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 81.48148148148148, "percent_statements_covered_display": "81.48", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [13, 14, 15, 16, 17], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_md.py": {"executed_lines": [1, 4, 5, 10, 11, 12, 13, 14, 15, 17, 26, 27, 30], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5, 10, 11, 12, 13, 14, 15, 17, 26, 27, 30], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESCLConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5, 10, 11, 12, 13, 14, 15, 17, 26, 27, 30], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_nc.py": {"executed_lines": [1, 2, 4, 5, 6, 7, 9, 12, 14, 15, 16, 17, 18, 19, 20, 21, 29, 32, 33, 35, 45, 64], "summary": {"covered_lines": 22, "num_statements": 43, "percent_covered": 43.13725490196079, "percent_covered_display": "43.14", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 51.16279069767442, "percent_statements_covered_display": "51.16", "num_branches": 8, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 8, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [37, 38, 39, 40, 46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 61, 62, 66, 68], "excluded_lines": [], "executed_branches": [], "missing_branches": [[46, 47], [46, 50], [53, -45], [53, 54], [55, 53], [55, 56], [57, 58], [57, 61]], "functions": {"NCConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [37, 38, 39, 40], "excluded_lines": [], "start_line": 35, "executed_branches": [], "missing_branches": []}, "NCConverter.prefill_cache": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 15, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 15, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 8, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 8, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 61, 62], "excluded_lines": [], "start_line": 45, "executed_branches": [], "missing_branches": [[46, 47], [46, 50], [53, -45], [53, 54], [55, 53], [55, 56], [57, 58], [57, 61]]}, "NCConverter.download_files": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [66, 68], "excluded_lines": [], "start_line": 64, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 9, 12, 14, 15, 16, 17, 18, 19, 20, 21, 29, 32, 33, 35, 45, 64], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"NCConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 8, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 8, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [37, 38, 39, 40, 46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 61, 62, 66, 68], "excluded_lines": [], "start_line": 12, "executed_branches": [], "missing_branches": [[46, 47], [46, 50], [53, -45], [53, 54], [55, 53], [55, 56], [57, 58], [57, 61]]}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 9, 12, 14, 15, 16, 17, 18, 19, 20, 21, 29, 32, 33, 35, 45, 64], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_pv.py": {"executed_lines": [1, 2, 3, 5, 8, 9, 15, 16, 17, 18, 25, 26, 27, 28, 36, 37, 38, 40], "summary": {"covered_lines": 18, "num_statements": 30, "percent_covered": 56.25, "percent_covered_display": "56.25", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 60.0, "percent_statements_covered_display": "60.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [41, 42, 43, 45, 47, 50, 51, 54, 55, 56, 59, 62], "excluded_lines": [], "executed_branches": [], "missing_branches": [[41, 42], [41, 45]], "functions": {"ESPVConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [41, 42, 43, 45, 47, 50, 51, 54, 55, 56, 59, 62], "excluded_lines": [], "start_line": 40, "executed_branches": [], "missing_branches": [[41, 42], [41, 45]]}, "": {"executed_lines": [1, 2, 3, 5, 8, 9, 15, 16, 17, 18, 25, 26, 27, 28, 36, 37, 38, 40], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESPVConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [41, 42, 43, 45, 47, 50, 51, 54, 55, 56, 59, 62], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": [[41, 42], [41, 45]]}, "": {"executed_lines": [1, 2, 3, 5, 8, 9, 15, 16, 17, 18, 25, 26, 27, 28, 36, 37, 38, 40], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/es_vc.py": {"executed_lines": [1, 2, 4, 6, 9, 10, 11, 12, 13, 14, 19, 20, 21, 22, 32, 33, 39, 41], "summary": {"covered_lines": 18, "num_statements": 26, "percent_covered": 64.28571428571429, "percent_covered_display": "64.29", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 69.23076923076923, "percent_statements_covered_display": "69.23", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [42, 43, 44, 46, 48, 49, 50, 54], "excluded_lines": [], "executed_branches": [], "missing_branches": [[42, 43], [42, 44]], "functions": {"ESVCConverter.get_urls": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [42, 43, 44, 46, 48, 49, 50, 54], "excluded_lines": [], "start_line": 41, "executed_branches": [], "missing_branches": [[42, 43], [42, 44]]}, "": {"executed_lines": [1, 2, 4, 6, 9, 10, 11, 12, 13, 14, 19, 20, 21, 22, 32, 33, 39, 41], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ESVCConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [42, 43, 44, 46, 48, 49, 50, 54], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": [[42, 43], [42, 44]]}, "": {"executed_lines": [1, 2, 4, 6, 9, 10, 11, 12, 13, 14, 19, 20, 21, 22, 32, 33, 39, 41], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/fi.py": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 18, 19, 20, 21, 30, 34, 36, 37, 39], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 18, 19, 20, 21, 30, 34, 36, 37, 39], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 18, 19, 20, 21, 30, 34, 36, 37, 39], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/fr.py": {"executed_lines": [1, 2, 4, 5, 6, 7, 8, 10, 11, 14, 16, 58, 61, 62, 64, 66, 67, 68, 69, 70, 71, 76, 79, 80, 82, 84, 85, 86, 87, 93, 95, 96, 97, 98, 100, 108, 109, 112, 114, 118], "summary": {"covered_lines": 40, "num_statements": 47, "percent_covered": 80.0, "percent_covered_display": "80.00", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 85.1063829787234, "percent_statements_covered_display": "85.11", "num_branches": 8, "num_partial_branches": 4, "covered_branches": 4, "missing_branches": 4, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [63, 72, 73, 74, 75, 81, 111], "excluded_lines": [], "executed_branches": [[62, 64], [71, 76], [80, 82], [109, 112]], "missing_branches": [[62, 63], [71, 72], [80, 81], [109, 111]], "functions": {"FRConverter.download_files": {"executed_lines": [61, 62, 64, 66, 67, 68, 69, 70, 71, 76, 79, 80, 82], "summary": {"covered_lines": 13, "num_statements": 19, "percent_covered": 64.0, "percent_covered_display": "64.00", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 68.42105263157895, "percent_statements_covered_display": "68.42", "num_branches": 6, "num_partial_branches": 3, "covered_branches": 3, "missing_branches": 3, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [63, 72, 73, 74, 75, 81], "excluded_lines": [], "start_line": 58, "executed_branches": [[62, 64], [71, 76], [80, 82]], "missing_branches": [[62, 63], [71, 72], [80, 81]]}, "FRConverter.migrate": {"executed_lines": [109, 112], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 60.0, "percent_covered_display": "60.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "66.67", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [111], "excluded_lines": [], "start_line": 108, "executed_branches": [[109, 112]], "missing_branches": [[109, 111]]}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 8, 10, 11, 14, 16, 58, 84, 85, 86, 87, 93, 95, 96, 97, 98, 100, 108, 114, 118], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"FRConverter": {"executed_lines": [61, 62, 64, 66, 67, 68, 69, 70, 71, 76, 79, 80, 82, 109, 112], "summary": {"covered_lines": 15, "num_statements": 22, "percent_covered": 63.333333333333336, "percent_covered_display": "63.33", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 68.18181818181819, "percent_statements_covered_display": "68.18", "num_branches": 8, "num_partial_branches": 4, "covered_branches": 4, "missing_branches": 4, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [63, 72, 73, 74, 75, 81, 111], "excluded_lines": [], "start_line": 14, "executed_branches": [[62, 64], [71, 76], [80, 82], [109, 112]], "missing_branches": [[62, 63], [71, 72], [80, 81], [109, 111]]}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 8, 10, 11, 14, 16, 58, 84, 85, 86, 87, 93, 95, 96, 97, 98, 100, 108, 114, 118], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/hr.py": {"executed_lines": [1, 3, 4, 6, 9, 10, 11, 15, 16, 17, 18, 27, 29, 33, 34, 36, 38, 68, 70, 106, 107, 108], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 6, 9, 10, 11, 15, 16, 17, 18, 27, 29, 33, 34, 36, 38, 68, 70, 106, 107, 108], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 9, 10, 11, 15, 16, 17, 18, 27, 29, 33, 34, 36, 38, 68, 70, 106, 107, 108], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/ie.py": {"executed_lines": [1, 2, 4, 5, 6, 7, 10, 11, 20, 21, 22, 23, 25, 26, 27, 28, 35, 37, 41, 43, 44, 46, 47, 48, 50, 52, 55, 63, 64], "summary": {"covered_lines": 29, "num_statements": 29, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"IEConverter.migrate": {"executed_lines": [43, 44, 46, 47, 48, 50], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 41, "executed_branches": [], "missing_branches": []}, "IEConverter.file_migration": {"executed_lines": [55], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 52, "executed_branches": [], "missing_branches": []}, "IEConverter.layer_filter": {"executed_lines": [64], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 63, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 10, 11, 20, 21, 22, 23, 25, 26, 27, 28, 35, 37, 41, 52, 63], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"IEConverter": {"executed_lines": [43, 44, 46, 47, 48, 50, 55, 64], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 10, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 10, 11, 20, 21, 22, 23, 25, 26, 27, 28, 35, 37, 41, 52, 63], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/india_10k.py": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 20, 21, 22, 23, 24, 29], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 20, 21, 22, 23, 24, 29], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"IndiaConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 8, 9, 10, 11, 12, 20, 21, 22, 23, 24, 29], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/it_1.py": {"executed_lines": [15, 17, 18, 20, 21, 24, 25, 26, 27, 28, 36, 40, 41, 42, 44, 45, 46, 47], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [15, 17, 18, 20, 21, 24, 25, 26, 27, 28, 36, 40, 41, 42, 44, 45, 46, 47], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 24, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [15, 17, 18, 20, 21, 24, 25, 26, 27, 28, 36, 40, 41, 42, 44, 45, 46, 47], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/jecam.py": {"executed_lines": [1, 2, 4, 5, 6, 8, 11, 12, 13, 14, 15, 16, 34, 35, 36, 37, 48, 51, 52, 61, 62, 63, 64, 65, 67, 68, 73, 75, 76, 77], "summary": {"covered_lines": 30, "num_statements": 30, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"JecamConvert.migrate": {"executed_lines": [62, 63, 64, 65, 67, 68, 73, 75, 76, 77], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 61, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 8, 11, 12, 13, 14, 15, 16, 34, 35, 36, 37, 48, 51, 52, 61], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"JecamConvert": {"executed_lines": [62, 63, 64, 65, 67, 68, 73, 75, 76, 77], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 11, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 8, 11, 12, 13, 14, 15, 16, 34, 35, 36, 37, 48, 51, 52, 61], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/jp.py": {"executed_lines": [1, 4, 5, 13, 14, 15, 16, 23, 24, 25, 27, 35, 38], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5, 13, 14, 15, 16, 23, 24, 25, 27, 35, 38], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"JPConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5, 13, 14, 15, 16, 23, 24, 25, 27, 35, 38], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/lacuna_labels.py": {"executed_lines": [1, 4, 5, 6, 7, 8, 9, 37, 38, 40, 42, 52], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5, 6, 7, 8, 9, 37, 38, 40, 42, 52], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"LacunaLabelsConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5, 6, 7, 8, 9, 37, 38, 40, 42, 52], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/lt.py": {"executed_lines": [1, 4, 5, 6, 7, 8, 10, 11, 12, 13], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5, 6, 7, 8, 10, 11, 12, 13], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"LTConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5, 6, 7, 8, 10, 11, 12, 13], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/lu.py": {"executed_lines": [1, 3, 6, 7, 10, 11, 12, 13, 18, 19, 20, 21], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 10, 11, 12, 13, 18, 19, 20, 21], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 10, 11, 12, 13, 18, 19, 20, 21], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/lv.py": {"executed_lines": [1, 3, 4, 6, 9, 10, 18, 19, 20, 21, 30, 31, 32, 33, 42, 49, 50, 53], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 6, 9, 10, 18, 19, 20, 21, 30, 31, 32, 33, 42, 49, 50, 53], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 9, 10, 18, 19, 20, 21, 30, 31, 32, 33, 42, 49, 50, 53], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/nl.py": {"executed_lines": [1, 2, 4, 5, 9, 12, 13, 14, 20, 21, 22, 23, 37, 41, 43, 53, 58, 62, 63, 64, 66], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 2, 4, 5, 9, 12, 13, 14, 20, 21, 22, 23, 37, 41, 43, 53, 58, 62, 63, 64, 66], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"NLCropConverter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 12, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 9, 12, 13, 14, 20, 21, 22, 23, 37, 41, 43, 53, 58, 62, 63, 64, 66], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/nl_block.py": {"executed_lines": [1, 3, 6, 7, 11, 12, 13, 14, 31, 33, 34, 35, 36, 40, 41, 42], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 6, 7, 11, 12, 13, 14, 31, 33, 34, 35, 36, 40, 41, 42], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 6, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 6, 7, 11, 12, 13, 14, 31, 33, 34, 35, 36, 40, 41, 42], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/nz.py": {"executed_lines": [1, 3, 4, 6, 7, 10, 11, 17, 23, 24, 25, 26, 35, 38, 39, 40, 41, 48, 49, 51, 52, 53, 54, 55, 63, 64, 67, 75, 77, 78, 79, 80], "summary": {"covered_lines": 32, "num_statements": 36, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 88.88888888888889, "percent_statements_covered_display": "88.89", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 3, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [42, 43, 44, 47], "excluded_lines": [], "executed_branches": [[40, 41], [40, 49], [41, 48]], "missing_branches": [[41, 42], [43, 44], [43, 47]], "functions": {"NZCropConverter.download_files": {"executed_lines": [38, 39, 40, 41, 48, 49], "summary": {"covered_lines": 6, "num_statements": 10, "percent_covered": 56.25, "percent_covered_display": "56.25", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 60.0, "percent_statements_covered_display": "60.00", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 3, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [42, 43, 44, 47], "excluded_lines": [], "start_line": 35, "executed_branches": [[40, 41], [40, 49], [41, 48]], "missing_branches": [[41, 42], [43, 44], [43, 47]]}, "NZCropConverter.migrate": {"executed_lines": [77, 78, 79, 80], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 75, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 10, 11, 17, 23, 24, 25, 26, 35, 51, 52, 53, 54, 55, 63, 64, 67, 75], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"NZCropConverter": {"executed_lines": [38, 39, 40, 41, 48, 49, 77, 78, 79, 80], "summary": {"covered_lines": 10, "num_statements": 14, "percent_covered": 65.0, "percent_covered_display": "65.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 71.42857142857143, "percent_statements_covered_display": "71.43", "num_branches": 6, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 3, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [42, 43, 44, 47], "excluded_lines": [], "start_line": 10, "executed_branches": [[40, 41], [40, 49], [41, 48]], "missing_branches": [[41, 42], [43, 44], [43, 47]]}, "": {"executed_lines": [1, 3, 4, 6, 7, 10, 11, 17, 23, 24, 25, 26, 35, 51, 52, 53, 54, 55, 63, 64, 67, 75], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/planet_afb.py": {"executed_lines": [2, 3, 5, 8, 9, 10, 15, 16, 17, 18, 23, 24, 25, 26, 27, 36, 37, 39], "summary": {"covered_lines": 18, "num_statements": 24, "percent_covered": 69.23076923076923, "percent_covered_display": "69.23", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 75.0, "percent_statements_covered_display": "75.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 48, 49, 50, 51], "excluded_lines": [], "executed_branches": [], "missing_branches": [[48, 49], [48, 51]], "functions": {"Converter.file_migration": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 48, 49, 50, 51], "excluded_lines": [], "start_line": 39, "executed_branches": [], "missing_branches": [[48, 49], [48, 51]]}, "": {"executed_lines": [2, 3, 5, 8, 9, 10, 15, 16, 17, 18, 23, 24, 25, 26, 27, 36, 37, 39], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [46, 47, 48, 49, 50, 51], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": [[48, 49], [48, 51]]}, "": {"executed_lines": [2, 3, 5, 8, 9, 10, 15, 16, 17, 18, 23, 24, 25, 26, 27, 36, 37, 39], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/pt.py": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 13, 14, 27, 28, 30, 33, 34, 43, 44, 45, 48, 49], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"PTConverter.layer_filter": {"executed_lines": [28], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 27, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 13, 14, 27, 30, 33, 34, 43, 44, 45, 48, 49], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"PTConverter": {"executed_lines": [28], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 9, 10, 11, 13, 14, 27, 30, 33, 34, 43, 44, 45, 48, 49], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/se.py": {"executed_lines": [1, 2, 4, 5, 8, 9, 17, 18, 19, 20, 28, 29, 30, 31, 38, 39, 40, 45, 46, 47], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.migrate": {"executed_lines": [46, 47], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 45, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 17, 18, 19, 20, 28, 29, 30, 31, 38, 39, 40, 45], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [46, 47], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 8, 9, 17, 18, 19, 20, 28, 29, 30, 31, 38, 39, 40, 45], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/si.py": {"executed_lines": [1, 3, 4, 7, 8, 14, 15, 16, 17, 22, 24, 26, 35, 36, 37, 38], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 3, 4, 7, 8, 14, 15, 16, 17, 22, 24, 26, 35, 36, 37, 38], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 7, 8, 14, 15, 16, 17, 22, 24, 26, 35, 36, 37, 38], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/sk.py": {"executed_lines": [1, 3, 4, 7, 8, 14, 15, 16, 17, 27, 28, 29, 32, 33, 41, 49, 50, 51, 52, 53, 54], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 95.65217391304348, "percent_covered_display": "95.65", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [[50, 51]], "missing_branches": [[50, 52]], "functions": {"Converter.migrate": {"executed_lines": [50, 51, 52, 53, 54], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 85.71428571428571, "percent_covered_display": "85.71", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 49, "executed_branches": [[50, 51]], "missing_branches": [[50, 52]]}, "": {"executed_lines": [1, 3, 4, 7, 8, 14, 15, 16, 17, 27, 28, 29, 32, 33, 41, 49], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [50, 51, 52, 53, 54], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 85.71428571428571, "percent_covered_display": "85.71", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 1, "covered_branches": 1, "missing_branches": 1, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [[50, 51]], "missing_branches": [[50, 52]]}, "": {"executed_lines": [1, 3, 4, 7, 8, 14, 15, 16, 17, 27, 28, 29, 32, 33, 41, 49], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/template.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 16, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 16, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [7, 13, 20, 45, 47, 49, 51, 62, 67, 72, 77, 84, 87, 93, 98, 117], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 16, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 16, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [7, 13, 20, 45, 47, 49, 51, 62, 67, 72, 77, 84, 87, 93, 98, 117], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 13, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 16, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 16, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [7, 13, 20, 45, 47, 49, 51, 62, 67, 72, 77, 84, 87, 93, 98, 117], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/us_ca_scm.py": {"executed_lines": [1, 3, 5, 6, 7, 10, 11, 23, 24, 25, 26, 27, 35, 36, 37, 44, 47, 48, 54, 58, 59, 60, 61, 62], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"Converter.migrate": {"executed_lines": [58, 59, 60, 61, 62], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 54, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 10, 11, 23, 24, 25, 26, 27, 35, 36, 37, 44, 47, 48, 54], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [58, 59, 60, 61, 62], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 10, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 10, 11, 23, 24, 25, 26, 27, 35, 36, 37, 44, 47, 48, 54], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/us_usda_cropland.py": {"executed_lines": [1, 2, 3, 5, 6, 7, 10, 13, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 47, 49, 58, 59, 60, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 78], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 97.61904761904762, "percent_covered_display": "97.62", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 1, "percent_branches_covered": 75.0, "percent_branches_covered_display": "75.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [[65, 66], [65, 70], [72, 73]], "missing_branches": [[72, 74]], "functions": {"Converter.migrate": {"executed_lines": [58, 59, 60, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 78], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 95.45454545454545, "percent_covered_display": "95.45", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 1, "percent_branches_covered": 75.0, "percent_branches_covered_display": "75.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 49, "executed_branches": [[65, 66], [65, 70], [72, 73]], "missing_branches": [[72, 74]]}, "": {"executed_lines": [1, 2, 3, 5, 6, 7, 10, 13, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 47, 49], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [58, 59, 60, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 78], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 95.45454545454545, "percent_covered_display": "95.45", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 4, "num_partial_branches": 1, "covered_branches": 3, "missing_branches": 1, "percent_branches_covered": 75.0, "percent_branches_covered_display": "75.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 10, "executed_branches": [[65, 66], [65, 70], [72, 73]], "missing_branches": [[72, 74]]}, "": {"executed_lines": [1, 2, 3, 5, 6, 7, 10, 13, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 47, 49], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/datasets/varda.py": {"executed_lines": [4, 7, 8, 9, 16, 17, 18, 19, 21, 22, 23, 25], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [4, 7, 8, 9, 16, 17, 18, 19, 21, 22, 23, 25], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Converter": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 7, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [4, 7, 8, 9, 16, 17, 18, 19, 21, 22, 23, 25], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/describe.py": {"executed_lines": [1, 2, 4, 5, 6, 8, 11, 12, 13, 19, 20, 22, 23, 25, 30], "summary": {"covered_lines": 15, "num_statements": 18, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83.33", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [14, 15, 17], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"DescribeFile.get_cli_callback": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [14, 17], "excluded_lines": [], "start_line": 13, "executed_branches": [], "missing_branches": []}, "DescribeFile.get_cli_callback.callback": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [15], "excluded_lines": [], "start_line": 14, "executed_branches": [], "missing_branches": []}, "DescribeFile.__init__": {"executed_lines": [20], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 19, "executed_branches": [], "missing_branches": []}, "DescribeFile._schema_to_dict": {"executed_lines": [23, 25, 30], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 22, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 8, 11, 12, 13, 19, 22], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"DescribeFile": {"executed_lines": [20, 23, 25, 30], "summary": {"covered_lines": 4, "num_statements": 7, "percent_covered": 57.142857142857146, "percent_covered_display": "57.14", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 57.142857142857146, "percent_statements_covered_display": "57.14", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [14, 15, 17], "excluded_lines": [], "start_line": 11, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 8, 11, 12, 13, 19, 22], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/fiboa/version.py": {"executed_lines": [1, 2, 4, 5, 6, 7, 10, 11, 14, 23, 24, 25, 28], "summary": {"covered_lines": 13, "num_statements": 17, "percent_covered": 68.42105263157895, "percent_covered_display": "68.42", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 76.47058823529412, "percent_statements_covered_display": "76.47", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [15, 16, 17, 20], "excluded_lines": [], "executed_branches": [], "missing_branches": [[16, 17], [16, 20]], "functions": {"get_fiboa_uri": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 10, "executed_branches": [], "missing_branches": []}, "is_supported": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [15, 16, 17, 20], "excluded_lines": [], "start_line": 14, "executed_branches": [], "missing_branches": [[16, 17], [16, 20]]}, "get_versions": {"executed_lines": [24, 25, 28], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 23, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 2, 4, 5, 6, 7, 10, 14, 23], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"": {"executed_lines": [1, 2, 4, 5, 6, 7, 10, 11, 14, 23, 24, 25, 28], "summary": {"covered_lines": 13, "num_statements": 17, "percent_covered": 68.42105263157895, "percent_covered_display": "68.42", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 76.47058823529412, "percent_statements_covered_display": "76.47", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [15, 16, 17, 20], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": [[16, 17], [16, 20]]}}}, "fiboa_cli/improve.py": {"executed_lines": [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 19, 20, 21, 32, 33, 37, 40, 41, 42, 44, 46, 47, 48, 49, 51, 53, 54, 55, 61, 63, 66, 68, 69, 70, 71, 73, 74, 77, 78, 81, 82, 83, 84, 86, 87, 88, 90, 92, 95, 101, 102, 103, 104, 105, 107, 108, 110, 111, 114, 115, 122, 123, 124, 127, 128, 130, 131, 132, 133, 136, 147, 148, 149, 151, 152, 155], "summary": {"covered_lines": 80, "num_statements": 86, "percent_covered": 85.83333333333333, "percent_covered_display": "85.83", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 93.02325581395348, "percent_statements_covered_display": "93.02", "num_branches": 34, "num_partial_branches": 11, "covered_branches": 23, "missing_branches": 11, "percent_branches_covered": 67.6470588235294, "percent_branches_covered_display": "67.65"}, "missing_lines": [22, 38, 75, 96, 99, 129], "excluded_lines": [], "executed_branches": [[37, 40], [44, 46], [44, 51], [47, 48], [68, 69], [68, 71], [74, 77], [86, 87], [86, 90], [87, 88], [95, 101], [103, 104], [103, 107], [104, 105], [114, 115], [114, 122], [122, 123], [127, 128], [128, 130], [130, 131], [132, 133], [132, 136], [147, 148]], "missing_branches": [[37, 38], [47, 51], [74, 75], [87, 86], [95, 96], [104, 103], [122, 127], [127, 130], [128, 129], [130, 136], [147, 151]], "functions": {"ImproveData.get_cli_args": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [22], "excluded_lines": [], "start_line": 21, "executed_branches": [], "missing_branches": []}, "ImproveData.improve_file": {"executed_lines": [37, 40, 41, 42, 44, 46, 47, 48, 49, 51, 53, 54, 55, 61], "summary": {"covered_lines": 14, "num_statements": 15, "percent_covered": 85.71428571428571, "percent_covered_display": "85.71", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 93.33333333333333, "percent_statements_covered_display": "93.33", "num_branches": 6, "num_partial_branches": 2, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [38], "excluded_lines": [], "start_line": 33, "executed_branches": [[37, 40], [44, 46], [44, 51], [47, 48]], "missing_branches": [[37, 38], [47, 51]]}, "ImproveData.improve": {"executed_lines": [66, 68, 69, 70, 71], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 63, "executed_branches": [[68, 69], [68, 71]], "missing_branches": []}, "ImproveData.add_hcat": {"executed_lines": [74, 77, 78, 81, 82, 83, 84, 86, 87, 88, 90], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83.33", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 91.66666666666667, "percent_statements_covered_display": "91.67", "num_branches": 6, "num_partial_branches": 2, "covered_branches": 4, "missing_branches": 2, "percent_branches_covered": 66.66666666666667, "percent_branches_covered_display": "66.67"}, "missing_lines": [75], "excluded_lines": [], "start_line": 73, "executed_branches": [[74, 77], [86, 87], [86, 90], [87, 88]], "missing_branches": [[74, 75], [87, 86]]}, "ImproveData.migrate_fiboa_2": {"executed_lines": [95, 101, 102, 103, 104, 105, 107, 108, 110, 111, 114, 115, 122, 123, 124, 127, 128, 130, 131, 132, 133, 136, 147, 148, 149, 151, 152], "summary": {"covered_lines": 27, "num_statements": 30, "percent_covered": 80.0, "percent_covered_display": "80.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 90.0, "percent_statements_covered_display": "90.00", "num_branches": 20, "num_partial_branches": 7, "covered_branches": 13, "missing_branches": 7, "percent_branches_covered": 65.0, "percent_branches_covered_display": "65.00"}, "missing_lines": [96, 99, 129], "excluded_lines": [], "start_line": 92, "executed_branches": [[95, 101], [103, 104], [103, 107], [104, 105], [114, 115], [114, 122], [122, 123], [127, 128], [128, 130], [130, 131], [132, 133], [132, 136], [147, 148]], "missing_branches": [[95, 96], [104, 103], [122, 127], [127, 130], [128, 129], [130, 136], [147, 151]]}, "": {"executed_lines": [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 19, 20, 21, 32, 33, 63, 73, 92, 155], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ImproveData": {"executed_lines": [37, 40, 41, 42, 44, 46, 47, 48, 49, 51, 53, 54, 55, 61, 66, 68, 69, 70, 71, 74, 77, 78, 81, 82, 83, 84, 86, 87, 88, 90, 95, 101, 102, 103, 104, 105, 107, 108, 110, 111, 114, 115, 122, 123, 124, 127, 128, 130, 131, 132, 133, 136, 147, 148, 149, 151, 152], "summary": {"covered_lines": 57, "num_statements": 63, "percent_covered": 82.47422680412372, "percent_covered_display": "82.47", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 90.47619047619048, "percent_statements_covered_display": "90.48", "num_branches": 34, "num_partial_branches": 11, "covered_branches": 23, "missing_branches": 11, "percent_branches_covered": 67.6470588235294, "percent_branches_covered_display": "67.65"}, "missing_lines": [22, 38, 75, 96, 99, 129], "excluded_lines": [], "start_line": 19, "executed_branches": [[37, 40], [44, 46], [44, 51], [47, 48], [68, 69], [68, 71], [74, 77], [86, 87], [86, 90], [87, 88], [95, 101], [103, 104], [103, 107], [104, 105], [114, 115], [114, 122], [122, 123], [127, 128], [128, 130], [130, 131], [132, 133], [132, 136], [147, 148]], "missing_branches": [[37, 38], [47, 51], [74, 75], [87, 86], [95, 96], [104, 103], [122, 127], [127, 130], [128, 129], [130, 136], [147, 151]]}, "ImproveData.add_hcat.HCAT": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 81, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 19, 20, 21, 32, 33, 63, 73, 92, 155], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/merge.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"MergeDatasets": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/publish.py": {"executed_lines": [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 24, 27, 32, 33, 34, 35, 36, 39, 40, 41, 46, 47, 67, 68, 74, 75, 76, 77, 79, 80, 84, 89, 90, 109, 110, 112, 113, 115, 116, 117, 118, 119, 122, 123, 124, 125, 130, 131, 132, 135, 136, 137, 140, 141, 142, 144, 145, 150, 154, 155, 158, 163, 164, 165, 167, 168, 169, 171, 172, 173, 174, 175, 184, 192, 193, 195, 196, 197, 202], "summary": {"covered_lines": 88, "num_statements": 121, "percent_covered": 66.44295302013423, "percent_covered_display": "66.44", "missing_lines": 33, "excluded_lines": 0, "percent_statements_covered": 72.72727272727273, "percent_statements_covered_display": "72.73", "num_branches": 28, "num_partial_branches": 9, "covered_branches": 11, "missing_branches": 17, "percent_branches_covered": 39.285714285714285, "percent_branches_covered_display": "39.29"}, "missing_lines": [48, 69, 70, 72, 81, 82, 85, 86, 87, 114, 127, 151, 152, 159, 203, 204, 207, 208, 209, 210, 212, 213, 214, 215, 228, 229, 230, 243, 244, 245, 246, 247, 248], "excluded_lines": [], "executed_branches": [[34, 35], [34, 36], [113, 115], [115, 116], [122, 123], [135, 136], [150, 154], [158, 163], [164, 165], [171, 172], [172, 173]], "missing_branches": [[85, -84], [85, 86], [113, 114], [115, 117], [122, 127], [135, 137], [150, 151], [158, 159], [164, 167], [171, 192], [172, 174], [203, 204], [203, 208], [208, 209], [208, 212], [245, 246], [245, 248]], "functions": {"multihash_sha256": {"executed_lines": [32, 33, 34, 35, 36], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 27, "executed_branches": [[34, 35], [34, 36]], "missing_branches": []}, "Publish.get_cli_args": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [48], "excluded_lines": [], "start_line": 47, "executed_branches": [], "missing_branches": []}, "Publish.get_cli_callback": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [69, 72], "excluded_lines": [], "start_line": 68, "executed_branches": [], "missing_branches": []}, "Publish.get_cli_callback.callback": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [70], "excluded_lines": [], "start_line": 69, "executed_branches": [], "missing_branches": []}, "Publish.__init__": {"executed_lines": [75, 76, 77, 79, 80], "summary": {"covered_lines": 5, "num_statements": 7, "percent_covered": 71.42857142857143, "percent_covered_display": "71.43", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 71.42857142857143, "percent_statements_covered_display": "71.43", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [81, 82], "excluded_lines": [], "start_line": 74, "executed_branches": [], "missing_branches": []}, "Publish.check_command": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 2, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [85, 86, 87], "excluded_lines": [], "start_line": 84, "executed_branches": [], "missing_branches": [[85, -84], [85, 86]]}, "Publish.publish": {"executed_lines": [109, 110, 112, 113, 115, 116, 117, 118, 119, 122, 123, 124, 125, 130, 131, 132, 135, 136, 137, 140, 141, 142], "summary": {"covered_lines": 22, "num_statements": 24, "percent_covered": 81.25, "percent_covered_display": "81.25", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 91.66666666666667, "percent_statements_covered_display": "91.67", "num_branches": 8, "num_partial_branches": 4, "covered_branches": 4, "missing_branches": 4, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [114, 127], "excluded_lines": [], "start_line": 90, "executed_branches": [[113, 115], [115, 116], [122, 123], [135, 136]], "missing_branches": [[113, 114], [115, 117], [122, 127], [135, 137]]}, "Publish.create_stac_collection": {"executed_lines": [145, 150, 154, 155, 158, 163, 164, 165, 167, 168, 169, 171, 172, 173, 174, 175, 184, 192, 193], "summary": {"covered_lines": 19, "num_statements": 22, "percent_covered": 75.0, "percent_covered_display": "75.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 86.36363636363636, "percent_statements_covered_display": "86.36", "num_branches": 10, "num_partial_branches": 5, "covered_branches": 5, "missing_branches": 5, "percent_branches_covered": 50.0, "percent_branches_covered_display": "50.00"}, "missing_lines": [151, 152, 159], "excluded_lines": [], "start_line": 144, "executed_branches": [[150, 154], [158, 163], [164, 165], [171, 172], [172, 173]], "missing_branches": [[150, 151], [158, 159], [164, 167], [171, 192], [172, 174]]}, "Publish.file_metadata": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 196, "executed_branches": [], "missing_branches": []}, "Publish.generate_pmtiles": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 19, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 6, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 6, "percent_branches_covered": 0.0, "percent_branches_covered_display": "0.00"}, "missing_lines": [203, 204, 207, 208, 209, 210, 212, 213, 214, 215, 228, 229, 230, 243, 244, 245, 246, 247, 248], "excluded_lines": [], "start_line": 202, "executed_branches": [], "missing_branches": [[203, 204], [203, 208], [208, 209], [208, 212], [245, 246], [245, 248]]}, "": {"executed_lines": [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 24, 27, 39, 40, 41, 46, 47, 67, 68, 74, 84, 89, 90, 144, 195, 196, 202], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"Publish": {"executed_lines": [75, 76, 77, 79, 80, 109, 110, 112, 113, 115, 116, 117, 118, 119, 122, 123, 124, 125, 130, 131, 132, 135, 136, 137, 140, 141, 142, 145, 150, 154, 155, 158, 163, 164, 165, 167, 168, 169, 171, 172, 173, 174, 175, 184, 192, 193, 197], "summary": {"covered_lines": 47, "num_statements": 80, "percent_covered": 52.83018867924528, "percent_covered_display": "52.83", "missing_lines": 33, "excluded_lines": 0, "percent_statements_covered": 58.75, "percent_statements_covered_display": "58.75", "num_branches": 26, "num_partial_branches": 9, "covered_branches": 9, "missing_branches": 17, "percent_branches_covered": 34.61538461538461, "percent_branches_covered_display": "34.62"}, "missing_lines": [48, 69, 70, 72, 81, 82, 85, 86, 87, 114, 127, 151, 152, 159, 203, 204, 207, 208, 209, 210, 212, 213, 214, 215, 228, 229, 230, 243, 244, 245, 246, 247, 248], "excluded_lines": [], "start_line": 39, "executed_branches": [[113, 115], [115, 116], [122, 123], [135, 136], [150, 154], [158, 163], [164, 165], [171, 172], [172, 173]], "missing_branches": [[85, -84], [85, 86], [113, 114], [115, 117], [122, 127], [135, 137], [150, 151], [158, 159], [164, 167], [171, 192], [172, 174], [203, 204], [203, 208], [208, 209], [208, 212], [245, 246], [245, 248]]}, "": {"executed_lines": [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 24, 27, 32, 33, 34, 35, 36, 39, 40, 41, 46, 47, 67, 68, 74, 84, 89, 90, 144, 195, 196, 202], "summary": {"covered_lines": 41, "num_statements": 41, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [[34, 35], [34, 36]], "missing_branches": []}}}, "fiboa_cli/registry.py": {"executed_lines": [1, 3, 5, 8, 9, 10, 11, 12, 13, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 58, 59, 61, 62, 63, 66], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [[58, -27], [58, 59]], "missing_branches": [], "functions": {"FiboaRegistry.register_commands": {"executed_lines": [28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 58, 59], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 27, "executed_branches": [[58, -27], [58, 59]], "missing_branches": []}, "FiboaRegistry.get_default_collection": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 61, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 3, 5, 8, 9, 10, 11, 12, 13, 24, 25, 27, 61, 66], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"FiboaRegistry": {"executed_lines": [28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 58, 59, 62, 63], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 2, "num_partial_branches": 0, "covered_branches": 2, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 8, "executed_branches": [[58, -27], [58, 59]], "missing_branches": []}, "": {"executed_lines": [1, 3, 5, 8, 9, 10, 11, 12, 13, 24, 25, 27, 61, 66], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/rename_extension.py": {"executed_lines": [1, 4, 5, 6, 8, 9], "summary": {"covered_lines": 6, "num_statements": 9, "percent_covered": 66.66666666666667, "percent_covered_display": "66.67", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "66.67", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [10, 11, 13], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"RenameExtension.get_cli_callback": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [10, 13], "excluded_lines": [], "start_line": 9, "executed_branches": [], "missing_branches": []}, "RenameExtension.get_cli_callback.callback": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [11], "excluded_lines": [], "start_line": 10, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5, 6, 8, 9], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"RenameExtension": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0.00", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [10, 11, 13], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5, 6, 8, 9], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/validate.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ValidateData": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}, "fiboa_cli/validate_schema.py": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "executed_branches": [], "missing_branches": [], "functions": {"": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}, "classes": {"ValidateSchema": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 4, "executed_branches": [], "missing_branches": []}, "": {"executed_lines": [1, 4, 5], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100.00", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100.00", "num_branches": 0, "num_partial_branches": 0, "covered_branches": 0, "missing_branches": 0, "percent_branches_covered": 100.0, "percent_branches_covered_display": "100.00"}, "missing_lines": [], "excluded_lines": [], "start_line": 1, "executed_branches": [], "missing_branches": []}}}}, "totals": {"covered_lines": 2107, "num_statements": 2468, "percent_covered": 81.46306818181819, "percent_covered_display": "81.46", "missing_lines": 361, "excluded_lines": 0, "percent_statements_covered": 85.37277147487845, "percent_statements_covered_display": "85.37", "num_branches": 348, "num_partial_branches": 79, "covered_branches": 187, "missing_branches": 161, "percent_branches_covered": 53.735632183908045, "percent_branches_covered_display": "53.74"}} \ No newline at end of file diff --git a/fiboa_cli/conversion/duckdb.py b/fiboa_cli/conversion/duckdb.py index 7e770f0f..4cf714a3 100644 --- a/fiboa_cli/conversion/duckdb.py +++ b/fiboa_cli/conversion/duckdb.py @@ -233,10 +233,8 @@ def convert( self.warning(f"GeoParquet 1.1 post-processing failed: {e}") # canonical spatial ordering, same grid as the per-file merge - try: - from vecorel_cli.vecorel.hilbert import crs_total_bounds - except ImportError: - from .hilbert import crs_total_bounds + from vecorel_cli.vecorel.hilbert import crs_total_bounds + from .per_file import _ensure_hilbert_sorted with pq.ParquetFile(output_file) as pf: diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 9aa57f07..88830001 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -168,10 +168,7 @@ def merge_files( ) # Same Hilbert reference grid that the upstream sort used. - try: - from vecorel_cli.vecorel.hilbert import crs_total_bounds - except ImportError: - from .hilbert import crs_total_bounds + from vecorel_cli.vecorel.hilbert import crs_total_bounds total_bounds = crs_total_bounds(crs) @@ -186,7 +183,7 @@ def merge_files( n_resorted += 1 self.warning( f" {path}: was not Hilbert-sorted, re-sorted in place. " - "(Bump vecorel-cli to skip this rewrite next time.)" + "(Files written by vecorel-cli >= 0.2.16 arrive pre-sorted.)" ) if n_resorted: self.warning(f"Re-sorted {n_resorted}/{len(paths)} part file(s) before merging.") @@ -256,10 +253,7 @@ def _bounds_array_for_table(table: pa.Table, primary_col: str) -> np.ndarray: def _hilbert_keys_for_table(table: pa.Table, primary_col: str, total_bounds) -> np.ndarray: - try: - from vecorel_cli.vecorel.hilbert import hilbert_distances_from_bounds - except ImportError: - from fiboa_cli.conversion.hilbert import hilbert_distances_from_bounds + from vecorel_cli.vecorel.hilbert import hilbert_distances_from_bounds bounds = _bounds_array_for_table(table, primary_col) return hilbert_distances_from_bounds(bounds, total_bounds) @@ -316,7 +310,10 @@ def _ensure_hilbert_sorted( write_kwargs["compression_level"] = compression_level if row_group_size is not None: write_kwargs["row_group_size"] = row_group_size - pq.write_table(sorted_table, path, **write_kwargs) + # store_schema=False: the widened large_* arrow types must not be embedded, + # or the rewritten file stops schema-matching untouched siblings on merge + # (parquet's physical types are identical either way) + pq.write_table(sorted_table, path, store_schema=False, **write_kwargs) return True diff --git a/fiboa_cli/publish.py b/fiboa_cli/publish.py index 9dced76e..c4fa411e 100644 --- a/fiboa_cli/publish.py +++ b/fiboa_cli/publish.py @@ -214,14 +214,10 @@ def ensure_spatial_order(self, parquet_file: Path): import json as _json import pyarrow.parquet as _pq + from vecorel_cli.vecorel.hilbert import crs_total_bounds from .conversion.per_file import _ensure_hilbert_sorted - try: - from vecorel_cli.vecorel.hilbert import crs_total_bounds - except ImportError: - from .conversion.hilbert import crs_total_bounds - with _pq.ParquetFile(parquet_file) as pf: meta = pf.schema_arrow.metadata or {} if b"geo" not in meta: diff --git a/pixi.lock b/pixi.lock index 3bb3036b..f95ec030 100644 --- a/pixi.lock +++ b/pixi.lock @@ -176,7 +176,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-64: @@ -340,7 +340,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-arm64: @@ -505,7 +505,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ win-64: @@ -668,7 +668,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl - pypi: ./ @@ -823,7 +823,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-64: @@ -962,7 +962,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-arm64: @@ -1102,7 +1102,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ win-64: @@ -1241,7 +1241,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl - pypi: ./ @@ -1358,7 +1358,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-64: @@ -1460,7 +1460,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-arm64: @@ -1563,7 +1563,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ win-64: @@ -1663,7 +1663,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl - pypi: ./ @@ -1807,7 +1807,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-64: @@ -1936,7 +1936,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ osx-arm64: @@ -2066,7 +2066,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: ./ win-64: @@ -2192,7 +2192,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl - pypi: ./ @@ -2912,9 +2912,9 @@ packages: - pypi: ./ name: fiboa-cli version: 0.21.0 - sha256: 029b84f127d79447749ed576962a1d426f57f2c15f02ede916455266078600d9 + sha256: 2ff8ea0441a99c9090278f65a43782ef06708d2993eaa1935e92df104f8892d1 requires_dist: - - vecorel-cli==0.2.15 + - vecorel-cli==0.2.16 - beautifulsoup4>=4.12 - spdx-license-list==3.27.0 - duckdb==1.4.2 @@ -7191,10 +7191,10 @@ packages: purls: [] size: 115235 timestamp: 1767320173250 -- pypi: https://files.pythonhosted.org/packages/da/b6/ba9cfde9eb83094a6642a42f036b90f74dcff9abdd307cbdd21dc996e562/vecorel_cli-0.2.15-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/b3/34/90f99cc38c7feda78f1a9f2543b7bd42d111c425179c97851a07863102f5/vecorel_cli-0.2.16-py3-none-any.whl name: vecorel-cli - version: 0.2.15 - sha256: b8e297b5967521b994dc7342aa72cf43cc0a0326609d8e7c4afb32d09c9f786c + version: 0.2.16 + sha256: 700ea019992979bbd1cdc0be5778aff670739c1c34e6d40bb87daa3352ecb572 requires_dist: - pyyaml>=6.0,<7.0 - click>=8.1,<9.0 @@ -7204,7 +7204,7 @@ packages: - numpy>=2.0,<3.0 - pyarrow>=21.0,<24.0 - py7zr>=1.0,<2.0 - - fsspec==2025.7.0 + - fsspec>=2025.7.0 - jsonschema[format]>=4.20,<5.0 - aiohttp>=3.9,<4.0 - yarl>=1.20,<2.0 @@ -7212,6 +7212,10 @@ packages: - semantic-version>=2.10.0,<3.0 - json-stream>=2.3.0,<3.0 - loguru==0.7.3 + - s3fs>=2025.7.0 ; extra == 's3' + - gcsfs>=2025.7.0 ; extra == 'gcs' + - s3fs>=2025.7.0 ; extra == 'cloud' + - gcsfs>=2025.7.0 ; extra == 'cloud' requires_python: '>=3.11' - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.36.1-pyhd8ed1ab_0.conda sha256: fa0a21fdcd0a8e6cf64cc8cd349ed6ceb373f09854fd3c4365f0bc4586dccf9a diff --git a/pyproject.toml b/pyproject.toml index 4aa86aac..6374f574 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] requires-python = ">=3.11" dependencies = [ - "vecorel-cli==0.2.15", + "vecorel-cli==0.2.16", "beautifulsoup4>=4.12", "spdx-license-list==3.27.0", "duckdb==1.4.2", diff --git a/tests/test_per_file.py b/tests/test_per_file.py index 3bf9d8a5..b40c6b6a 100644 --- a/tests/test_per_file.py +++ b/tests/test_per_file.py @@ -8,9 +8,9 @@ import numpy as np import pyarrow.parquet as pq import pytest +from vecorel_cli.vecorel.hilbert import crs_total_bounds, hilbert_distances_from_bounds from fiboa_cli import Registry # noqa: F401 -from fiboa_cli.conversion.hilbert import crs_total_bounds, hilbert_distances_from_bounds from fiboa_cli.convert import ConvertData from fiboa_cli.datasets.es import Converter as ESConverter From 4688a000afd5af19d433f684ce434634828ed076 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 30 Aug 2026 20:41:53 +0200 Subject: [PATCH 72/94] EE: the 2010-2015 WFS layers are published empty; offer 2016-2024 Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/ee.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/ee.py b/fiboa_cli/datasets/ee.py index 1ec6d66b..b8a9306e 100644 --- a/fiboa_cli/datasets/ee.py +++ b/fiboa_cli/datasets/ee.py @@ -19,7 +19,9 @@ class Convert(AddHCATMixin, FiboaBaseConverter): str(year): { f"https://kls.pria.ee/geoserver/inspire_gsaa/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=inspire_gsaa:LU.GSAA.AGRICULTURAL_PARCELS_{year}&propertyName={ATTRIBUTES}": f"ee_gsaa_{year}.gml" } - for year in range(2024, 2009, -1) + # the WFS also lists layers for 2010-2015, but they return zero + # features (checked 2026-08-30) + for year in range(2024, 2015, -1) } ec_mapping_csv = "https://fiboa.org/code/ee/ee.csv" id = "ee" From bbee51e0eef1ad8ba9113cc1e08ed398ebc4803b Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 30 Aug 2026 20:55:21 +0200 Subject: [PATCH 73/94] Resort rewrite: preserve schema metadata via batch-wise narrow-schema write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit store_schema=False silently drops ALL key-value metadata (geo, collection), not just the embedded arrow schema — the first production resort produced a file geopandas could not read. Write through a ParquetWriter against the original narrow schema instead, casting each sub-2GB batch back from the widened types; regression test asserts the resorted file keeps its metadata and narrow schema. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/per_file.py | 24 ++++++++++++++++-------- tests/test_per_file.py | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/fiboa_cli/conversion/per_file.py b/fiboa_cli/conversion/per_file.py index 88830001..43054241 100644 --- a/fiboa_cli/conversion/per_file.py +++ b/fiboa_cli/conversion/per_file.py @@ -286,9 +286,11 @@ def _ensure_hilbert_sorted( with pq.ParquetFile(path) as pf: table = pf.read() metadata = pf.schema_arrow.metadata + narrow_schema = table.schema.with_metadata(metadata) # int32 offsets of plain binary/string columns overflow when a take() - # concatenates >2 GB of chunks (large WKB columns); widen them first. - # Parquet's physical BYTE_ARRAY is identical either way. + # concatenates >2 GB of chunks (large WKB columns); widen them for the + # take, and cast each written batch back so the file keeps its original + # schema (Parquet's physical BYTE_ARRAY is identical either way). fields = [] widened = False for f in table.schema: @@ -308,12 +310,18 @@ def _ensure_hilbert_sorted( write_kwargs = {"compression": compression} if compression_level is not None: write_kwargs["compression_level"] = compression_level - if row_group_size is not None: - write_kwargs["row_group_size"] = row_group_size - # store_schema=False: the widened large_* arrow types must not be embedded, - # or the rewritten file stops schema-matching untouched siblings on merge - # (parquet's physical types are identical either way) - pq.write_table(sorted_table, path, store_schema=False, **write_kwargs) + # Write batch-wise against the ORIGINAL narrow schema: each batch is far + # below the int32 offset limit, so the down-cast is safe, the geo/collection + # metadata is preserved, and the rewritten file schema-matches untouched + # pre-sorted siblings during merges. + step = row_group_size or 131_072 + writer = pq.ParquetWriter(path, narrow_schema, **write_kwargs) + try: + for start in range(0, sorted_table.num_rows, step): + batch = sorted_table.slice(start, step) + writer.write_table(batch.cast(narrow_schema) if widened else batch) + finally: + writer.close() return True diff --git a/tests/test_per_file.py b/tests/test_per_file.py index b40c6b6a..a0d4b327 100644 --- a/tests/test_per_file.py +++ b/tests/test_per_file.py @@ -142,6 +142,21 @@ def test_merge_resorts_unsorted_part(tmp_path, capsys): rev = tbl.take(list(reversed(range(tbl.num_rows)))) pq.write_table(rev.cast(tbl.schema), shuffled) conv = ESConverter() + # resort the shuffled file directly first: the in-place rewrite must + # preserve schema metadata and the narrow (non-large) column types + from fiboa_cli.conversion.per_file import _ensure_hilbert_sorted + + resorted = _ensure_hilbert_sorted( + str(shuffled), "geometry", crs_total_bounds("EPSG:4258"), "zstd", None + ) + assert resorted is True + import json as _json + + with pq.ParquetFile(shuffled) as pf: + meta = pf.schema_arrow.metadata or {} + assert b"geo" in meta and b"collection" in meta + _json.loads(meta[b"geo"]) + assert pf.schema_arrow.equals(pq.ParquetFile(part).schema_arrow, check_metadata=False) merged = tmp_path / "merged.parquet" conv.merge_files(str(merged), [str(part), str(shuffled)], cleanup_parts=True) assert pq.ParquetFile(merged).metadata.num_rows == 2 * tbl.num_rows From 5ea3dc15c489a6cd5ca7593bd50ae4776ba22a35 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 30 Aug 2026 21:28:04 +0200 Subject: [PATCH 74/94] ES-CB: the REST service now also serves the 2025 SIGPAC layers Co-Authored-By: Claude Fable 5 --- fiboa_cli/datasets/es_cb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/es_cb.py b/fiboa_cli/datasets/es_cb.py index f6e0a52f..8fc8645b 100644 --- a/fiboa_cli/datasets/es_cb.py +++ b/fiboa_cli/datasets/es_cb.py @@ -35,7 +35,7 @@ class ESCBConverter(EsriRESTConverterMixin, ESBaseConverter): } } - variants = {str(year): str(year) for year in range(2024, 2010 - 1, -1)} + variants = {str(year): str(year) for year in range(2025, 2010 - 1, -1)} use_code_attribute = "USO_SIGPAC" use_variant_as_determination = True From 32171c7f40401d005cc127af93f66d974e82d36a Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Sun, 30 Aug 2026 23:22:53 +0200 Subject: [PATCH 75/94] REST converter: discover the qualified key on joined layers; use orderByFields Cantabria's 2010-2014 SIGPAC layers are server-side joins: every field is table-qualified, so where=OBJECTID>x returned 'Failed to execute query'. Probe the layer for the real key name and page on it. Also 'sortBy' is not an ArcGIS parameter (orderByFields is); it only worked on layers whose default order happens to be the key. es_cb strips the table prefixes after download, first occurrence wins. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/converter_rest.py | 29 +++++++++++++++++++++++--- fiboa_cli/datasets/es_cb.py | 14 +++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index 9a5b25e8..f09ada59 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -46,17 +46,40 @@ def get_data(self, paths, **kwargs): layer = self.rest_layer_filter(service_metadata["layers"]) page_size = service_metadata["maxRecordCount"] layer_url = f"{base_url}/{layer['id']}/query" + # Joined layers qualify every field with the table name; discover the + # real key field before paging on it ("OBJECTID" alone fails there). + probe = requests.get( + layer_url, + { + "f": "json", + "where": "1=1", + "outFields": "*", + "resultRecordCount": 1, + "returnGeometry": "false", + }, + ).json() + attribute = self.rest_attribute + if probe.get("features"): + names = list(probe["features"][0]["attributes"].keys()) + attribute = next( + (n for n in names if n == self.rest_attribute), + next( + (n for n in names if n.endswith("." + self.rest_attribute)), self.rest_attribute + ), + ) get_dict = self.rest_params | { "outFields": "*", "returnGeometry": "true", "f": "geojson", - "sortBy": self.rest_attribute, + # note: the ArcGIS parameter is orderByFields; "sortBy" was ignored + # and only worked on layers whose default order is the key anyway + "orderByFields": attribute, "resultRecordCount": page_size, } gdfs = [] last_id = -1 while True: - get_dict["where"] = f"{self.rest_attribute}>{last_id}" + get_dict["where"] = f"{attribute}>{last_id}" url = f"{layer_url}?{urlencode(get_dict)}" if cache_fs is not None: cache_file = os.path.join( @@ -88,7 +111,7 @@ def get_data(self, paths, **kwargs): ( c for c in data.columns - if c == self.rest_attribute or c.endswith("." + self.rest_attribute) + if c == attribute or c.endswith("." + self.rest_attribute) ), self.rest_attribute, ) diff --git a/fiboa_cli/datasets/es_cb.py b/fiboa_cli/datasets/es_cb.py index 8fc8645b..17a55395 100644 --- a/fiboa_cli/datasets/es_cb.py +++ b/fiboa_cli/datasets/es_cb.py @@ -45,6 +45,20 @@ class ESCBConverter(EsriRESTConverterMixin, ESBaseConverter): rest_base_url = "https://geoservicios.cantabria.es/inspire/rest/services/SIGPAC/MapServer" # rest_params = {"where": "USO_SIGPAC NOT IN ('AG','CA','ED','FO','IM','IS','IV','TH','ZC','ZU','ZV','MT')"} + def migrate(self, gdf): + # 2010-2014 are joined layers: fields arrive table-qualified + # (SIGPAC_2014_RECFE_ETRS89.PROVINCIA, SIGPAC_2014_ATRRE.USO_SIGPAC). + # Strip the prefixes, first occurrence wins (the geometry table leads). + if any("." in c for c in gdf.columns): + renames = {} + for c in gdf.columns: + base = c.rsplit(".", 1)[-1] + if base not in renames.values() and base not in gdf.columns: + renames[c] = base + gdf = gdf.rename(columns=renames) + gdf = gdf.loc[:, ~gdf.columns.duplicated()] + return super().migrate(gdf) + def rest_layer_filter(self, layers): if not self.variant: self.variant = next(iter(self.variants)) From 992f15b0d1e3b4c7bde0f3917a503ec4ab6814a8 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 31 Aug 2026 00:12:05 +0200 Subject: [PATCH 76/94] Pre-warm all schema URIs with retries before converting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vecorel.org/fiboa.org fail intermittently; a blip at write time killed conversions after 25-minute source downloads (four times this week). The needed URIs are known up front, so fetch them first — retrying with backoff, failing fast and cheap — and let load_file's per-process cache make the write itself network-free. Co-Authored-By: Claude Fable 5 --- fiboa_cli/conversion/fiboa_converter.py | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index 3d7016c5..747137c6 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -24,6 +24,35 @@ def __init__(self, *args, **kwargs): # "remove unlisted columns" step of the base converter. self.columns = {**self.columns, "determination:datetime": "determination:datetime"} + def convert(self, *args, **kwargs): + self._prewarm_schemas() + return super().convert(*args, **kwargs) + + def _prewarm_schemas(self): + """Fetch every schema this conversion will need before doing any real + work, with retries. The schema hosts (vecorel.org, fiboa.org) fail + intermittently; without this, a transient blip after a long source + download kills the conversion at the very last step. load_file caches + per process, so a successful pre-warm makes the write network-free.""" + import time + + from vecorel_cli.vecorel.util import load_file + from vecorel_cli.vecorel.version import vecorel_version + + uris = set(self.extensions) + uris.add(get_fiboa_uri()) + uris.add(f"https://vecorel.org/specification/v{vecorel_version}/schema.yaml") + for uri in sorted(uris): + for attempt in range(5): + try: + load_file(uri) + break + except Exception as e: + if attempt == 4: + raise RuntimeError(f"Cannot load schema {uri} after 5 attempts: {e}") from e + self.warning(f"Schema fetch failed ({uri}), retrying: {str(e)[:100]}") + time.sleep(2**attempt * 2) + def post_migrate(self, gdf): gdf = super().post_migrate(gdf) From 7147f9f3634682de89b9f22537d027e31e015549 Mon Sep 17 00:00:00 2001 From: Matthias Mohr Date: Mon, 31 Aug 2026 13:54:17 +0900 Subject: [PATCH 77/94] Fix pixi.lock --- pixi.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.lock b/pixi.lock index f95ec030..7e39bf1c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -2912,7 +2912,7 @@ packages: - pypi: ./ name: fiboa-cli version: 0.21.0 - sha256: 2ff8ea0441a99c9090278f65a43782ef06708d2993eaa1935e92df104f8892d1 + sha256: 8a003159fca90548059103ad97e25198c2a8068f2c6f6a9507f01d1e593ab063 requires_dist: - vecorel-cli==0.2.16 - beautifulsoup4>=4.12 From 6b5a3730cd3dcdd8ce85ee8e4bcbb0c88a7f87e1 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Mon, 31 Aug 2026 18:55:05 +0200 Subject: [PATCH 78/94] REST converter: page by id windows instead of server-side sort orderByFields costs ~100 s per request on joined layers (the server sorts the whole join every page), which made the Cantabria 2010-2014 layers take days. A range filter on the unique key answers in ~1 s and a window of page_size ids cannot overflow a page, so fetch min/max once (two sorted one-row queries) and page by half-open id windows, skipping empty ones. Pages cached by the old scheme are reused when their ids prove they cover a window exactly. A where filter in rest_params is now ANDed in instead of being clobbered by the paging clause. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PvUrMu5mhX3WEUoLLkm8TG --- fiboa_cli/conversion/converter_rest.py | 128 +++++++++++++++++-------- 1 file changed, 89 insertions(+), 39 deletions(-) diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index f09ada59..d94a7323 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -67,57 +67,107 @@ def get_data(self, paths, **kwargs): (n for n in names if n.endswith("." + self.rest_attribute)), self.rest_attribute ), ) + base_where = self.rest_params.get("where") + + # Page by half-open id windows instead of orderByFields + "id > last": + # server-side sorting costs ~100 s per request on joined layers, while a + # range filter on the indexed key answers in about a second. The key is + # unique, so a window of page_size ids cannot overflow a page; id gaps + # only produce empty windows, which are skipped. + min_id = self._rest_id_bound(layer_url, attribute, base_where, "ASC") + max_id = self._rest_id_bound(layer_url, attribute, base_where, "DESC") + get_dict = self.rest_params | { "outFields": "*", "returnGeometry": "true", "f": "geojson", - # note: the ArcGIS parameter is orderByFields; "sortBy" was ignored - # and only worked on layers whose default order is the key anyway - "orderByFields": attribute, - "resultRecordCount": page_size, } - gdfs = [] - last_id = -1 - while True: - get_dict["where"] = f"{attribute}>{last_id}" - url = f"{layer_url}?{urlencode(get_dict)}" + page = 0 + lo = min_id - 1 + while lo < max_id: + hi = lo + page_size + data = None if cache_fs is not None: - cache_file = os.path.join( - cache_folder, f"{self.id}_{layer['id']}_{last_id}.geojson" + data = self._window_from_legacy_cache( + cache_fs, cache_folder, layer["id"], lo, hi, page_size ) - if not cache_fs.exists(cache_file): - try: - with cache_fs.open(cache_file, mode="wb") as file: - stream_file(source_fs, url, file) - except Exception: - # A download that broke off must not survive as a cached page - if cache_fs.exists(cache_file): - cache_fs.rm(cache_file) - raise - url = cache_file + if data is None: + clause = f"{attribute}>{lo} AND {attribute}<={hi}" + get_dict["where"] = f"({base_where}) AND {clause}" if base_where else clause + url = f"{layer_url}?{urlencode(get_dict)}" + if cache_fs is not None: + cache_file = os.path.join( + cache_folder, f"{self.id}_{layer['id']}_r{lo}.geojson" + ) + if not cache_fs.exists(cache_file): + try: + with cache_fs.open(cache_file, mode="wb") as file: + stream_file(source_fs, url, file) + except Exception: + # A download that broke off must not survive as a cached page + if cache_fs.exists(cache_file): + cache_fs.rm(cache_file) + raise + url = cache_file + + try: + data = gpd.read_file(url) + except Exception as e: + # An error response from the server must not survive as a cached page + if cache_fs is not None and cache_fs.exists(url): + cache_fs.rm(url) + raise RuntimeError( + f"Could not read ids ({lo} ... {hi}] of {layer_url}: {e}" + ) from e + + lo = hi + if len(data) == 0: + continue + print(f"Read {len(data)} features, page {page} from ids ({hi - page_size} ... {hi}]") + page += 1 + yield data, base_url, base_url, layer["id"] + + def _rest_id_bound(self, layer_url, attribute, base_where, direction): + clause = f"{attribute}>-1" + response = requests.get( + layer_url, + { + "f": "json", + "where": f"({base_where}) AND {clause}" if base_where else clause, + "outFields": attribute, + "returnGeometry": "false", + "orderByFields": f"{attribute} {direction}", + "resultRecordCount": 1, + }, + ).json() + return int(next(iter(response["features"][0]["attributes"].values()))) + def _window_from_legacy_cache(self, cache_fs, cache_folder, layer_id, lo, hi, page_size): + """Pages cached by the old sorted paging are keyed by the previous page's + last id. On dense layers they coincide exactly with an id window, so reuse + one when its ids prove it covers (lo, hi] completely.""" + for key in [-1, lo] if lo == 0 else [lo]: + path = os.path.join(cache_folder, f"{self.id}_{layer_id}_{key}.geojson") + if not cache_fs.exists(path): + continue try: - data = gpd.read_file(url) - except Exception as e: - # An error response from the server must not survive as a cached page - if cache_fs is not None and cache_fs.exists(url): - cache_fs.rm(url) - raise RuntimeError(f"Could not read page {len(gdfs)} of {layer_url}: {e}") from e - print( - f"Read {len(data)} features, page {len(gdfs)} from [{data.iloc[0, 0]} ... {data.iloc[-1, 0]}]" - ) - # joined layers return the field as
. + data = gpd.read_file(path) + except Exception: + continue id_column = next( ( c for c in data.columns - if c == attribute or c.endswith("." + self.rest_attribute) + if c == self.rest_attribute or c.endswith("." + self.rest_attribute) ), - self.rest_attribute, + None, ) - last_id = data[id_column].values[-1] - - yield data, base_url, base_url, layer["id"] - - if not len(data) >= page_size: - break + if id_column is None or len(data) == 0: + continue + ids = data[id_column] + covers = (len(data) == page_size and ids.max() == hi) or ( + len(data) < page_size and ids.max() <= hi + ) + if ids.min() == lo + 1 and covers: + return data + return None From db5766026c75720e7fa89fc66eabef7024dd72fd Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 1 Sep 2026 09:04:39 +0200 Subject: [PATCH 79/94] ES-GA: editions 2014-2026, per-campaign column names, determination by variant ideg.xunta.gal serves SIXPAC_2014..SIXPAC_2026 (2010-2013 never existed, 2025/2026 were missing). The older campaigns differ: 2014 names the layer RECINTO with SUP_SIGPAC and no DN_OID, 2015 uses SUP_SIX/USO_SIX, 2020 lacks DN_OID but has IDGEOM. Rename per page in file_migration so the land-use filter and id checks see the canonical names; 2014 gets the SIGPAC recinto reference as id, 2020 the geometry id. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PvUrMu5mhX3WEUoLLkm8TG --- fiboa_cli/datasets/es_ga.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/fiboa_cli/datasets/es_ga.py b/fiboa_cli/datasets/es_ga.py index 0e2889a6..e0fbc674 100644 --- a/fiboa_cli/datasets/es_ga.py +++ b/fiboa_cli/datasets/es_ga.py @@ -31,15 +31,34 @@ class ESGAConverter(EsriRESTConverterMixin, ESBaseConverter): } } - variants = {str(year): str(year) for year in range(2024, 2010 - 1, -1)} + # ideg.xunta.gal serves one MapServer per campaign, SIXPAC_2014 .. SIXPAC_2026 (as of 2026-09) + variants = {str(year): str(year) for year in range(2026, 2014 - 1, -1)} use_code_attribute = "USO_SIGPAC" + use_variant_as_determination = True rest_base_url = ( "https://ideg.xunta.gal/servizos/rest/services/ParcelasCatastrais/SIXPAC_{year}/MapServer" ) + # The older campaigns name the same things differently: + # 2014: RECINTO layer, SUP_SIGPAC, no DN_OID (nor AGREGADO) + # 2015: SUP_SIX / USO_SIX + # 2020: no DN_OID, but IDGEOM (the geometry id, unique and never null) + file_renames = {"SUP_SIGPAC": "DN_SURFACE", "SUP_SIX": "DN_SURFACE", "USO_SIX": "USO_SIGPAC"} + def rest_layer_filter(self, layers): - return next(layer for layer in layers if "recintos" in layer["name"].lower()) + return next(layer for layer in layers if "recinto" in layer["name"].lower()) + + def file_migration(self, gdf, path, uri, layer): + gdf = gdf.rename(columns={k: v for k, v in self.file_renames.items() if k in gdf.columns}) + if "DN_OID" not in gdf.columns: + if "IDGEOM" in gdf.columns: + gdf["DN_OID"] = gdf["IDGEOM"] + else: + # 2014 has no surrogate id at all; the SIGPAC recinto reference is the identifier + parts = ["PROVINCIA", "MUNICIPIO", "ZONA", "POLIGONO", "PARCELA", "RECINTO"] + gdf["DN_OID"] = gdf[parts].astype(int).astype(str).agg("-".join, axis=1) + return gdf def get_urls(self): if not self.variant: From 48fe60e68e303621a75a06595092d685000b0dd5 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 1 Sep 2026 09:11:27 +0200 Subject: [PATCH 80/94] REST converter: key cached pages by service, not just layer id Every SIXPAC_ MapServer of es_ga has its Recintos layer at id 2, so pages of different years collided in the cache and later years silently re-read the first year's pages. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PvUrMu5mhX3WEUoLLkm8TG --- fiboa_cli/conversion/converter_rest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/conversion/converter_rest.py b/fiboa_cli/conversion/converter_rest.py index d94a7323..617223ea 100644 --- a/fiboa_cli/conversion/converter_rest.py +++ b/fiboa_cli/conversion/converter_rest.py @@ -1,4 +1,5 @@ import os +import re from urllib.parse import urlencode import geopandas as gpd @@ -82,6 +83,9 @@ def get_data(self, paths, **kwargs): "returnGeometry": "true", "f": "geojson", } + # Layer ids repeat across services (every SIXPAC_ MapServer has its + # Recintos layer at id 2), so the service must be part of the cache key. + service = re.sub(r"\W+", "_", base_url.rstrip("/").split("/rest/services/")[-1]) page = 0 lo = min_id - 1 while lo < max_id: @@ -97,7 +101,7 @@ def get_data(self, paths, **kwargs): url = f"{layer_url}?{urlencode(get_dict)}" if cache_fs is not None: cache_file = os.path.join( - cache_folder, f"{self.id}_{layer['id']}_r{lo}.geojson" + cache_folder, f"{self.id}_{service}_{layer['id']}_r{lo}.geojson" ) if not cache_fs.exists(cache_file): try: From af3bbe81251baac755787286abbb63abe61cf7db Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Tue, 1 Sep 2026 09:23:22 +0200 Subject: [PATCH 81/94] ES-GA: keep only mapped columns per page ~16k pages of 1000 features are concatenated per edition; the 20 unmapped attribute columns would otherwise stay in memory until the very end. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PvUrMu5mhX3WEUoLLkm8TG --- fiboa_cli/datasets/es_ga.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fiboa_cli/datasets/es_ga.py b/fiboa_cli/datasets/es_ga.py index e0fbc674..a75d345a 100644 --- a/fiboa_cli/datasets/es_ga.py +++ b/fiboa_cli/datasets/es_ga.py @@ -58,7 +58,9 @@ def file_migration(self, gdf, path, uri, layer): # 2014 has no surrogate id at all; the SIGPAC recinto reference is the identifier parts = ["PROVINCIA", "MUNICIPIO", "ZONA", "POLIGONO", "PARCELA", "RECINTO"] gdf["DN_OID"] = gdf[parts].astype(int).astype(str).agg("-".join, axis=1) - return gdf + # ~16k pages of 1000 features are concatenated per edition; the 20 unmapped + # attribute columns would otherwise sit in memory until the very end + return gdf[[c for c in self.columns if c in gdf.columns]] def get_urls(self): if not self.variant: From b322fa35826604999917534c11477801eeabdd13 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 2 Sep 2026 10:27:09 +0200 Subject: [PATCH 82/94] Drop empty/missing geometries; widen schema prewarm retries A null or empty geometry survives conversion but breaks the canonical Hilbert sort at the very last step (geopandas refuses hilbert_distance on such a GeoSeries), so a 90-minute read ends with nothing written. es_ga 2020-2022 each failed this way; the rate is about 1 in 250,000 features. Drop those rows under the same bounded max_dropped_share rule already used for the required non-null properties, so a converter that produces many of them still errors out. Also widen the schema prewarm budget from 5 attempts (~30 s) to 8 with a 60 s cap (~4 min): a vecorel.org blip outlasted the old budget and killed es_ga 2018 and 2019 outright. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PSwkoiouWUsspkFd9aQ2AZ --- fiboa_cli/conversion/fiboa_converter.py | 29 +++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/fiboa_cli/conversion/fiboa_converter.py b/fiboa_cli/conversion/fiboa_converter.py index 747137c6..95d1ce5c 100644 --- a/fiboa_cli/conversion/fiboa_converter.py +++ b/fiboa_cli/conversion/fiboa_converter.py @@ -42,16 +42,20 @@ def _prewarm_schemas(self): uris = set(self.extensions) uris.add(get_fiboa_uri()) uris.add(f"https://vecorel.org/specification/v{vecorel_version}/schema.yaml") + attempts = 8 for uri in sorted(uris): - for attempt in range(5): + for attempt in range(attempts): try: load_file(uri) break except Exception as e: - if attempt == 4: - raise RuntimeError(f"Cannot load schema {uri} after 5 attempts: {e}") from e + if attempt == attempts - 1: + raise RuntimeError( + f"Cannot load schema {uri} after {attempts} attempts: {e}" + ) from e self.warning(f"Schema fetch failed ({uri}), retrying: {str(e)[:100]}") - time.sleep(2**attempt * 2) + # ~4 min of tolerance: vecorel.org outages have outlasted a 30 s budget + time.sleep(min(2**attempt * 2, 60)) def post_migrate(self, gdf): gdf = super().post_migrate(gdf) @@ -74,6 +78,23 @@ def post_migrate(self, gdf): ) gdf = gdf[~nulls] + # A null or empty geometry cannot be validated, tiled or Hilbert-sorted + # (geopandas refuses hilbert_distance on such a GeoSeries, which fails the + # run at the very last step), so drop those rows under the same bounded + # rule as the required properties. + if gdf.active_geometry_name is not None: + geom = gdf.geometry + blank = geom.isna() | geom.is_empty + if blank.any(): + share = blank.mean() + if share > self.max_dropped_share: + raise ValueError( + f"{int(blank.sum())} of {len(gdf)} rows ({share:.1%}) have an empty or " + f"missing geometry; fix the converter instead of dropping them" + ) + self.warning(f"Dropping {int(blank.sum())} rows with an empty or missing geometry") + gdf = gdf[~blank] + gdf_area_key = next((k for k, v in self.columns.items() if v == AREA_KEY), None) if self.area_calculate_missing: # If CRS is not in meters, reproject to an equal-area projection for area calculation From 3ce10ae34148e9b3596dafcb6816ede9f501a5f8 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Wed, 2 Sep 2026 14:05:28 +0200 Subject: [PATCH 83/94] ES-GA: exclude PR (pasto arbustivo) before the 2023 campaign Galicia had no MT (matorral) code until 2023: scrub was coded PR, which the Spanish base filter keeps as grazing land. Sampling the cached pages shows PR at ~23% of features for every edition 2014-2022 and MT entirely absent, then the two swap from 2023 on (MT ~23%, PR ~1%). Keeping PR therefore left the pre-2023 editions about 75% larger than 2023+ (8.76M vs 4.97M fields for es_ga 2022 vs 2023) with no change on the ground. Exclude PR for campaigns before 2023 so the published series is comparable; 2023+ is untouched and still keeps the genuine shrub pasture. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PSwkoiouWUsspkFd9aQ2AZ --- fiboa_cli/datasets/es_ga.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fiboa_cli/datasets/es_ga.py b/fiboa_cli/datasets/es_ga.py index a75d345a..a861b82c 100644 --- a/fiboa_cli/datasets/es_ga.py +++ b/fiboa_cli/datasets/es_ga.py @@ -46,6 +46,23 @@ class ESGAConverter(EsriRESTConverterMixin, ESBaseConverter): # 2020: no DN_OID, but IDGEOM (the geometry id, unique and never null) file_renames = {"SUP_SIGPAC": "DN_SURFACE", "SUP_SIX": "DN_SURFACE", "USO_SIX": "USO_SIGPAC"} + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + base_filter = self.column_filters[self.use_code_attribute] + + def code_filter(col): + keep = base_filter(col) + # Galicia had no MT (matorral) code before the 2023 campaign: scrub was + # coded PR (pasto arbustivo), which the base filter keeps as grazing land. + # PR is ~23% of features up to 2022 but only ~1% once MT exists, so + # keeping it would leave the pre-2023 editions ~75% larger than 2023+ + # for no real change on the ground. Drop it for those campaigns. + if self.variant and int(self.variant) < 2023: + keep &= col != "PR" + return keep + + self.column_filters = {self.use_code_attribute: code_filter} + def rest_layer_filter(self, layers): return next(layer for layer in layers if "recinto" in layer["name"].lower()) From bb2e0bc5bf9005f2df3d6427620c4d25131e2c05 Mon Sep 17 00:00:00 2001 From: Ivor Bosloper Date: Thu, 3 Sep 2026 21:06:20 +0200 Subject: [PATCH 84/94] PT: add the 2025 edition, which changed shape IFAP restructured the file for 2025: the field boundaries moved from "Culturas_" layers into "T" ones (beside an empty "Culturas" container and a "Codes" lookup table), the crop code column is now PUN_CUL_CO, the crop name is gone, and the file is published in WGS 84 with Shape_Area and Shape_Length still computed in the source units -- degrees, so 2.7e-07 where 2023 says 3046 m2. Widen the layer filter to both shapes, rename the crop code back in migrate(), and recompute both metrics on EPSG:6933 when the file is geographic, the same projection `fiboa improve` uses to fill missing sizes. Take the determination date from the variant instead of the hard-coded 2023, now that a second edition exists; for 2023 that yields the same value it published before. Editions of one converter drift apart exactly like this, so the test list now accepts a "#