The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

lczexplore_multiple_workflows

Matthieu Gousseff

2026-10-19

This vignette focuses on loading, intersecting and comparing multiple (3 to 5) LCZ maps on the same area or set of areas. The first part of the vignette describes the recommended pipeline, which should cover most of the cases.

Some specific functions are described later on for more specific cases.

Note : Only vector workflows (as opposed to raster) can be processed when comparing more than 2 workflows. lczexplore::importLCZraster function allows to import raster images with an LCZ band and sf::write_sf re-exports them, for instance to FlatGeobuf (.fgb)

Context and vocabulary

Local Climate Zones

Climate change is a growing concern for city planners. Stewardt and Oke have proposed a classification into Local Climate Zones (LCZ) that describes rural and urban areas, with 10 built types and 7 land cover types. Several workflows allow to produce LCZ maps, and none can be considered as an absolute reference. Therefore, there is a need for comparison of maps produced by different workflows on the same area.

This vignette shows how to compare 3 workflows (i.e. their resulting maps) at a time. The process can be extended to more maps, but graphical outputs may become hard to read for more than 4 or 5 workflows.

This package does not produce LCZ maps, but imports, shows and compare maps from standard GIS format, with a prefered input format in flatGeoBuffer (.fgb).

Spatial Units

Each LCZ map partitions the territory into spatial units, for instance regular grid when based on raster image, or topographic spatial units. Each spatial unit is assigned a LCZ type. When comparing different maps, one needs to intersect all spatial units to obtain units common to all maps. On each intersected unit, it is then possible to look if two workflows agree or disagree.

Data preparation for multiple workflows and multiple study areas

The most generic case study is when several maps are available for several locations. A simple folder tree allows an easy analysis.

Input data structure

When several LCZ maps are available for several places and stored into local files, the following data structure allows an easier analysis.

–main_data_directory

|–location1

|--wf1_lcz.fgb

|--wf2_lcz.fgb

|--wfp_lcz.fgb

|–location2

|--wf1_lcz.fgb

|--wf2_lcz.fgb

|--wfp_lcz.fgb

The main data directory must contain only the data subdirectories, as the loading functions will span the subdirectories searching for data.

In each subdirectory, the data are stored in files named wfNamei_lcz.fgb where are the names of the workflows. In the tree above, the workflows are called wf1 and wf2.

Other spatial extensions may work, but .fgb files are recommended and were the most tested.

General steps to compare several LCZ maps

Import. First, all the data is loaded in a list.

This list contains a sublist for each location, and each location sublist contains one sf object for each LCZ map.

Intersection. Second, the spatial units of all maps (i.e. the geometries bearing the LCZ type) are intersected. On any resulting geometry, a pair of workflow either totally agree or totally disagree. Intersection is done within lists, which allows to try to intersect only geometries of the same location. The result is concatenated in a single sf objects which contains a column for each lcz map, named after the workflow it comes from, a column with the location name, a column with the area and a column with the geometry for each intersected spatial unit.

Visualisation

Simple descriptive barplots give a general idea of the behavior of the different workflows.

Pairwise comparison allow a visual correspondence between 2 workflows.

Circular Chord Diagram allow a simultaneous visualization of correspondences between all workflows.

Numerical Comparison

Pairwise agreement between workflow and consensus value for each LCZ type quantify this comparison.

pipeline
pipeline

Importing maps from several locations and several workflows

In case of several workflows and several locations, the function importMultipleLocsLCZvect allow to load all the data in a list.

In the data structure from above section, the function would be applied to the main_directory folder.

The small data files for this vignette are in the inst/extdata/multipleWfs folder, which contains the locations Arville and Redon, for which three LCZ maps are available, produced by the following workflows : - GeoClimate based on OpenStreetMap input data - GeoClimate based on french Geographical Information Institute database called BDTOPO - WUDAPT, based on satellite images and machine learning.

require(lczexplore)
twoLocsDir<-paste0(
  system.file("extdata", package = "lczexplore"),"/multipleWfs")
twoLocsSfList<-importMultipleLocsLCZvect(dirPath = twoLocsDir, workflowNames = c("osm","bdt","wudapt"),
                                   location = c("Arville", "Redon"))
#> The data will be imported from the following folders :/tmp/RtmppWIPZu/Rinst132c522a1db42/lczexplore/extdata/multipleWfs/ArvilleThe data will be imported from the following folders :/tmp/RtmppWIPZu/Rinst132c522a1db42/lczexplore/extdata/multipleWfs/Redon
#> The locations are :ArvilleThe locations are :Redon
#> The names of the LCZ types columns were mis-specified, attempt to load with default 'lcz_primary' name
#> Column lcz_primary not found,but LCZ_PRIMARY was, trying with it
#> Column lcz_primary not found,but LCZ_PRIMARY was, trying with it
#> Column lcz_primary not found,but LCZ_PRIMARY was, trying with it
#> Column lcz_primary not found,but LCZ_PRIMARY was, trying with it

The function importMultipleLocsLCZvect loads all the input .fgb files in a list.

When comparing maps for only one location use the similar function importMultipleLCZvect.

Intersecting geometries from the different workflows

The spatial units

In the case of only two workflows, the function compareLCZ loads the two sf objects, intersects their geometries, produce the array of correspondance between the LCZ types of the two workflows.

With more than two workflows, one needs more control over the intersection steps. First, in case of multiple locations, the intersection of the several workflows must occur within each location. Second, intersecting the spatial units of multiple workflows can lead to artificially small geometries.

The createIntersect function allows to specify a minimal area threshold. Resulting geometries whose area are lesser than this threshold will be discarded.

twoLocsSfIntersected <- createIntersect(sfList = twoLocsSfList, columns = rep("lcz_primary", 4),
                                        refCrs=NULL, workflowNames=c("osm", "bdt", "wudapt"), minZeroArea=0.000)
#> Warning: attribute variables are assumed to be spatially constant throughout
#> all geometries
#> Warning: attribute variables are assumed to be spatially constant throughout
#> all geometries
#> Warning: attribute variables are assumed to be spatially constant throughout
#> all geometries
#> Warning: attribute variables are assumed to be spatially constant throughout
#> all geometries

Note : the function createIntersect is able to see if the list fed to the sfList argument has one or two levels, i.e. if there are one or multiple locations.

Visualizing the data

Reminder: visualize a single map

The twoLocsSfList list contains one sf object per workflow per location. This sf object can be fed to the function showLCZ() for a map and a summary of the area classified in each LCZ type.

showLCZ(sf = twoLocsSfList$Redon$osm, column = "lcz_primary", repr = "standard")

Reminder: comparing two simple maps

One can also compare two maps from the same location using the function compareLCZ()

Note : for only 2 maps, the user feeds compareLCZ the sf files, the function does the intersection.

redonOSM <- twoLocsSfList$Redon$osm
redonBDT <- twoLocsSfList$Redon$bdt
compareLCZ(sf1 = redonOSM, sf2 = redonBDT, column1 = "lcz_primary", column2 = "lcz_primary", wf1 = "osm", wf2 = "bdt_V_2")
#>  The column  lcz_primary  of the dataset sf1 is the reference against which the  lcz_primary  column of the dataset  sf2 will be compared.
#> For Technical reason in this package, worfklow names can't contain spaces or underscores, they were replaced by dots
#> Both sf datasets need to live in the same crs projection (srid / epsg),
#> they will be coerced to the specified reference (sf1)
#> Warning: Unknown or uninitialised column: `location`.
#> Warning: Unknown or uninitialised column: `wf`.
#> Warning: Unknown or uninitialised column: `location`.
#> Warning: Unknown or uninitialised column: `wf`.
#> Warning: attribute variables are assumed to be spatially constant throughout
#> all geometries

Area per LCZ type and workflow for more than 2 workflows

The function barplotLCZfromIntersected allows the visualisation of what area is classified into each LCZ type of each workflow.

areaPerLCZtype<-barplotLCZfromIntersect(sfIn = twoLocsSfIntersected, workflowNames = c("osm", "bdt", "wudapt"))
#> The names of the columns of the lcz types for each workflow are missing, an attempt will be made with workflowNames instead

Compare multiple workflows

The function compareMultipleLCZ outputs a list containing two objects. The first one is an sf object, named sfInt. It contains the LCZ type for each workflow, the pairwise agreements, and the number of workflows who agree for each geometry (ie, intersected spatial units).

twoLocsCompared <- compareMultipleLCZ(
  sfInt = twoLocsSfIntersected,
  columns = c("osm","bdt","wudapt"),
  trimPerc = 0)
#> This function computes how any LCZ type from any workflow
#>   breaks into the LCZ types of all other workflows: it can take a while
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Only standard LCZ levels detected, standard colors and labels used
#> Note: 3 points are out of plotting region in sector 'bdt_002', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_002', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_003', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_003', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_005', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_006', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_006', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_006', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_008', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_008', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_008', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_009', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_009', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_009', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_101', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_101', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_101', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_102', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_102', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_102', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_104', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_104', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_104', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_105', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_105', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_106', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_107', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_107', track
#> '2'.

## Pairwise agreements From this sfInt object, the function workflowAgreeAreas compute pairwise agreement between workflows, defined as the sum (or percentage) of the area on which two workflows agree. It is also directly given as an output of compareMultipleLCZ()

twoLocsCompared$agreements
#>     whichWfs  areaAgree areaDisagree percAgree
#> 1 osm_wudapt 11556485.4    1330970.1  89.67236
#> 2    osm_bdt   831817.6     755647.3  52.39911
#> 3 bdt_wudapt   500523.3    1086941.6  31.52972

Repartition from one workflow to another and data visualisations

To explore this agreement further, we compute all the repartition from an LCZ type from a workflow to the LCZ types of another workflow, using the function createWeightedFlux

twoLocsFlow<-createWeightedFlux(twoLocsSfIntersected, wfNamesIn = c("osm","bdt","wudapt"))
#> This function computes how any LCZ type from any workflow
#>   breaks into the LCZ types of all other workflows: it can take a while
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, 106, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, 106, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, 106, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 5, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 5, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 5, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, 106, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, 106, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, 106, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 5, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 5, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 5, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified
#> Warning: Unknown levels in `f`: 1, 4, 7, 10, 103, Unclassified

To avoid exploring all the pairwise possible matrices, we can explore these correspondences through a circular chord diagram, using the function drawChordDiagram.

drawChordDiagram(weightedFluxIn = twoLocsFlow)
#> Only standard LCZ levels detected, standard colors and labels used
#> Note: 3 points are out of plotting region in sector 'bdt_002', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_002', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_003', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_003', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_005', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_006', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_006', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_006', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_008', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_008', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_008', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_009', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_009', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_009', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_101', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_101', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_101', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_102', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_102', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_102', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_104', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_104', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'wud_104', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_105', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_105', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_106', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'bdt_107', track
#> '2'.
#> Note: 3 points are out of plotting region in sector 'osm_107', track
#> '2'.

#> [[1]]
#> NULL
#> 
#> [[2]]
#> NULL
#> 
#> [[3]]
#> NULL
#> 
#> [[4]]
#> NULL
#> 
#> [[5]]
#> NULL
#> 
#> [[6]]
#> NULL
#> 
#> [[7]]
#> NULL
#> 
#> [[8]]
#> NULL
#> 
#> [[9]]
#> NULL
#> 
#> [[10]]
#> NULL
#> 
#> [[11]]
#> NULL
#> 
#> [[12]]
#> NULL

This treatment is also directly done in compareMultipleLCZ, but as circlize doesn’t create an object (like ggplot2 does) it can not be exported as an object, so you have to recall drawChordDiagram.

A very specific case of missing geometries

When, for a location, some workflows have missing geometries, i.e. don’t map some area and leave them as holes, one can put a zone.fgb file in the directory, and the function importConcatMultipleLocsLCZvect will allow to use it and create the missing geometries and assign them a given type. Feed the name of the workflow whose geometries are missing to the missingGeomWf argument, and the function will intersect it with the zone.fgb geometries. Sometimes, the missing geometries can be of one type, as a decision from the designers of the LCZ workflow. In this case, one can choose another workflow as a reference (through refWf) and the corresponding LCZ typ (through refLCZ). The remaining geometries which wouldn’t intersect with the Chosen LCZ type of the chosen reference workflow will receive the value fed to residualLCZvalue.

twoLocsDir<-paste0(
  system.file("extdata", package = "lczexplore"),"/multipleWfs")

# allWfsAllLocs<-importConcatMultipleLocsLCZvect(twoLocsDir, locations = NA, workflowNames = c("osm", "bdt", "wudapt"),
#                                        missingGeomsWf= "osm", refWf = NULL, refLCZ = NA,
#                                        residualLCZvalue = NA, column = "lcz_primary")

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.