Fielddomain - #2598
Fielddomain#2598edzer wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for exposing GDAL/OGR field domain metadata through st_read() outputs, enabling consumers to inspect per-field domain names and (when supported) dataset-level domain definitions.
Changes:
- Attach a per-field
domainsattribute when building the intermediate R list from an OGR layer. - (GDAL >= 3.5) Extract dataset field-domain definitions and attach them as a
FieldDomainsattribute. - Preserve/propagate these attributes through
process_cpl_read_ogr()into the finalsfobject (and explicitly NULL them for non-geometry returns).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/gdal_read.cpp | Captures field domain names during layer read and (GDAL >= 3.5) collects field-domain definitions from the dataset. |
| R/read.R | Propagates domains / FieldDomains attributes onto the returned sf (and clears them for non-geometry returns). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (poFieldDefn->GetDomainName() != "") | ||
| domains[i] = poFieldDefn->GetDomainName(); |
There was a problem hiding this comment.
poFieldDefn->GetDomainName() is compared to "" using !=, which for const char* does a pointer comparison rather than checking string content. This can incorrectly treat empty domain names as present (or miss non-empty ones, depending on implementation). Please check for non-empty content explicitly (e.g., store the returned pointer and test dn && dn[0] != '\0', or convert to std::string and use .empty()).
| if (poFieldDefn->GetDomainName() != "") | |
| domains[i] = poFieldDefn->GetDomainName(); | |
| const char *domain_name = poFieldDefn->GetDomainName(); | |
| if (domain_name != NULL && domain_name[0] != '\0') | |
| domains[i] = domain_name; |
| OGRFieldDomainMergePolicy mp = d->GetMergePolicy(); | ||
| Rcpp::LogicalVector l(1); | ||
| if (mp == OFDMP_DEFAULT_VALUE) | ||
| l[0] = NA_LOGICAL; | ||
| else if (mp == OFDMP_SUM) | ||
| l[0] = true; | ||
| else if (mp == OFDMP_GEOMETRY_WEIGHTED) | ||
| l[0] = false; | ||
| fdi[1] = l; | ||
| OGRFieldDomainSplitPolicy sp = d->GetSplitPolicy(); | ||
| if (sp == OFDSP_DEFAULT_VALUE) | ||
| l[0] = NA_LOGICAL; | ||
| else if (sp == OFDSP_DUPLICATE) | ||
| l[0] = false; | ||
| else if (sp == OFDSP_GEOMETRY_RATIO) | ||
| l[0] = true; | ||
| fdi[2] = l; |
There was a problem hiding this comment.
get_field_domains() only handles a subset of OGRFieldDomainMergePolicy/SplitPolicy enum values. For any other value, the LogicalVector defaults to FALSE, which will silently misrepresent the policy. Please add a final else branch for both merge and split policies to set NA_LOGICAL (or error) for unhandled enum values to avoid incorrect metadata.
| return(structure(x, domains = NULL, FieldDomains = NULL)) | ||
| } | ||
|
|
||
| nm = names(x)[which.geom] | ||
| Encoding(nm) = "UTF-8" | ||
| geom = x[which.geom] | ||
| do = fd = NULL | ||
| domains = attr(x, "domains") | ||
| if (!is.null(domains) && length(domains) && !all(is.na(domains))) { | ||
| fd = attr(x, "FieldDomains") | ||
| do = domains | ||
| } |
There was a problem hiding this comment.
New behavior attaches domains / FieldDomains attributes to the returned object, but there are no tests asserting the presence/absence and basic structure of these attributes (including the no-geometry branch). Please add a test (likely in tests/testthat/test-read.R) that verifies the attributes are NULL when no domains exist, and (when GDAL supports it and test data is available) that non-empty domains are propagated consistently.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| r = read_sf(system.file("gpkg/td.gpkg.zip", package = "sf"), options=c("IMMUTABLE=YES")) | ||
| expect_false(is.null(attr(r, "FieldDomains"))) | ||
| expect_false(is.null(attr(r, "domains"))) |
There was a problem hiding this comment.
This test unconditionally expects FieldDomains/domains attributes, but the implementation only populates FieldDomains when compiled with GDAL >= 3.5.0 (and domains requires at least one non-NA domain name, which depends on GDAL/driver support). Please gate the expectations with skip_if_not(sf_extSoftVersion()[['GDAL']] >= '3.5.0') (and/or make the assertions conditional) so the test suite passes on builds using older GDAL versions.
| r = read_sf(system.file("gpkg/td.gpkg.zip", package = "sf"), options=c("IMMUTABLE=YES")) | |
| expect_false(is.null(attr(r, "FieldDomains"))) | |
| expect_false(is.null(attr(r, "domains"))) | |
| skip_if_not(sf_extSoftVersion()[["GDAL"]] >= "3.5.0") | |
| r = read_sf(system.file("gpkg/td.gpkg.zip", package = "sf"), options=c("IMMUTABLE=YES")) | |
| field_domains = attr(r, "FieldDomains") | |
| expect_false(is.null(field_domains)) | |
| field_domain_names = names(field_domains) | |
| if (!is.null(field_domain_names) && any(!is.na(field_domain_names) & nzchar(field_domain_names))) { | |
| expect_false(is.null(attr(r, "domains"))) | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Adds basic functionality of reading (some of the) field domain information from data sources; see #1640