Skip to content

Conversation

@dbaston
Copy link
Member

@dbaston dbaston commented Nov 6, 2025

Adds a gdal vector sort command to sort features spatially using either a Hilbert curve or a depth-first traversal of an STRtree. Code still needs to be cleaned up but comments on the concept are welcome.

Only argument is --method [hilbert,strtree]. I think we could also have --fields that would be mutually exclusive with --method if a user wants to sort on an attribute instead.

Input parcel dataset, colored on row number:

image

Hilbert-sorted:

image

STRtree-sorted:

image

@dbaston dbaston marked this pull request as draft November 6, 2025 19:01
{
OGRFeature *poSrcFeature = srcFeatures[iSrcFeature].get();

// Tried just using poSrcFeature->SetFDefnUnsafe and plugging
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call SetFID(OGRNullFID) should likely allow you to use SetFDefnUnsafe


bool Process(OGRLayer &srcLayer, OGRLayer &dstLayer) override
{
std::vector<std::unique_ptr<OGRFeature>> features;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great if we had a way of dealing with layers that don't fit in RAM (but where the hilbertCodes vector would still fit). That would likely involve using a temporary storage like I did in the Parquet driver where when with the SORT_BY_BBOX layer creation option is used, a temporary GeoPackage file is created., for source layers that don't have efficient GetFeature() implementation.
This remark is not to be implemented in the context of this PR.

@pytest.fixture()
def alg():
return gdal.GetGlobalAlgorithmRegistry()["vector"]["sort"]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be added tests on layers without geometry fields, or with features without geometry

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where the features without geometry will be placed? First, last, or removed?

const OGRFeature *poFeature = features[i].get();
const OGRGeometry *poGeom = poFeature->GetGeometryRef();

if (poGeom != nullptr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (poGeom != nullptr)
if (poGeom != nullptr && !poGeom->IsEmpty())

{
const OGRFeature *poFeature = features[i].get();
const OGRGeometry *poGeom = poFeature->GetGeometryRef();
if (poGeom == nullptr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (poGeom == nullptr)
if (poGeom == nullptr || poGeom->IsEmpty())

@coveralls
Copy link
Collaborator

Coverage Status

coverage: 71.362% (+0.002%) from 71.36%
when pulling 64d2b84 on dbaston:gdal-vector-sort
into ea75dfb on OSGeo:master.

public:
static constexpr const char *NAME = "sort";
static constexpr const char *DESCRIPTION =
"Spatially order the features a layer";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...on a layer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants