Skip to content

Commit

Permalink
GeometryCollection: Cache properties to avoid O(n) lookups
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaston committed Dec 11, 2024
1 parent fc95e4b commit 8a447c1
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 33 deletions.
13 changes: 13 additions & 0 deletions include/geos/geom/GeometryCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,19 @@ class GEOS_DLL GeometryCollection : public Geometry {

bool hasCurvedComponents() const override;

struct CollectionFlags {

This comment has been minimized.

Copy link
@dr-jts

dr-jts Dec 11, 2024

What is the storage cost for this?

I guess it's incurred for every collection object, right?

This comment has been minimized.

Copy link
@dbaston

dbaston Dec 12, 2024

Author Owner

Actually zero, if I reorder the members so that "flags" goes in between "geometries" and "envelope"

This comment has been minimized.

Copy link
@dr-jts

dr-jts Dec 12, 2024

Nice. Unfortunately I don't think the same optimization happens in Java.

This comment has been minimized.

Copy link
@dbaston

dbaston Dec 12, 2024

Author Owner

I don't know? If the JVM is padding to a multiple of 8 bytes, it might fit in for free. If not, we're talking about a single byte, right?

This comment has been minimized.

Copy link
@dr-jts

dr-jts Dec 12, 2024

Based on this Java booleans likely take 4 bytes each - athough it's complicated.

bool flagsCalculated : 1;
bool hasPoints : 1;
bool hasLines : 1;
bool hasPolygons : 1;
bool hasM : 1;
bool hasZ : 1;
bool hasCurves : 1;
};

mutable CollectionFlags flags;

void setFlags() const;

};

Expand Down
97 changes: 64 additions & 33 deletions src/geom/GeometryCollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ GeometryCollection::GeometryCollection(const GeometryCollection& gc)
:
Geometry(gc),
geometries(gc.geometries.size()),
envelope(gc.envelope)
envelope(gc.envelope),
flags{}
{
for(std::size_t i = 0; i < geometries.size(); ++i) {
geometries[i] = gc.geometries[i]->clone();
Expand All @@ -51,6 +52,7 @@ GeometryCollection::operator=(const GeometryCollection& gc)
{
geometries.resize(gc.geometries.size());
envelope = gc.envelope;
flags = gc.flags;

for (std::size_t i = 0; i < geometries.size(); i++) {
geometries[i] = gc.geometries[i]->clone();
Expand All @@ -62,7 +64,9 @@ GeometryCollection::operator=(const GeometryCollection& gc)
GeometryCollection::GeometryCollection(std::vector<std::unique_ptr<Geometry>> && newGeoms, const GeometryFactory& factory) :
Geometry(&factory),
geometries(std::move(newGeoms)),
envelope(computeEnvelopeInternal()) {
envelope(computeEnvelopeInternal()),
flags{}
{

if (hasNullElements(&geometries)) {
throw util::IllegalArgumentException("geometries must not contain null elements\n");
Expand Down Expand Up @@ -114,31 +118,69 @@ GeometryCollection::isEmpty() const
return true;
}

void
GeometryCollection::setFlags() const {
if (flags.flagsCalculated) {
return;
}

for (const auto& geom : geometries) {
flags.hasPoints |= geom->hasDimension(Dimension::P);
flags.hasLines |= geom->hasDimension(Dimension::L);
flags.hasPolygons |= geom->hasDimension(Dimension::A);
flags.hasM |= geom->hasM();
flags.hasZ |= geom->hasZ();
flags.hasCurves |= geom->hasCurvedComponents();
}

flags.flagsCalculated = true;
}

Dimension::DimensionType
GeometryCollection::getDimension() const
{
Dimension::DimensionType dimension = Dimension::False;
for(const auto& g : geometries) {
dimension = std::max(dimension, g->getDimension());
setFlags();

if (flags.hasPolygons) {
return Dimension::A;
}
return dimension;
if (flags.hasLines) {
return Dimension::L;
}
if (flags.hasPoints) {
return Dimension::P;
}
return Dimension::False;
}

bool
GeometryCollection::isDimensionStrict(Dimension::DimensionType d) const {
return std::all_of(geometries.begin(), geometries.end(),
[&d](const std::unique_ptr<Geometry> & g) {
return g->getDimension() == d;
});
setFlags();

if (isEmpty()) {
return true;
}

switch(d) {
case Dimension::A: return flags.hasPolygons && !flags.hasLines && !flags.hasPoints;
case Dimension::L: return !flags.hasPolygons && flags.hasLines && !flags.hasPoints;
case Dimension::P: return !flags.hasPolygons && !flags.hasLines && flags.hasPoints;
default:
return false;
}
}

bool
GeometryCollection::hasDimension(Dimension::DimensionType d) const {
return std::any_of(geometries.begin(),
geometries.end(),
[&d](const std::unique_ptr<Geometry>& g) {
return g->hasDimension(d);
});
setFlags();

switch (d) {
case Dimension:: A: return flags.hasPolygons;
case Dimension:: L: return flags.hasLines;
case Dimension:: P: return flags.hasPoints;
default:
return false;
}
}

int
Expand All @@ -165,23 +207,15 @@ GeometryCollection::getCoordinateDimension() const
bool
GeometryCollection::hasM() const
{
for (const auto& g : geometries) {
if (g->hasM()) {
return true;
}
}
return false;
setFlags();
return flags.hasM;
}

bool
GeometryCollection::hasZ() const
{
for (const auto& g : geometries) {
if (g->hasZ()) {
return true;
}
}
return false;
setFlags();
return flags.hasZ;
}

size_t
Expand All @@ -201,6 +235,7 @@ GeometryCollection::releaseGeometries()
{
auto ret = std::move(geometries);
geometryChanged();
flags.flagsCalculated = false;
return ret;
}

Expand Down Expand Up @@ -334,12 +369,8 @@ GeometryCollection::compareToSameClass(const Geometry* g) const
}

bool GeometryCollection::hasCurvedComponents() const {
for (const auto& g : geometries) {
if (g->hasCurvedComponents()) {
return true;
}
}
return false;
setFlags();
return flags.hasCurves;
}

const CoordinateXY*
Expand Down

0 comments on commit 8a447c1

Please sign in to comment.