diff --git a/algorithm_catalog/compass_informatics/UHI_per_pixel/benchmark-scenarios/uhi_jja_udp.json b/algorithm_catalog/compass_informatics/UHI_per_pixel/benchmark-scenarios/uhi_jja_udp.json new file mode 100644 index 00000000..ab82294f --- /dev/null +++ b/algorithm_catalog/compass_informatics/UHI_per_pixel/benchmark-scenarios/uhi_jja_udp.json @@ -0,0 +1,26 @@ +[ + { + "id": "UHI_per_pixel", + "type": "openeo", + "description": "UHI_per_pixel test", + "backend": "https://openeo.dataspace.copernicus.eu/openeo/1.2", + "process_graph": { + "urban_heat_island": { + "process_id": "UHI_per_pixel", + "namespace": "https://openeo.dataspace.copernicus.eu/openeo/1.2/processes/u:776bee94-53de-49f5-88d6-c3660d927048/UHI_per_pixel", + "arguments": { + "spatial_extent": ["west": 7.546, "south": 44.989, "east": 7.781, "north": 45.145] + }, + "temporal_extent": [ + "2020-01-01", "2021-12-31" + ] + }, + "result": true + } + }, + "reference_data": { + }, + "reference_options": { + } + } +] diff --git a/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_udp.py b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_udp.py new file mode 100644 index 00000000..f6eeba99 --- /dev/null +++ b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_udp.py @@ -0,0 +1,223 @@ +""" +uhi_udp_register.py +==================== +Builds and stores the Urban Heat Island (UHI) User-Defined Process (UDP) +on the Copernicus OpenEO back-end. + +Usage +----- + python uhi_udp_register.py + +The UDP will be saved under the process id "UHI_per_pixel" in your account. +""" + +import numpy as np +import openeo +from openeo.api.process import Parameter + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- +LANDSAT_STAC = ( + "https://planetarycomputer.microsoft.com/api/stac/v1/collections/landsat-c2-l2" +) +CCI_STAC = ( + "https://planetarycomputer.microsoft.com/api/stac/v1/collections/esa-cci-lc" +) +CCI_URBAN_CLASS = 190 # ESA CCI LCCS class for artificial / urban surfaces +MAX_CLOUD_COVER = 5 # % +DEFAULT_KERNEL = 51 # pixels (~1.5 km radius at 30 m Landsat resolution) +UDP_ID = "UHI_per_pixel" + +# JJA intervals are pre-built for this range at registration time. +# Extend YEAR_RANGE_END if you need to process data beyond 2035. +YEAR_RANGE_START = 1985 +YEAR_RANGE_END = 2035 + +# CCI land cover year — fixed at registration time because load_stac cannot +# accept a runtime Parameter as a temporal_extent value (causes ValueError). +# Change this constant and re-register if you need a different CCI snapshot. +CCI_YEAR = 2018 + + +# --------------------------------------------------------------------------- +# Build the parameterised process graph +# --------------------------------------------------------------------------- + +def build_uhi_graph( + connection: openeo.Connection, + spatial_extent: Parameter, + temporal_extent: Parameter, + kernel_size: Parameter, +) -> openeo.DataCube: + """ + Construct the symbolic UHI process graph. + + spatial_extent, temporal_extent, and kernel_size are Parameter objects; + the openEO client serialises them as ``{"from_parameter": ""}`` nodes. + CCI year is baked in at registration time via the CCI_YEAR constant. + + Returns the final (result) DataCube node. + """ + + # ------------------------------------------------------------------ + # 1. Load Landsat TIRS B10 for the requested AOI + time window + # ------------------------------------------------------------------ + landsat_cube = connection.load_stac( + LANDSAT_STAC, + spatial_extent=spatial_extent, + temporal_extent=temporal_extent, + bands=["TIRS_B10"], + properties={"eo:cloud_cover": lambda v: v <= MAX_CLOUD_COVER}, + ) + + # Scale DN → °C (Landsat Collection-2 scale + offset, then K→°C) + lst_raw = landsat_cube.band("TIRS_B10") * 0.00341802 + 149.0 - 273.15 + + # ------------------------------------------------------------------ + # 2. JJA mean across all years in the temporal extent + # + # We cannot loop over a runtime Parameter to build per-year + # intervals dynamically, so instead we pre-build JJA intervals + # for a wide year range (YEAR_RANGE_START–YEAR_RANGE_END) at + # UDP registration time. The temporal_extent parameter already + # limits which Landsat scenes are loaded, so intervals that fall + # outside that window simply contribute no data and are ignored + # by the reducer. + # ------------------------------------------------------------------ + jja_intervals = [ + [f"{y}-06-01", f"{y}-09-01"] # left-closed, right-open → Jun/Jul/Aug + for y in range(YEAR_RANGE_START, YEAR_RANGE_END + 1) + ] + + lst = lst_raw.aggregate_temporal( + intervals=jja_intervals, + reducer="mean", + ).reduce_temporal(reducer="mean") + + # ------------------------------------------------------------------ + # 3. Load ESA CCI land cover for the requested year + # ------------------------------------------------------------------ + cci_raw = connection.load_stac( + CCI_STAC, + spatial_extent=spatial_extent, + temporal_extent=[f"{CCI_YEAR}-01-01", f"{CCI_YEAR}-12-31"], + bands=["lccs_class"], + ) + cci = cci_raw.reduce_temporal(reducer="first").resample_cube_spatial(lst) + + # ------------------------------------------------------------------ + # 4. Urban / rural masks + # CCI class 190 = Urban / artificial surfaces + # ------------------------------------------------------------------ + urban_mask = cci != CCI_URBAN_CLASS # True → urban + rural_mask = cci == CCI_URBAN_CLASS # True → rural + + rural_indicator = rural_mask * 1 # 1 = rural, 0 = urban + + # ------------------------------------------------------------------ + # 5. Rural-only LST (urban pixels → 0) + # ------------------------------------------------------------------ + rural_temp_zero = lst * rural_indicator + + # ------------------------------------------------------------------ + # 6. Moving-window local rural mean + # We cannot build a Python list from a runtime Parameter, so we + # use apply_kernel with a flat unit kernel whose size is fixed at + # registration time. If you need a dynamic kernel size at + # runtime, register separate UDPs or use apply_neighborhood. + # ------------------------------------------------------------------ + kernel = [[1] * DEFAULT_KERNEL for _ in range(DEFAULT_KERNEL)] + + rural_sum = rural_temp_zero.apply_kernel(kernel=kernel, factor=1, border=0) + rural_count = rural_indicator.apply_kernel(kernel=kernel, factor=1, border=0) + + local_rural_mean = rural_sum / rural_count + local_rural_mean = local_rural_mean.mask(rural_count == 0) + + # ------------------------------------------------------------------ + # 7. UHI = LST − local rural mean, clipped to urban pixels only + # ------------------------------------------------------------------ + local_uhi = lst - local_rural_mean + local_uhi_urban = local_uhi.mask(urban_mask) + + return local_uhi_urban + + +# --------------------------------------------------------------------------- +# Register the UDP +# --------------------------------------------------------------------------- + +def register_udp() -> None: + connection = openeo.connect( + "https://openeo.dataspace.copernicus.eu/openeo/1.2" + ).authenticate_oidc() + + # --- Declare parameters ------------------------------------------------ + spatial_extent = Parameter.spatial_extent( + name="spatial_extent", + description=( + "Bounding box of the area of interest as an object with " + "'west', 'south', 'east', 'north' keys (EPSG:4326)." + ), + ) + + temporal_extent = Parameter.temporal_interval( + name="temporal_extent", + description=( + "Date range over which to compute the JJA mean LST, " + "e.g. ['2015-01-01', '2024-12-31']. " + "All June-August periods within the range are averaged." + ), + # schema={"type": "array", "subtype": "temporal-interval"}, + default=["2018-01-01", "2023-12-31"], + ) + + kernel_size = Parameter.integer( + name="kernel_size", + description=( + "Side length (pixels) of the square moving-window kernel " + "used to estimate the local rural background temperature. " + "Must be odd. At 30 m Landsat resolution, 51 ≈ 1.5 km radius. " + "NOTE: the kernel is fixed at registration time (default 51). " + "Re-register with a different DEFAULT_KERNEL constant to change it." + ), + default=DEFAULT_KERNEL, + ) + + # --- Build graph ------------------------------------------------------- + result_cube = build_uhi_graph( + connection, + spatial_extent=spatial_extent, + temporal_extent=temporal_extent, + kernel_size=kernel_size, + ) + + # --- Store on the back-end --------------------------------------------- + udp = connection.save_user_defined_process( + user_defined_process_id=UDP_ID, + process_graph=result_cube, + parameters=[spatial_extent, temporal_extent, kernel_size], + summary="Urban Heat Island intensity (JJA mean LST - local rural background)", + description=( + "Computes per-pixel Urban Heat Island (UHI) intensity for a given " + "area and time period using Landsat 8/9 TIRS Band 10 Land Surface " + "Temperature (LST) and ESA CCI land cover.\n\n" + "Algorithm:\n" + " 1. Load Landsat TIRS B10, convert to °C.\n" + " 2. Aggregate to JJA (June-August) mean across all years in the " + " requested temporal extent.\n" + " 3. Load ESA CCI land cover; resample to LST grid.\n" + " 4. Separate urban (CCI=190) and rural pixels.\n" + " 5. Compute local rural mean LST with a moving-window kernel.\n" + " 6. UHI = LST_urban - local_rural_mean (urban pixels only).\n" + ), + public=True, # set True to share with other users + ) + + print(f"UDP '{UDP_ID}' successfully registered.") + print(udp.describe()) + + +if __name__ == "__main__": + register_udp() \ No newline at end of file diff --git a/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_udp_run.py b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_udp_run.py new file mode 100644 index 00000000..264eeb42 --- /dev/null +++ b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_udp_run.py @@ -0,0 +1,96 @@ +""" +uhi_udp_run.py +============== +Invokes the "uhi_jja" User-Defined Process (UDP) that was registered by +uhi_udp_register.py, submits a batch job, and downloads the result GeoTIFF. + +Usage +----- +Edit the PARAMETERS dict below (or import and call run_uhi_udp() directly), +then run: + + python uhi_udp_run.py + +Prerequisites +------------- + python uhi_udp_register.py # must be run first, once +""" + +import openeo + +OPENEO_URL = "https://openeo.dataspace.copernicus.eu/openeo/1.2" +UDP_ID = "UHI_per_pixel" + +# --------------------------------------------------------------------------- +# Edit these to define your job +# --------------------------------------------------------------------------- +# PARAMETERS = { +# "spatial_extent": { +# "west": -0.563, +# "south": 51.261, +# "east": 0.280, +# "north": 51.732, +# # "crs": "EPSG:4326", # optional, 4326 is the default +# }, +# "temporal_extent": ["2020-01-01", "2021-12-31"], +# "kernel_size": 71, # optional — uses the UDP default (51) +# # Note: CCI land cover year is fixed at UDP registration time (default 2018). +# # To change it, update CCI_YEAR in uhi_udp_register.py and re-register. +# } + +OUTPUT_FILE = r".\uhi_jja_result_torino.tif" +JOB_TITLE = "UHI_JJA_torino" + +# Uncomment for other cities: +# PARAMETERS = { # Paris +# "spatial_extent": {"west": 2.273, "south": 48.819, "east": 2.413, "north": 48.897}, +# "temporal_extent": ["2018-01-01", "2023-12-31"], +# } +PARAMETERS = { # Torino + "spatial_extent": {"west": 7.546, "south": 44.989, "east": 7.781, "north": 45.145}, + "temporal_extent": ["2020-01-01", "2021-12-31"], +} + +def run_uhi_udp( + parameters: dict = PARAMETERS, + output_file: str = OUTPUT_FILE, + job_title: str = JOB_TITLE, +) -> openeo.BatchJob: + """ + Submit a batch job that runs the UHI_per_pixel UDP and downloads the result. + + Parameters + ---------- + parameters : dict + Must contain at least ``spatial_extent`` and ``temporal_extent``. + ``cci_year`` and ``kernel_size`` are optional (UDP defaults apply). + output_file : str + Local path for the downloaded GeoTIFF. + job_title : str + Human-readable title shown in the back-end job manager. + + Returns + ------- + openeo.BatchJob + The completed batch job object. + """ + connection = openeo.connect(OPENEO_URL).authenticate_oidc() + + # Load the stored UDP and supply runtime parameter values + cube = connection.datacube_from_process( + process_id=UDP_ID, + namespace=r'https://openeo.dataspace.copernicus.eu/openeo/1.2/processes/u:776bee94-53de-49f5-88d6-c3660d927048/UHI_per_pixel', + **parameters, + ) + + job = cube.execute_batch( + outputfile=output_file, + out_format="GTiff", + title=job_title, + ) + job.get_results().download_files(target=output_file.replace(".tif", "")) + print(f"Results downloaded to '{output_file.replace('.tif', '')}/'") + return job + +run_uhi_udp() + diff --git a/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/london_thumbnail.png b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/london_thumbnail.png new file mode 100644 index 00000000..be0d2db3 Binary files /dev/null and b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/london_thumbnail.png differ diff --git a/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/uhi_jja_udp.json b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/uhi_jja_udp.json new file mode 100644 index 00000000..0515e93e --- /dev/null +++ b/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/uhi_jja_udp.json @@ -0,0 +1,831 @@ +{ + "description": "Computes per-pixel Urban Heat Island (UHI) intensity for a given area and time period using Landsat 8/9 TIRS Band 10 Land Surface Temperature (LST) and ESA CCI land cover.\n\nAlgorithm:\n 1. Load Landsat TIRS B10, convert to °C.\n 2. Aggregate to JJA (June-August) mean across all years in the requested temporal extent.\n 3. Load ESA CCI land cover; resample to LST grid.\n 4. Separate urban (CCI=190) and rural pixels.\n 5. Compute local rural mean LST with a moving-window kernel.\n 6. UHI = LST_urban - local_rural_mean (urban pixels only).\n", + "id": "UHI_per_pixel", + "links": [ + { + "href": "https://openeo.dataspace.copernicus.eu/openeo/1.2/processes/u:776bee94-53de-49f5-88d6-c3660d927048/UHI_per_pixel", + "rel": "canonical", + "title": "Public URL for user-defined process 'UHI_per_pixel'" + } + ], + "parameters": [ + { + "description": "Bounding box of the area of interest as an object with 'west', 'south', 'east', 'north' keys (EPSG:4326).", + "name": "spatial_extent", + "schema": [ + { + "properties": { + "base": { + "default": null, + "description": "Base (optional, lower left corner, coordinate axis 3).", + "type": [ + "number", + "null" + ] + }, + "crs": { + "anyOf": [ + { + "examples": [3857], + "minimum": 1000, + "subtype": "epsg-code", + "title": "EPSG Code", + "type": "integer" + }, + { + "subtype": "wkt2-definition", + "title": "WKT2", + "type": "string" + } + ], + "default": 4326, + "description": "Coordinate reference system of the extent, specified as as [EPSG code](http://www.epsg-registry.org/) or [WKT2 CRS string](http://docs.opengeospatial.org/is/18-010r7/18-010r7.html). Defaults to `4326` (EPSG code 4326) unless the client explicitly requests a different coordinate reference system." + }, + "east": { + "description": "East (upper right corner, coordinate axis 1).", + "type": "number" + }, + "height": { + "default": null, + "description": "Height (optional, upper right corner, coordinate axis 3).", + "type": [ + "number", + "null" + ] + }, + "north": { + "description": "North (upper right corner, coordinate axis 2).", + "type": "number" + }, + "south": { + "description": "South (lower left corner, coordinate axis 2).", + "type": "number" + }, + "west": { + "description": "West (lower left corner, coordinate axis 1).", + "type": "number" + } + }, + "required": [ + "west", + "south", + "east", + "north" + ], + "subtype": "bounding-box", + "title": "Bounding Box", + "type": "object" + }, + { + "description": "Limits the data cube to the bounding box of the given geometries in the vector data cube. For raster data, all pixels inside the bounding box that do not intersect with any of the polygons will be set to no data (`null`). Empty geometries are ignored.", + "dimensions": [ + { + "type": "geometry" + } + ], + "subtype": "datacube", + "title": "Vector data cube", + "type": "object" + }, + { + "description": "Don't filter spatially. All data is included in the data cube.", + "title": "No filter", + "type": "null" + } + ] + }, + { + "default": [ + "2018-01-01", + "2023-12-31" + ], + "description": "Date range over which to compute the JJA mean LST, e.g. ['2015-01-01', '2024-12-31']. All June-August periods within the range are averaged.", + "name": "temporal_extent", + "optional": true, + "schema": { + "items": { + "anyOf": [ + { + "format": "date-time", + "subtype": "date-time", + "type": "string" + }, + { + "format": "date", + "subtype": "date", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "maxItems": 2, + "minItems": 2, + "subtype": "temporal-interval", + "type": "array", + "uniqueItems": true + } + }, + { + "default": 51, + "description": "Side length (pixels) of the square moving-window kernel used to estimate the local rural background temperature. Must be odd. At 30 m Landsat resolution, 51 ≈ 1.5 km radius. NOTE: the kernel is fixed at registration time (default 51). Re-register with a different DEFAULT_KERNEL constant to change it.", + "name": "kernel_size", + "optional": true, + "schema": { + "type": "integer" + } + } + ], + "process_graph": { + "aggregatetemporal1": { + "arguments": { + "data": { + "from_node": "reducedimension1" + }, + "intervals": [ + [ + "1985-06-01", + "1985-09-01" + ], + [ + "1986-06-01", + "1986-09-01" + ], + [ + "1987-06-01", + "1987-09-01" + ], + [ + "1988-06-01", + "1988-09-01" + ], + [ + "1989-06-01", + "1989-09-01" + ], + [ + "1990-06-01", + "1990-09-01" + ], + [ + "1991-06-01", + "1991-09-01" + ], + [ + "1992-06-01", + "1992-09-01" + ], + [ + "1993-06-01", + "1993-09-01" + ], + [ + "1994-06-01", + "1994-09-01" + ], + [ + "1995-06-01", + "1995-09-01" + ], + [ + "1996-06-01", + "1996-09-01" + ], + [ + "1997-06-01", + "1997-09-01" + ], + [ + "1998-06-01", + "1998-09-01" + ], + [ + "1999-06-01", + "1999-09-01" + ], + [ + "2000-06-01", + "2000-09-01" + ], + [ + "2001-06-01", + "2001-09-01" + ], + [ + "2002-06-01", + "2002-09-01" + ], + [ + "2003-06-01", + "2003-09-01" + ], + [ + "2004-06-01", + "2004-09-01" + ], + [ + "2005-06-01", + "2005-09-01" + ], + [ + "2006-06-01", + "2006-09-01" + ], + [ + "2007-06-01", + "2007-09-01" + ], + [ + "2008-06-01", + "2008-09-01" + ], + [ + "2009-06-01", + "2009-09-01" + ], + [ + "2010-06-01", + "2010-09-01" + ], + [ + "2011-06-01", + "2011-09-01" + ], + [ + "2012-06-01", + "2012-09-01" + ], + [ + "2013-06-01", + "2013-09-01" + ], + [ + "2014-06-01", + "2014-09-01" + ], + [ + "2015-06-01", + "2015-09-01" + ], + [ + "2016-06-01", + "2016-09-01" + ], + [ + "2017-06-01", + "2017-09-01" + ], + [ + "2018-06-01", + "2018-09-01" + ], + [ + "2019-06-01", + "2019-09-01" + ], + [ + "2020-06-01", + "2020-09-01" + ], + [ + "2021-06-01", + "2021-09-01" + ], + [ + "2022-06-01", + "2022-09-01" + ], + [ + "2023-06-01", + "2023-09-01" + ], + [ + "2024-06-01", + "2024-09-01" + ], + [ + "2025-06-01", + "2025-09-01" + ], + [ + "2026-06-01", + "2026-09-01" + ], + [ + "2027-06-01", + "2027-09-01" + ], + [ + "2028-06-01", + "2028-09-01" + ], + [ + "2029-06-01", + "2029-09-01" + ], + [ + "2030-06-01", + "2030-09-01" + ], + [ + "2031-06-01", + "2031-09-01" + ], + [ + "2032-06-01", + "2032-09-01" + ], + [ + "2033-06-01", + "2033-09-01" + ], + [ + "2034-06-01", + "2034-09-01" + ], + [ + "2035-06-01", + "2035-09-01" + ] + ], + "reducer": { + "process_graph": { + "mean1": { + "arguments": { + "data": { + "from_parameter": "data" + } + }, + "process_id": "mean", + "result": true + } + } + } + }, + "process_id": "aggregate_temporal" + }, + "apply1": { + "arguments": { + "data": { + "from_node": "resamplecubespatial1" + }, + "process": { + "process_graph": { + "eq1": { + "arguments": { + "x": { + "from_parameter": "x" + }, + "y": 190 + }, + "process_id": "eq" + }, + "multiply2": { + "arguments": { + "x": { + "from_node": "eq1" + }, + "y": 1 + }, + "process_id": "multiply", + "result": true + } + } + } + }, + "process_id": "apply" + }, + "apply2": { + "arguments": { + "data": { + "from_node": "applykernel2" + }, + "process": { + "process_graph": { + "eq2": { + "arguments": { + "x": { + "from_parameter": "x" + }, + "y": 0 + }, + "process_id": "eq", + "result": true + } + } + } + }, + "process_id": "apply" + }, + "apply3": { + "arguments": { + "data": { + "from_node": "resamplecubespatial1" + }, + "process": { + "process_graph": { + "neq1": { + "arguments": { + "x": { + "from_parameter": "x" + }, + "y": 190 + }, + "process_id": "neq", + "result": true + } + } + } + }, + "process_id": "apply" + }, + "applykernel1": { + "arguments": { + "border": 0, + "data": { + "from_node": "mergecubes1" + }, + "factor": 1, + "kernel": [ + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + ], + "replace_invalid": 0 + }, + "process_id": "apply_kernel" + }, + "applykernel2": { + "arguments": { + "border": 0, + "data": { + "from_node": "apply1" + }, + "factor": 1, + "kernel": [ + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + ], + "replace_invalid": 0 + }, + "process_id": "apply_kernel" + }, + "loadstac1": { + "arguments": { + "bands": [ + "TIRS_B10" + ], + "properties": { + "eo:cloud_cover": { + "process_graph": { + "lte1": { + "arguments": { + "x": { + "from_parameter": "value" + }, + "y": 5 + }, + "process_id": "lte", + "result": true + } + } + } + }, + "spatial_extent": { + "from_parameter": "spatial_extent" + }, + "temporal_extent": { + "from_parameter": "temporal_extent" + }, + "url": "https://planetarycomputer.microsoft.com/api/stac/v1/collections/landsat-c2-l2" + }, + "process_id": "load_stac" + }, + "loadstac2": { + "arguments": { + "bands": [ + "lccs_class" + ], + "spatial_extent": { + "from_parameter": "spatial_extent" + }, + "temporal_extent": [ + "2018-01-01", + "2018-12-31" + ], + "url": "https://planetarycomputer.microsoft.com/api/stac/v1/collections/esa-cci-lc" + }, + "process_id": "load_stac" + }, + "mask1": { + "arguments": { + "data": { + "from_node": "mergecubes2" + }, + "mask": { + "from_node": "apply2" + } + }, + "process_id": "mask" + }, + "mask2": { + "arguments": { + "data": { + "from_node": "mergecubes3" + }, + "mask": { + "from_node": "apply3" + } + }, + "process_id": "mask", + "result": true + }, + "mergecubes1": { + "arguments": { + "cube1": { + "from_node": "reducedimension2" + }, + "cube2": { + "from_node": "apply1" + }, + "overlap_resolver": { + "process_graph": { + "multiply3": { + "arguments": { + "x": { + "from_parameter": "x" + }, + "y": { + "from_parameter": "y" + } + }, + "process_id": "multiply", + "result": true + } + } + } + }, + "process_id": "merge_cubes" + }, + "mergecubes2": { + "arguments": { + "cube1": { + "from_node": "applykernel1" + }, + "cube2": { + "from_node": "applykernel2" + }, + "overlap_resolver": { + "process_graph": { + "divide1": { + "arguments": { + "x": { + "from_parameter": "x" + }, + "y": { + "from_parameter": "y" + } + }, + "process_id": "divide", + "result": true + } + } + } + }, + "process_id": "merge_cubes" + }, + "mergecubes3": { + "arguments": { + "cube1": { + "from_node": "reducedimension2" + }, + "cube2": { + "from_node": "mask1" + }, + "overlap_resolver": { + "process_graph": { + "subtract2": { + "arguments": { + "x": { + "from_parameter": "x" + }, + "y": { + "from_parameter": "y" + } + }, + "process_id": "subtract", + "result": true + } + } + } + }, + "process_id": "merge_cubes" + }, + "reducedimension1": { + "arguments": { + "data": { + "from_node": "loadstac1" + }, + "dimension": "bands", + "reducer": { + "process_graph": { + "add1": { + "arguments": { + "x": { + "from_node": "multiply1" + }, + "y": 149 + }, + "process_id": "add" + }, + "arrayelement1": { + "arguments": { + "data": { + "from_parameter": "data" + }, + "index": 0 + }, + "process_id": "array_element" + }, + "multiply1": { + "arguments": { + "x": { + "from_node": "arrayelement1" + }, + "y": 0.00341802 + }, + "process_id": "multiply" + }, + "subtract1": { + "arguments": { + "x": { + "from_node": "add1" + }, + "y": 273.15 + }, + "process_id": "subtract", + "result": true + } + } + } + }, + "process_id": "reduce_dimension" + }, + "reducedimension2": { + "arguments": { + "data": { + "from_node": "aggregatetemporal1" + }, + "dimension": "t", + "reducer": { + "process_graph": { + "mean2": { + "arguments": { + "data": { + "from_parameter": "data" + } + }, + "process_id": "mean", + "result": true + } + } + } + }, + "process_id": "reduce_dimension" + }, + "reducedimension3": { + "arguments": { + "data": { + "from_node": "loadstac2" + }, + "dimension": "t", + "reducer": { + "process_graph": { + "first1": { + "arguments": { + "data": { + "from_parameter": "data" + } + }, + "process_id": "first", + "result": true + } + } + } + }, + "process_id": "reduce_dimension" + }, + "resamplecubespatial1": { + "arguments": { + "data": { + "from_node": "reducedimension3" + }, + "method": "near", + "target": { + "from_node": "reducedimension2" + } + }, + "process_id": "resample_cube_spatial" + } + }, + "public": true, + "summary": "Urban Heat Island intensity (JJA mean LST - local rural background)" +} diff --git a/algorithm_catalog/compass_informatics/UHI_per_pixel/records/UHI_per_pixel.json b/algorithm_catalog/compass_informatics/UHI_per_pixel/records/UHI_per_pixel.json new file mode 100644 index 00000000..8dc39c37 --- /dev/null +++ b/algorithm_catalog/compass_informatics/UHI_per_pixel/records/UHI_per_pixel.json @@ -0,0 +1,109 @@ +{ + "type": "Feature", + "stac_version": "1.0.0", + "id": "UHI_per_pixel", + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core", + "https://apex.esa.int/core/openeo-udp" + ], + "properties": { + "created": "2026-03-06T00:00:00Z", + "updated": "2026-03-06T00:00:00Z", + "type": "service", + "title": "Urban Heat Island Intensity (JJA)", + "description": "This openEO User Defined Process (UDP) computes Urban Heat Island (UHI) intensity during the summer months (June–August). It processes satellite-derived Land Surface Temperature data and compares urban pixels with surrounding rural reference areas using a configurable kernel size.", + "keywords": [ + "Urban Heat Island", + "Land Surface Temperature", + "Climate" + ], + "visibility": "public", + "license": "Apache-2.0", + "cost_estimate": 1, + "cost_units": "credits", + "formats": [ + { + "name": "GeoTIFF" + } + ], + "contacts": [ + { + "name": "Adriaan Keurhorst", + "email": "adriaan.keurhorst@compass.ie", + "role": "author" + } + ], + "themes": [ + { + "concepts": [ + { + "id": "Urban Heat Island" + } + ], + "scheme": "https://gcmd.earthdata.nasa.gov/kms/concepts/concept_scheme/sciencekeywords" + } + ] + }, + "links": [ + { + "rel": "platform", + "href": "../../../../platform_catalog/cdse_openeo_federation.json", + "type": "application/json", + "title": "Copernicus Data Space openEO Backend" + }, + { + "rel": "provider", + "type": "application/json", + "title": "Eurac Research", + "href": "../../record.json" + }, + { + "rel": "thumbnail", + "href": "https://github.com/adriaanzico/gtif-urban-heat-island/blob/main/preview/london_thumbnail.png", + "type": "image/png", + "title": "Thumbnail preview of UHI Intensity in London" + }, + { + "rel": "preview", + "href": "https://github.com/adriaanzico/gtif-urban-heat-island/blob/main/preview/london_thumbnail.png", + "type": "image/png", + "title": "Preview of UHI Intensity in London" + }, + { + "rel": "application", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/urban_heat_preview/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_per_pixel.json", + "type": "application/json", + "title": "openEO UDP definition" + }, + { + "rel": "service", + "href": "https://openeofed.dataspace.copernicus.eu/", + "type": "application/json", + "title": "openEO Backend" + }, + { + "rel": "about", + "href": "https://github.com/adriaanzico/gtif-urban-heat-island", + "title": "Algorithm Documentation", + "type": "text/html" + }, + { + "rel": "code", + "href": "https://github.com/adriaanzico/gtif-urban-heat-island/blob/main/udp/UHI_udp.py", + "title": "Source Code", + "type": "text/html" + }, + { + "rel": "license", + "href": "https://opensource.org/licenses/Apache-2.0", + "title": "License", + "type": "text/html" + }, + { + "rel": "webapp", + "type": "text/html", + "title": "OpenEO Web Editor", + "href": "https://editor.openeo.org/?wizard=UDP&wizard~process=bap_composite&wizard~processUrl=https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/urban_heat_preview/algorithm_catalog/compass_informatics/UHI_per_pixel/openeo_udp/UHI_per_pixel.json&server=https://openeofed.dataspace.copernicus.eu" + } + ] +} \ No newline at end of file diff --git a/algorithm_catalog/compass_informatics/readme.md b/algorithm_catalog/compass_informatics/readme.md new file mode 100644 index 00000000..15da899d --- /dev/null +++ b/algorithm_catalog/compass_informatics/readme.md @@ -0,0 +1,2 @@ +# gtif-urban-heat-island +Urban Heat Island (UHI) intensity during the summer months (June–August). It processes satellite-derived Land Surface Temperature data and compares urban pixels with surrounding rural reference areas using a configurable kernel size. diff --git a/algorithm_catalog/compass_informatics/record.json b/algorithm_catalog/compass_informatics/record.json new file mode 100644 index 00000000..39b005c8 --- /dev/null +++ b/algorithm_catalog/compass_informatics/record.json @@ -0,0 +1,54 @@ +{ + "id": "compass_informatics", + "type": "Feature", + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core" + ], + "properties": { + "created": "2025-12-03T13:00:00Z", + "updated": "2025-12-03T13:00:00Z", + "type": "algorithm_provider", + "title": "Compass Informatics", + "description": "", + "keywords": [], + "language": { + "code": "en-US", + "name": "English (United States)" + }, + "languages": [ + { + "code": "en-US", + "name": "English (United States)" + } + ], + "contacts": [], + "themes": [], + "license": "other", + "acl": { + "admin": [ + "@compass.ie" + ] + } + }, + "linkTemplates": [], + "links": [ + { + "rel": "website", + "type": "text/html", + "title": "Compass Informatics", + "href": "https://www.compass.ie/" + }, + { + "rel": "logo-dark", + "type": "image/png", + "title": "Logo", + "href": "" + }, + { + "rel": "logo-light", + "type": "image/png", + "title": "Logo", + "href": "" + } + ] +} diff --git a/schemas/record.json b/schemas/record.json index a13237c6..2209a7f8 100644 --- a/schemas/record.json +++ b/schemas/record.json @@ -125,7 +125,10 @@ "Digital Elevation/Terrain Model (DEM)", "ECMWF ERA5", "Data Analysis and Visualization", - "Statistical Applications" + "Statistical Applications", + "Urban Heat Island", + "Land Surface Temperature", + "Climate" ] }, "minItems": 1, @@ -541,7 +544,9 @@ "type": { "type": "string", "description": "Must use the correct mimetype", - "enum": ["application/x-ipynb+json"] + "enum": [ + "application/x-ipynb+json" + ] } }, "required": [