From 0788a1e95d03dd600f8404c43f1bbde7fe8020ff Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Mon, 3 May 2021 14:08:16 +0200 Subject: [PATCH 1/6] Add Curve and CircularString geometries --- binding.gyp | 19 +-- lib/gdal.js | 1 + .../geometry_collection_children.cpp | 4 +- src/collections/linestring_points.cpp | 6 +- src/collections/polygon_rings.cpp | 6 +- src/gdal_dataset.cpp | 2 +- src/gdal_feature.cpp | 2 +- src/gdal_layer.cpp | 2 +- src/gdal_multipolygon.cpp | 135 ----------------- src/geometry/gdal_circularstring.cpp | 136 ++++++++++++++++++ src/geometry/gdal_circularstring.hpp | 39 +++++ src/geometry/gdal_curve.hpp | 66 +++++++++ src/{ => geometry}/gdal_geometry.cpp | 9 +- src/geometry/gdal_geometry.hpp | 96 +++++++++++++ .../gdal_geometrybase.hpp} | 106 +++----------- .../gdal_geometrycollection.cpp | 4 +- src/geometry/gdal_geometrycollection.hpp | 39 +++++ .../gdal_geometrycollectionbase.hpp} | 27 +--- src/{ => geometry}/gdal_linearring.cpp | 32 +---- src/{ => geometry}/gdal_linearring.hpp | 12 +- src/{ => geometry}/gdal_linestring.cpp | 32 +---- src/{ => geometry}/gdal_linestring.hpp | 12 +- src/{ => geometry}/gdal_multilinestring.cpp | 4 +- src/{ => geometry}/gdal_multilinestring.hpp | 4 +- src/{ => geometry}/gdal_multipoint.cpp | 4 +- src/{ => geometry}/gdal_multipoint.hpp | 4 +- src/geometry/gdal_multipolygon.cpp | 64 +++++++++ src/{ => geometry}/gdal_multipolygon.hpp | 25 +--- src/{ => geometry}/gdal_point.cpp | 2 +- src/{ => geometry}/gdal_point.hpp | 5 +- src/{ => geometry}/gdal_polygon.cpp | 36 +---- src/{ => geometry}/gdal_polygon.hpp | 16 ++- src/node_gdal.cpp | 26 ++-- src/utils/warp_options.cpp | 2 +- test/api_geometry.test.js | 12 +- 35 files changed, 572 insertions(+), 419 deletions(-) delete mode 100644 src/gdal_multipolygon.cpp create mode 100644 src/geometry/gdal_circularstring.cpp create mode 100644 src/geometry/gdal_circularstring.hpp create mode 100644 src/geometry/gdal_curve.hpp rename src/{ => geometry}/gdal_geometry.cpp (99%) create mode 100644 src/geometry/gdal_geometry.hpp rename src/{gdal_geometry.hpp => geometry/gdal_geometrybase.hpp} (50%) rename src/{ => geometry}/gdal_geometrycollection.cpp (95%) create mode 100644 src/geometry/gdal_geometrycollection.hpp rename src/{gdal_geometrycollection.hpp => geometry/gdal_geometrycollectionbase.hpp} (62%) rename src/{ => geometry}/gdal_linearring.cpp (59%) rename src/{ => geometry}/gdal_linearring.hpp (58%) rename src/{ => geometry}/gdal_linestring.cpp (80%) rename src/{ => geometry}/gdal_linestring.hpp (63%) rename src/{ => geometry}/gdal_multilinestring.cpp (94%) rename src/{ => geometry}/gdal_multilinestring.hpp (91%) rename src/{ => geometry}/gdal_multipoint.cpp (91%) rename src/{ => geometry}/gdal_multipoint.hpp (90%) create mode 100644 src/geometry/gdal_multipolygon.cpp rename src/{ => geometry}/gdal_multipolygon.hpp (50%) rename src/{ => geometry}/gdal_point.cpp (99%) rename src/{ => geometry}/gdal_point.hpp (92%) rename src/{ => geometry}/gdal_polygon.cpp (64%) rename src/{ => geometry}/gdal_polygon.hpp (54%) diff --git a/binding.gyp b/binding.gyp index 927c17029..2d548408e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -29,15 +29,16 @@ "src/gdal_feature.cpp", "src/gdal_feature_defn.cpp", "src/gdal_field_defn.cpp", - "src/gdal_geometry.cpp", - "src/gdal_point.cpp", - "src/gdal_linestring.cpp", - "src/gdal_linearring.cpp", - "src/gdal_polygon.cpp", - "src/gdal_geometrycollection.cpp", - "src/gdal_multipoint.cpp", - "src/gdal_multilinestring.cpp", - "src/gdal_multipolygon.cpp", + "src/geometry/gdal_geometry.cpp", + "src/geometry/gdal_point.cpp", + "src/geometry/gdal_linestring.cpp", + "src/geometry/gdal_circularstring.cpp", + "src/geometry/gdal_linearring.cpp", + "src/geometry/gdal_polygon.cpp", + "src/geometry/gdal_geometrycollection.cpp", + "src/geometry/gdal_multipoint.cpp", + "src/geometry/gdal_multilinestring.cpp", + "src/geometry/gdal_multipolygon.cpp", "src/gdal_layer.cpp", "src/gdal_coordinate_transformation.cpp", "src/gdal_spatial_reference.cpp", diff --git a/lib/gdal.js b/lib/gdal.js index 244b30d1d..156b258c5 100644 --- a/lib/gdal.js +++ b/lib/gdal.js @@ -822,6 +822,7 @@ gdal.LayerFields.prototype.fromObject = function (obj, approx_ok) { gdal.Point.wkbType = gdal.wkbPoint gdal.LineString.wkbType = gdal.wkbLineString +gdal.CircularString.wkbType = gdal.wkbCircularString gdal.LinearRing.wkbType = gdal.wkbLinearRing gdal.Polygon.wkbType = gdal.wkbPolygon gdal.MultiPoint.wkbType = gdal.wkbMultiPoint diff --git a/src/collections/geometry_collection_children.cpp b/src/collections/geometry_collection_children.cpp index 311f1a1ed..764af1583 100644 --- a/src/collections/geometry_collection_children.cpp +++ b/src/collections/geometry_collection_children.cpp @@ -1,7 +1,7 @@ #include "geometry_collection_children.hpp" #include "../gdal_common.hpp" -#include "../gdal_geometry.hpp" -#include "../gdal_geometrycollection.hpp" +#include "../geometry/gdal_geometry.hpp" +#include "../geometry/gdal_geometrycollection.hpp" namespace node_gdal { diff --git a/src/collections/linestring_points.cpp b/src/collections/linestring_points.cpp index 908be0d48..ef90a640a 100644 --- a/src/collections/linestring_points.cpp +++ b/src/collections/linestring_points.cpp @@ -1,8 +1,8 @@ #include "linestring_points.hpp" #include "../gdal_common.hpp" -#include "../gdal_geometry.hpp" -#include "../gdal_linestring.hpp" -#include "../gdal_point.hpp" +#include "../geometry/gdal_geometry.hpp" +#include "../geometry/gdal_linestring.hpp" +#include "../geometry/gdal_point.hpp" namespace node_gdal { diff --git a/src/collections/polygon_rings.cpp b/src/collections/polygon_rings.cpp index 61457db74..7bf1cd879 100644 --- a/src/collections/polygon_rings.cpp +++ b/src/collections/polygon_rings.cpp @@ -1,8 +1,8 @@ #include "polygon_rings.hpp" #include "../gdal_common.hpp" -#include "../gdal_geometry.hpp" -#include "../gdal_linearring.hpp" -#include "../gdal_polygon.hpp" +#include "../geometry/gdal_geometry.hpp" +#include "../geometry/gdal_linearring.hpp" +#include "../geometry/gdal_polygon.hpp" namespace node_gdal { diff --git a/src/gdal_dataset.cpp b/src/gdal_dataset.cpp index c05b28d7c..f54785398 100644 --- a/src/gdal_dataset.cpp +++ b/src/gdal_dataset.cpp @@ -3,7 +3,7 @@ #include "collections/dataset_layers.hpp" #include "gdal_common.hpp" #include "gdal_driver.hpp" -#include "gdal_geometry.hpp" +#include "geometry/gdal_geometry.hpp" #include "gdal_layer.hpp" #include "gdal_majorobject.hpp" #include "gdal_rasterband.hpp" diff --git a/src/gdal_feature.cpp b/src/gdal_feature.cpp index f6c50b66f..731a26e1d 100644 --- a/src/gdal_feature.cpp +++ b/src/gdal_feature.cpp @@ -4,7 +4,7 @@ #include "gdal_common.hpp" #include "gdal_feature_defn.hpp" #include "gdal_field_defn.hpp" -#include "gdal_geometry.hpp" +#include "geometry/gdal_geometry.hpp" #include "gdal_layer.hpp" namespace node_gdal { diff --git a/src/gdal_layer.cpp b/src/gdal_layer.cpp index 898b80cc6..258b33f33 100644 --- a/src/gdal_layer.cpp +++ b/src/gdal_layer.cpp @@ -7,7 +7,7 @@ #include "gdal_feature.hpp" #include "gdal_feature_defn.hpp" #include "gdal_field_defn.hpp" -#include "gdal_geometry.hpp" +#include "geometry/gdal_geometry.hpp" #include "gdal_spatial_reference.hpp" #include diff --git a/src/gdal_multipolygon.cpp b/src/gdal_multipolygon.cpp deleted file mode 100644 index a768a7611..000000000 --- a/src/gdal_multipolygon.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "gdal_multipolygon.hpp" -#include "collections/geometry_collection_children.hpp" -#include "gdal_common.hpp" -#include "gdal_geometry.hpp" -#include "gdal_geometrycollection.hpp" -#include "gdal_polygon.hpp" - -#include - -namespace node_gdal { - -Nan::Persistent MultiPolygon::constructor; - -void MultiPolygon::Initialize(Local target) { - Nan::HandleScope scope; - - Local lcons = Nan::New(MultiPolygon::New); - lcons->Inherit(Nan::New(GeometryCollection::constructor)); - lcons->InstanceTemplate()->SetInternalFieldCount(1); - lcons->SetClassName(Nan::New("MultiPolygon").ToLocalChecked()); - - Nan::SetPrototypeMethod(lcons, "toString", toString); - Nan::SetPrototypeMethod(lcons, "unionCascaded", unionCascaded); - Nan::SetPrototypeMethod(lcons, "getArea", getArea); - - Nan::Set(target, Nan::New("MultiPolygon").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); - - constructor.Reset(lcons); -} - -MultiPolygon::MultiPolygon(OGRMultiPolygon *geom) : GeometryCollection(geom), this_(geom) { - LOG("Created MultiPolygon [%p]", geom); -} - -MultiPolygon::MultiPolygon() : GeometryCollection(), this_(NULL) { -} - -MultiPolygon::~MultiPolygon() { - if (this_) { - LOG("Disposing MultiPolygon [%p] (%s)", this_, owned_ ? "owned" : "unowned"); - } -} - -/** - * @constructor - * @class gdal.MultiPolygon - * @extends gdal.GeometryCollection - */ -NAN_METHOD(MultiPolygon::New) { - Nan::HandleScope scope; - MultiPolygon *f; - - if (!info.IsConstructCall()) { - Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword"); - return; - } - - if (info[0]->IsExternal()) { - Local ext = info[0].As(); - void *ptr = ext->Value(); - f = static_cast(ptr); - - } else { - if (info.Length() != 0) { - Nan::ThrowError("MultiPolygon constructor doesn't take any arguments"); - return; - } - f = new MultiPolygon(new OGRMultiPolygon()); - } - - Local children = GeometryCollectionChildren::New(info.This()); - Nan::SetPrivate(info.This(), Nan::New("children_").ToLocalChecked(), children); - - f->Wrap(info.This()); - info.GetReturnValue().Set(info.This()); -} - -Local MultiPolygon::New(OGRMultiPolygon *geom) { - Nan::EscapableHandleScope scope; - return scope.Escape(MultiPolygon::New(geom, true)); -} - -Local MultiPolygon::New(OGRMultiPolygon *geom, bool owned) { - Nan::EscapableHandleScope scope; - - if (!geom) { return scope.Escape(Nan::Null()); } - - // make a copy of geometry owned by a feature - // + no need to track when a feature is destroyed - // + no need to throw errors when a method trys to modify an owned read-only - // geometry - // - is slower - - if (!owned) { geom = static_cast(geom->clone()); }; - - MultiPolygon *wrapped = new MultiPolygon(geom); - wrapped->owned_ = true; - - UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(wrapped); - - Local ext = Nan::New(wrapped); - Local obj = - Nan::NewInstance(Nan::GetFunction(Nan::New(MultiPolygon::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked(); - - return scope.Escape(obj); -} - -NAN_METHOD(MultiPolygon::toString) { - Nan::HandleScope scope; - info.GetReturnValue().Set(Nan::New("MultiPolygon").ToLocalChecked()); -} - -/** - * Unions all the geometries and returns the result. - * - * @method unionCascaded - * @return {gdal.Geometry} - */ -NAN_METHOD(MultiPolygon::unionCascaded) { - Nan::HandleScope scope; - - MultiPolygon *geom = Nan::ObjectWrap::Unwrap(info.This()); - - info.GetReturnValue().Set(Geometry::New(geom->this_->UnionCascaded())); -} - -/** - * Computes the combined area of the collection. - * - * @method getArea - * @return {Number} - */ -NODE_WRAPPED_METHOD_WITH_RESULT(MultiPolygon, getArea, Number, get_Area); - -} // namespace node_gdal diff --git a/src/geometry/gdal_circularstring.cpp b/src/geometry/gdal_circularstring.cpp new file mode 100644 index 000000000..bc4cd8883 --- /dev/null +++ b/src/geometry/gdal_circularstring.cpp @@ -0,0 +1,136 @@ + +#include "gdal_circularstring.hpp" +#include "gdal_linestring.hpp" +#include "../collections/linestring_points.hpp" +#include "../gdal_common.hpp" +#include "gdal_geometry.hpp" +#include "gdal_point.hpp" + +#include + +namespace node_gdal { + +Nan::Persistent CircularString::constructor; + +void CircularString::Initialize(Local target) { + Nan::HandleScope scope; + + Local lcons = Nan::New(CircularString::New); + lcons->Inherit(Nan::New(Geometry::constructor)); + lcons->InstanceTemplate()->SetInternalFieldCount(1); + lcons->SetClassName(Nan::New("CircularString").ToLocalChecked()); + + Nan::SetPrototypeMethod(lcons, "toString", toString); + Nan::SetPrototypeMethod(lcons, "getLength", getLength); + Nan::SetPrototypeMethod(lcons, "value", value); + Nan::SetPrototypeMethod(lcons, "addSubLineString", addSubLineString); + + ATTR(lcons, "points", pointsGetter, READ_ONLY_SETTER); + + Nan::Set(target, Nan::New("CircularString").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); + + constructor.Reset(lcons); +} + +/** + * Concrete representation of an arc. + * + * @example + * ``` + * var CircularString = new gdal.CircularString(); + * CircularString.points.add(new gdal.Point(0,0)); + * CircularString.points.add(new gdal.Point(0,10));``` + * + * @constructor + * @class gdal.CircularString + * @extends gdal.Geometry + */ + +NAN_METHOD(CircularString::toString) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::New("CircularString").ToLocalChecked()); +} + +/** + * Computes the length of the arc. + * + * @method getLength + * @return Number + */ +NODE_WRAPPED_METHOD_WITH_RESULT(CircularString, getLength, Number, get_Length); + +/** + * Returns the point at the specified distance along the arc. + * + * @method value + * @param {Number} distance + * @return {gdal.Point} + */ +NAN_METHOD(CircularString::value) { + Nan::HandleScope scope; + + CircularString *geom = Nan::ObjectWrap::Unwrap(info.This()); + + OGRPoint *pt = new OGRPoint(); + double dist; + + NODE_ARG_DOUBLE(0, "distance", dist); + + geom->this_->Value(dist, pt); + + info.GetReturnValue().Set(Point::New(pt)); +} + +/** + * Add a segment of another LineString to this one. + * + * Adds the request range of vertices to the end of this compound curve in an + * efficient manner. If the start index is larger than the end index then the + * vertices will be reversed as they are copied. + * + * @method addSubLineString + * @param {gdal.LineString} LineString to be added + * @param {int} [start=0] the first vertex to copy, defaults to 0 to start with + * the first vertex in the other LineString + * @param {int} [end=-1] the last vertex to copy, defaults to -1 indicating the + * last vertex of the other LineString + * @return {void} + */ +NAN_METHOD(CircularString::addSubLineString) { + Nan::HandleScope scope; + + CircularString *geom = Nan::ObjectWrap::Unwrap(info.This()); + LineString *other; + int start = 0; + int end = -1; + + NODE_ARG_WRAPPED(0, "line", LineString, other); + NODE_ARG_INT_OPT(1, "start", start); + NODE_ARG_INT_OPT(2, "end", end); + + int n = other->get()->getNumPoints(); + + if (start < 0 || end < -1 || start >= n || end >= n) { + Nan::ThrowRangeError("Invalid start or end index for CircularString"); + return; + } + + geom->this_->addSubLineString(other->get(), start, end); + + UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(geom); + + return; +} + +/** + * Points that make up the arc. + * + * @attribute points + * @type {gdal.CircularStringPoints} + */ +NAN_GETTER(CircularString::pointsGetter) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("points_").ToLocalChecked()).ToLocalChecked()); +} + +} // namespace node_gdal diff --git a/src/geometry/gdal_circularstring.hpp b/src/geometry/gdal_circularstring.hpp new file mode 100644 index 000000000..51ec3ec5b --- /dev/null +++ b/src/geometry/gdal_circularstring.hpp @@ -0,0 +1,39 @@ +#ifndef __NODE_OGR_CIRCULARSTRING_H__ +#define __NODE_OGR_CIRCULARSTRING_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +#include "gdal_curve.hpp" +#include "../collections/linestring_points.hpp" + +using namespace v8; +using namespace node; + +namespace node_gdal { + +class CircularString : public Curve { + + public: + static Nan::Persistent constructor; + using Curve::Curve; + + static void Initialize(Local target); + using Curve::New; + static NAN_METHOD(toString); + static NAN_METHOD(getLength); + static NAN_METHOD(value); + static NAN_METHOD(addSubLineString); + + static NAN_GETTER(pointsGetter); +}; + +} // namespace node_gdal +#endif diff --git a/src/geometry/gdal_curve.hpp b/src/geometry/gdal_curve.hpp new file mode 100644 index 000000000..ec5fef7cd --- /dev/null +++ b/src/geometry/gdal_curve.hpp @@ -0,0 +1,66 @@ +#ifndef __NODE_OGR_CURVE_H__ +#define __NODE_OGR_CURVE_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +#include "gdal_geometrybase.hpp" + +using namespace v8; +using namespace node; + +namespace node_gdal { + +template class Curve : public GeometryBase { + public: + using GeometryBase::GeometryBase; + static NAN_METHOD(New); + using GeometryBase::New; + + protected: + static void SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE, v8::Local); +}; + +template NAN_METHOD((Curve::New)) { + Nan::HandleScope scope; + T *f; + + if (!info.IsConstructCall()) { + Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword"); + return; + } + + if (info[0]->IsExternal()) { + Local ext = info[0].As(); + void *ptr = ext->Value(); + f = static_cast(ptr); + + } else { + if (info.Length() != 0) { + Nan::ThrowError("String constructor doesn't take any arguments"); + return; + } + f = new T(new OGRT()); + } + + Local points = STRING::New(info.This()); + T::SetPrivate(info.This(), points); + + f->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); +} + +template +void Curve::SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE _this, v8::Local value) { + Nan::SetPrivate(_this, Nan::New("points_").ToLocalChecked(), value); +}; + +} // namespace node_gdal +#endif diff --git a/src/gdal_geometry.cpp b/src/geometry/gdal_geometry.cpp similarity index 99% rename from src/gdal_geometry.cpp rename to src/geometry/gdal_geometry.cpp index b22430174..28e5e2c81 100644 --- a/src/gdal_geometry.cpp +++ b/src/geometry/gdal_geometry.cpp @@ -1,16 +1,17 @@ -#include "gdal_common.hpp" +#include "../gdal_common.hpp" -#include "gdal_coordinate_transformation.hpp" +#include "../gdal_coordinate_transformation.hpp" #include "gdal_geometry.hpp" #include "gdal_geometrycollection.hpp" #include "gdal_linearring.hpp" #include "gdal_linestring.hpp" +#include "gdal_circularstring.hpp" #include "gdal_multilinestring.hpp" #include "gdal_multipoint.hpp" #include "gdal_multipolygon.hpp" #include "gdal_point.hpp" #include "gdal_polygon.hpp" -#include "gdal_spatial_reference.hpp" +#include "../gdal_spatial_reference.hpp" #include #include @@ -134,6 +135,7 @@ Local Geometry::New(OGRGeometry *geom, bool owned) { case wkbLineString: return scope.Escape(LineString::New(static_cast(geom), owned)); case wkbLinearRing: return scope.Escape(LinearRing::New(static_cast(geom), owned)); case wkbPolygon: return scope.Escape(Polygon::New(static_cast(geom), owned)); + case wkbCircularString: return scope.Escape(CircularString::New(static_cast(geom), owned)); case wkbGeometryCollection: return scope.Escape(GeometryCollection::New(static_cast(geom), owned)); case wkbMultiPoint: return scope.Escape(MultiPoint::New(static_cast(geom), owned)); @@ -1554,6 +1556,7 @@ Local Geometry::getConstructor(OGRwkbGeometryType type) { switch (type) { case wkbPoint: return scope.Escape(Nan::GetFunction(Nan::New(Point::constructor)).ToLocalChecked()); case wkbLineString: return scope.Escape(Nan::GetFunction(Nan::New(LineString::constructor)).ToLocalChecked()); + case wkbCircularString: return scope.Escape(Nan::GetFunction(Nan::New(CircularString::constructor)).ToLocalChecked()); case wkbLinearRing: return scope.Escape(Nan::GetFunction(Nan::New(LinearRing::constructor)).ToLocalChecked()); case wkbPolygon: return scope.Escape(Nan::GetFunction(Nan::New(Polygon::constructor)).ToLocalChecked()); case wkbGeometryCollection: diff --git a/src/geometry/gdal_geometry.hpp b/src/geometry/gdal_geometry.hpp new file mode 100644 index 000000000..404478764 --- /dev/null +++ b/src/geometry/gdal_geometry.hpp @@ -0,0 +1,96 @@ +#ifndef __NODE_OGR_GEOMETRY_H__ +#define __NODE_OGR_GEOMETRY_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +#include "../async.hpp" +#include "gdal_geometrybase.hpp" + +using namespace v8; +using namespace node; + +namespace node_gdal { + +class Geometry : public GeometryBase { + public: + static Nan::Persistent constructor; + using GeometryBase::GeometryBase; + + static void Initialize(Local target); + static NAN_METHOD(New); + using GeometryBase::New; + static Local New(OGRGeometry *geom, bool owned); + static NAN_METHOD(toString); + GDAL_ASYNCABLE_DECLARE(isEmpty); + GDAL_ASYNCABLE_DECLARE(isValid); + GDAL_ASYNCABLE_DECLARE(isSimple); + GDAL_ASYNCABLE_DECLARE(isRing); + static NAN_METHOD(clone); + GDAL_ASYNCABLE_DECLARE(empty); + GDAL_ASYNCABLE_DECLARE(exportToKML); + GDAL_ASYNCABLE_DECLARE(exportToGML); + GDAL_ASYNCABLE_DECLARE(exportToJSON); + GDAL_ASYNCABLE_DECLARE(exportToWKT); + GDAL_ASYNCABLE_DECLARE(exportToWKB); + GDAL_ASYNCABLE_DECLARE(closeRings); + GDAL_ASYNCABLE_DECLARE(segmentize); + GDAL_ASYNCABLE_DECLARE(intersects); + GDAL_ASYNCABLE_DECLARE(equals); + GDAL_ASYNCABLE_DECLARE(disjoint); + GDAL_ASYNCABLE_DECLARE(touches); + GDAL_ASYNCABLE_DECLARE(crosses); + GDAL_ASYNCABLE_DECLARE(within); + GDAL_ASYNCABLE_DECLARE(contains); + GDAL_ASYNCABLE_DECLARE(overlaps); + GDAL_ASYNCABLE_DECLARE(boundary); + GDAL_ASYNCABLE_DECLARE(distance); + GDAL_ASYNCABLE_DECLARE(convexHull); + GDAL_ASYNCABLE_DECLARE(buffer); + GDAL_ASYNCABLE_DECLARE(intersection); + GDAL_ASYNCABLE_DECLARE(unionGeometry); + GDAL_ASYNCABLE_DECLARE(difference); + GDAL_ASYNCABLE_DECLARE(symDifference); + GDAL_ASYNCABLE_DECLARE(centroid); + GDAL_ASYNCABLE_DECLARE(simplify); + GDAL_ASYNCABLE_DECLARE(simplifyPreserveTopology); + GDAL_ASYNCABLE_DECLARE(polygonize); + GDAL_ASYNCABLE_DECLARE(swapXY); + static NAN_METHOD(getNumGeometries); + GDAL_ASYNCABLE_DECLARE(getEnvelope); + GDAL_ASYNCABLE_DECLARE(getEnvelope3D); + GDAL_ASYNCABLE_DECLARE(flattenTo2D); + GDAL_ASYNCABLE_DECLARE(transform); + GDAL_ASYNCABLE_DECLARE(transformTo); + + // static constructor methods + GDAL_ASYNCABLE_DECLARE(create); + GDAL_ASYNCABLE_DECLARE(createFromWkt); + GDAL_ASYNCABLE_DECLARE(createFromWkb); + GDAL_ASYNCABLE_DECLARE(createFromGeoJson); + static NAN_METHOD(getName); + static NAN_METHOD(getConstructor); + + static NAN_GETTER(srsGetter); + static NAN_GETTER(typeGetter); + static NAN_GETTER(nameGetter); + static NAN_GETTER(wkbSizeGetter); + static NAN_GETTER(dimensionGetter); + static NAN_GETTER(coordinateDimensionGetter); + + static NAN_SETTER(srsSetter); + static NAN_SETTER(coordinateDimensionSetter); + + static OGRwkbGeometryType getGeometryType_fixed(OGRGeometry *geom); + static Local getConstructor(OGRwkbGeometryType type); +}; + +} // namespace node_gdal +#endif diff --git a/src/gdal_geometry.hpp b/src/geometry/gdal_geometrybase.hpp similarity index 50% rename from src/gdal_geometry.hpp rename to src/geometry/gdal_geometrybase.hpp index 4489bc77c..63ac2027f 100644 --- a/src/gdal_geometry.hpp +++ b/src/geometry/gdal_geometrybase.hpp @@ -1,17 +1,14 @@ -#ifndef __NODE_OGR_GEOMETRY_H__ -#define __NODE_OGR_GEOMETRY_H__ +#ifndef __NODE_OGR_GEOMETRYBASE_H__ +#define __NODE_OGR_GEOMETRYBASE_H__ // node #include #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" -// ogr -#include - -#include "async.hpp" +#include "../gdal_common.hpp" using namespace v8; using namespace node; @@ -20,20 +17,26 @@ namespace node_gdal { /* * Geometry class inheritance hierarchy + * It uses CRTP - https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern + * to get around the fact that the methods exposed to JS are static and cannot be virtual * * C++ * GeometryBase<> - * | \ \ \ \ \ - * Geometry Point LineString Polygon LinearRing GeometryCollectionBase<> - * | \ - * GeometryCollection Multi* + * | \ \ \ + * Geometry Point Curve<> GeometryCollectionBase<> + * | \ \ \ | \ + * LineString Polygon LinearRing CircularString GeometryCollection Multi* * * JS * Geometry - * | \ \ \ - * Point LineString Polygon GeometryCollection - * | | - * LinearRing Multi* + * | \ \ \ \ + * Point LineString Polygon CircularString GeometryCollection + * | | + * LinearRing Multi* + * + * + * The full GDAL OGRGeometry class hierarchy + * https://gdal.org/doxygen/classOGRGeometry.html */ #define UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(geom) \ @@ -122,78 +125,5 @@ template GeometryBase::~GeometryBase() { delete async_lock; } -class Geometry : public GeometryBase { - public: - static Nan::Persistent constructor; - using GeometryBase::GeometryBase; - - static void Initialize(Local target); - static NAN_METHOD(New); - using GeometryBase::New; - static Local New(OGRGeometry *geom, bool owned); - static NAN_METHOD(toString); - GDAL_ASYNCABLE_DECLARE(isEmpty); - GDAL_ASYNCABLE_DECLARE(isValid); - GDAL_ASYNCABLE_DECLARE(isSimple); - GDAL_ASYNCABLE_DECLARE(isRing); - static NAN_METHOD(clone); - GDAL_ASYNCABLE_DECLARE(empty); - GDAL_ASYNCABLE_DECLARE(exportToKML); - GDAL_ASYNCABLE_DECLARE(exportToGML); - GDAL_ASYNCABLE_DECLARE(exportToJSON); - GDAL_ASYNCABLE_DECLARE(exportToWKT); - GDAL_ASYNCABLE_DECLARE(exportToWKB); - GDAL_ASYNCABLE_DECLARE(closeRings); - GDAL_ASYNCABLE_DECLARE(segmentize); - GDAL_ASYNCABLE_DECLARE(intersects); - GDAL_ASYNCABLE_DECLARE(equals); - GDAL_ASYNCABLE_DECLARE(disjoint); - GDAL_ASYNCABLE_DECLARE(touches); - GDAL_ASYNCABLE_DECLARE(crosses); - GDAL_ASYNCABLE_DECLARE(within); - GDAL_ASYNCABLE_DECLARE(contains); - GDAL_ASYNCABLE_DECLARE(overlaps); - GDAL_ASYNCABLE_DECLARE(boundary); - GDAL_ASYNCABLE_DECLARE(distance); - GDAL_ASYNCABLE_DECLARE(convexHull); - GDAL_ASYNCABLE_DECLARE(buffer); - GDAL_ASYNCABLE_DECLARE(intersection); - GDAL_ASYNCABLE_DECLARE(unionGeometry); - GDAL_ASYNCABLE_DECLARE(difference); - GDAL_ASYNCABLE_DECLARE(symDifference); - GDAL_ASYNCABLE_DECLARE(centroid); - GDAL_ASYNCABLE_DECLARE(simplify); - GDAL_ASYNCABLE_DECLARE(simplifyPreserveTopology); - GDAL_ASYNCABLE_DECLARE(polygonize); - GDAL_ASYNCABLE_DECLARE(swapXY); - static NAN_METHOD(getNumGeometries); - GDAL_ASYNCABLE_DECLARE(getEnvelope); - GDAL_ASYNCABLE_DECLARE(getEnvelope3D); - GDAL_ASYNCABLE_DECLARE(flattenTo2D); - GDAL_ASYNCABLE_DECLARE(transform); - GDAL_ASYNCABLE_DECLARE(transformTo); - - // static constructor methods - GDAL_ASYNCABLE_DECLARE(create); - GDAL_ASYNCABLE_DECLARE(createFromWkt); - GDAL_ASYNCABLE_DECLARE(createFromWkb); - GDAL_ASYNCABLE_DECLARE(createFromGeoJson); - static NAN_METHOD(getName); - static NAN_METHOD(getConstructor); - - static NAN_GETTER(srsGetter); - static NAN_GETTER(typeGetter); - static NAN_GETTER(nameGetter); - static NAN_GETTER(wkbSizeGetter); - static NAN_GETTER(dimensionGetter); - static NAN_GETTER(coordinateDimensionGetter); - - static NAN_SETTER(srsSetter); - static NAN_SETTER(coordinateDimensionSetter); - - static OGRwkbGeometryType getGeometryType_fixed(OGRGeometry *geom); - static Local getConstructor(OGRwkbGeometryType type); -}; - } // namespace node_gdal #endif diff --git a/src/gdal_geometrycollection.cpp b/src/geometry/gdal_geometrycollection.cpp similarity index 95% rename from src/gdal_geometrycollection.cpp rename to src/geometry/gdal_geometrycollection.cpp index 2a20a12cc..f9ce3423f 100644 --- a/src/gdal_geometrycollection.cpp +++ b/src/geometry/gdal_geometrycollection.cpp @@ -1,7 +1,7 @@ #include "gdal_geometrycollection.hpp" -#include "collections/geometry_collection_children.hpp" -#include "gdal_common.hpp" +#include "../collections/geometry_collection_children.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include diff --git a/src/geometry/gdal_geometrycollection.hpp b/src/geometry/gdal_geometrycollection.hpp new file mode 100644 index 000000000..d7cc98c84 --- /dev/null +++ b/src/geometry/gdal_geometrycollection.hpp @@ -0,0 +1,39 @@ +#ifndef __NODE_OGR_GEOMETRYCOLLECTION_H__ +#define __NODE_OGR_GEOMETRYCOLLECTION_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +using namespace v8; +using namespace node; + +#include "gdal_geometrybase.hpp" +#include "gdal_geometrycollectionbase.hpp" +#include "../collections/geometry_collection_children.hpp" + +namespace node_gdal { + +class GeometryCollection : public GeometryCollectionBase { + + public: + static Nan::Persistent constructor; + using GeometryCollectionBase::GeometryCollectionBase; + + static void Initialize(Local target); + using GeometryCollectionBase::New; + static NAN_METHOD(toString); + static NAN_METHOD(getArea); + static NAN_METHOD(getLength); + + static NAN_GETTER(childrenGetter); +}; + +} // namespace node_gdal +#endif diff --git a/src/gdal_geometrycollection.hpp b/src/geometry/gdal_geometrycollectionbase.hpp similarity index 62% rename from src/gdal_geometrycollection.hpp rename to src/geometry/gdal_geometrycollectionbase.hpp index 96ebd013a..fcd292418 100644 --- a/src/gdal_geometrycollection.hpp +++ b/src/geometry/gdal_geometrycollectionbase.hpp @@ -1,12 +1,12 @@ -#ifndef __NODE_OGR_GEOMETRYCOLLECTION_H__ -#define __NODE_OGR_GEOMETRYCOLLECTION_H__ +#ifndef __NODE_OGR_GEOMETRYCOLLECTIONBASE_H__ +#define __NODE_OGR_GEOMETRYCOLLECTIONBASE_H__ // node #include #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include @@ -14,14 +14,16 @@ using namespace v8; using namespace node; -#include "gdal_geometry.hpp" -#include "collections/geometry_collection_children.hpp" +#include "gdal_geometrybase.hpp" +#include "../collections/geometry_collection_children.hpp" namespace node_gdal { template class GeometryCollectionBase : public GeometryBase { + public: using GeometryBase::GeometryBase; + static NAN_METHOD(New); using GeometryBase::New; }; @@ -55,20 +57,5 @@ template NAN_METHOD((GeometryCollectionBase::New) info.GetReturnValue().Set(info.This()); } -class GeometryCollection : public GeometryCollectionBase { - - public: - static Nan::Persistent constructor; - using GeometryCollectionBase::GeometryCollectionBase; - - static void Initialize(Local target); - using GeometryCollectionBase::New; - static NAN_METHOD(toString); - static NAN_METHOD(getArea); - static NAN_METHOD(getLength); - - static NAN_GETTER(childrenGetter); -}; - } // namespace node_gdal #endif diff --git a/src/gdal_linearring.cpp b/src/geometry/gdal_linearring.cpp similarity index 59% rename from src/gdal_linearring.cpp rename to src/geometry/gdal_linearring.cpp index 3db26cf05..843123917 100644 --- a/src/gdal_linearring.cpp +++ b/src/geometry/gdal_linearring.cpp @@ -1,7 +1,7 @@ #include "gdal_linearring.hpp" -#include "collections/linestring_points.hpp" -#include "gdal_common.hpp" +#include "../collections/linestring_points.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include "gdal_linestring.hpp" @@ -34,34 +34,6 @@ void LinearRing::Initialize(Local target) { * @class gdal.LinearRing * @extends gdal.LineString */ -NAN_METHOD(LinearRing::New) { - Nan::HandleScope scope; - LinearRing *f; - - if (!info.IsConstructCall()) { - Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword"); - return; - } - - if (info[0]->IsExternal()) { - Local ext = info[0].As(); - void *ptr = ext->Value(); - f = static_cast(ptr); - - } else { - if (info.Length() != 0) { - Nan::ThrowError("LinearRing constructor doesn't take any arguments"); - return; - } - f = new LinearRing(new OGRLinearRing()); - } - - Local points = LineStringPoints::New(info.This()); - Nan::SetPrivate(info.This(), Nan::New("points_").ToLocalChecked(), points); - - f->Wrap(info.This()); - info.GetReturnValue().Set(info.This()); -} NAN_METHOD(LinearRing::toString) { Nan::HandleScope scope; diff --git a/src/gdal_linearring.hpp b/src/geometry/gdal_linearring.hpp similarity index 58% rename from src/gdal_linearring.hpp rename to src/geometry/gdal_linearring.hpp index f7191d847..9ba3bdfc4 100644 --- a/src/gdal_linearring.hpp +++ b/src/geometry/gdal_linearring.hpp @@ -6,27 +6,27 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometry.hpp" +#include "gdal_curve.hpp" +#include "../collections/linestring_points.hpp" using namespace v8; using namespace node; namespace node_gdal { -class LinearRing : public GeometryBase { +class LinearRing : public Curve { public: static Nan::Persistent constructor; - using GeometryBase::GeometryBase; + using Curve::Curve; static void Initialize(Local target); - static NAN_METHOD(New); - using GeometryBase::New; + using Curve::New; static NAN_METHOD(toString); static NAN_METHOD(getArea); }; diff --git a/src/gdal_linestring.cpp b/src/geometry/gdal_linestring.cpp similarity index 80% rename from src/gdal_linestring.cpp rename to src/geometry/gdal_linestring.cpp index 10dad2a72..92771bed2 100644 --- a/src/gdal_linestring.cpp +++ b/src/geometry/gdal_linestring.cpp @@ -1,7 +1,7 @@ #include "gdal_linestring.hpp" -#include "collections/linestring_points.hpp" -#include "gdal_common.hpp" +#include "../collections/linestring_points.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include "gdal_point.hpp" @@ -44,34 +44,6 @@ void LineString::Initialize(Local target) { * @class gdal.LineString * @extends gdal.Geometry */ -NAN_METHOD(LineString::New) { - Nan::HandleScope scope; - LineString *f; - - if (!info.IsConstructCall()) { - Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword"); - return; - } - - if (info[0]->IsExternal()) { - Local ext = info[0].As(); - void *ptr = ext->Value(); - f = static_cast(ptr); - - } else { - if (info.Length() != 0) { - Nan::ThrowError("LineString constructor doesn't take any arguments"); - return; - } - f = new LineString(new OGRLineString()); - } - - Local points = LineStringPoints::New(info.This()); - Nan::SetPrivate(info.This(), Nan::New("points_").ToLocalChecked(), points); - - f->Wrap(info.This()); - info.GetReturnValue().Set(info.This()); -} NAN_METHOD(LineString::toString) { Nan::HandleScope scope; diff --git a/src/gdal_linestring.hpp b/src/geometry/gdal_linestring.hpp similarity index 63% rename from src/gdal_linestring.hpp rename to src/geometry/gdal_linestring.hpp index af4c2afdc..2156c8c55 100644 --- a/src/gdal_linestring.hpp +++ b/src/geometry/gdal_linestring.hpp @@ -6,27 +6,27 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometry.hpp" +#include "gdal_curve.hpp" +#include "../collections/linestring_points.hpp" using namespace v8; using namespace node; namespace node_gdal { -class LineString : public GeometryBase { +class LineString : public Curve { public: static Nan::Persistent constructor; - using GeometryBase::GeometryBase; + using Curve::Curve; static void Initialize(Local target); - static NAN_METHOD(New); - using GeometryBase::New; + using Curve::New; static NAN_METHOD(toString); static NAN_METHOD(getLength); static NAN_METHOD(value); diff --git a/src/gdal_multilinestring.cpp b/src/geometry/gdal_multilinestring.cpp similarity index 94% rename from src/gdal_multilinestring.cpp rename to src/geometry/gdal_multilinestring.cpp index 050c46ca7..f0d076cfe 100644 --- a/src/gdal_multilinestring.cpp +++ b/src/geometry/gdal_multilinestring.cpp @@ -1,7 +1,7 @@ #include "gdal_multilinestring.hpp" -#include "collections/geometry_collection_children.hpp" -#include "gdal_common.hpp" +#include "../collections/geometry_collection_children.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include "gdal_geometrycollection.hpp" #include "gdal_linestring.hpp" diff --git a/src/gdal_multilinestring.hpp b/src/geometry/gdal_multilinestring.hpp similarity index 91% rename from src/gdal_multilinestring.hpp rename to src/geometry/gdal_multilinestring.hpp index 598d5ab9c..e03a164c5 100644 --- a/src/gdal_multilinestring.hpp +++ b/src/geometry/gdal_multilinestring.hpp @@ -6,12 +6,12 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometrycollection.hpp" +#include "gdal_geometrycollectionbase.hpp" using namespace v8; using namespace node; diff --git a/src/gdal_multipoint.cpp b/src/geometry/gdal_multipoint.cpp similarity index 91% rename from src/gdal_multipoint.cpp rename to src/geometry/gdal_multipoint.cpp index cd4a7f375..a3273c9d6 100644 --- a/src/gdal_multipoint.cpp +++ b/src/geometry/gdal_multipoint.cpp @@ -1,7 +1,7 @@ #include "gdal_multipoint.hpp" -#include "collections/geometry_collection_children.hpp" -#include "gdal_common.hpp" +#include "../collections/geometry_collection_children.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include "gdal_geometrycollection.hpp" #include "gdal_point.hpp" diff --git a/src/gdal_multipoint.hpp b/src/geometry/gdal_multipoint.hpp similarity index 90% rename from src/gdal_multipoint.hpp rename to src/geometry/gdal_multipoint.hpp index d9f1e4078..f51c06775 100644 --- a/src/gdal_multipoint.hpp +++ b/src/geometry/gdal_multipoint.hpp @@ -6,12 +6,12 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometrycollection.hpp" +#include "gdal_geometrycollectionbase.hpp" using namespace v8; using namespace node; diff --git a/src/geometry/gdal_multipolygon.cpp b/src/geometry/gdal_multipolygon.cpp new file mode 100644 index 000000000..178a5ae8e --- /dev/null +++ b/src/geometry/gdal_multipolygon.cpp @@ -0,0 +1,64 @@ +#include "gdal_multipolygon.hpp" +#include "../collections/geometry_collection_children.hpp" +#include "../gdal_common.hpp" +#include "gdal_geometry.hpp" +#include "gdal_geometrycollection.hpp" +#include "gdal_polygon.hpp" + +#include + +namespace node_gdal { + +Nan::Persistent MultiPolygon::constructor; + +void MultiPolygon::Initialize(Local target) { + Nan::HandleScope scope; + + Local lcons = Nan::New(MultiPolygon::New); + lcons->Inherit(Nan::New(GeometryCollection::constructor)); + lcons->InstanceTemplate()->SetInternalFieldCount(1); + lcons->SetClassName(Nan::New("MultiPolygon").ToLocalChecked()); + + Nan::SetPrototypeMethod(lcons, "toString", toString); + Nan::SetPrototypeMethod(lcons, "unionCascaded", unionCascaded); + Nan::SetPrototypeMethod(lcons, "getArea", getArea); + + Nan::Set(target, Nan::New("MultiPolygon").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); + + constructor.Reset(lcons); +} + +/** + * @constructor + * @class gdal.MultiPolygon + * @extends gdal.GeometryCollection + */ + +NAN_METHOD(MultiPolygon::toString) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::New("MultiPolygon").ToLocalChecked()); +} + +/** + * Unions all the geometries and returns the result. + * + * @method unionCascaded + * @return {gdal.Geometry} + */ +NAN_METHOD(MultiPolygon::unionCascaded) { + Nan::HandleScope scope; + + MultiPolygon *geom = Nan::ObjectWrap::Unwrap(info.This()); + + info.GetReturnValue().Set(Geometry::New(geom->this_->UnionCascaded())); +} + +/** + * Computes the combined area of the collection. + * + * @method getArea + * @return {Number} + */ +NODE_WRAPPED_METHOD_WITH_RESULT(MultiPolygon, getArea, Number, get_Area); + +} // namespace node_gdal diff --git a/src/gdal_multipolygon.hpp b/src/geometry/gdal_multipolygon.hpp similarity index 50% rename from src/gdal_multipolygon.hpp rename to src/geometry/gdal_multipolygon.hpp index 04d2f26ce..f2f89c023 100644 --- a/src/gdal_multipolygon.hpp +++ b/src/geometry/gdal_multipolygon.hpp @@ -6,44 +6,29 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometrycollection.hpp" +#include "gdal_geometrycollectionbase.hpp" using namespace v8; using namespace node; namespace node_gdal { -class MultiPolygon : public GeometryCollection { +class MultiPolygon : public GeometryCollectionBase { public: static Nan::Persistent constructor; + using GeometryCollectionBase::GeometryCollectionBase; static void Initialize(Local target); - static NAN_METHOD(New); - static Local New(OGRMultiPolygon *geom); - static Local New(OGRMultiPolygon *geom, bool owned); + using GeometryCollectionBase::New; static NAN_METHOD(toString); static NAN_METHOD(unionCascaded); static NAN_METHOD(getArea); - - MultiPolygon(); - MultiPolygon(OGRMultiPolygon *geom); - inline OGRMultiPolygon *get() { - return this_; - } - inline bool isAlive() { - return this_; - } - - protected: - ~MultiPolygon(); - private: - OGRMultiPolygon *this_; }; } // namespace node_gdal diff --git a/src/gdal_point.cpp b/src/geometry/gdal_point.cpp similarity index 99% rename from src/gdal_point.cpp rename to src/geometry/gdal_point.cpp index b55e8c281..0121b9a3b 100644 --- a/src/gdal_point.cpp +++ b/src/geometry/gdal_point.cpp @@ -1,5 +1,5 @@ #include "gdal_point.hpp" -#include "gdal_common.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include diff --git a/src/gdal_point.hpp b/src/geometry/gdal_point.hpp similarity index 92% rename from src/gdal_point.hpp rename to src/geometry/gdal_point.hpp index f1bdb694e..c9d4f2fe1 100644 --- a/src/gdal_point.hpp +++ b/src/geometry/gdal_point.hpp @@ -6,12 +6,12 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometry.hpp" +#include "gdal_geometrybase.hpp" using namespace v8; using namespace node; @@ -19,7 +19,6 @@ using namespace node; namespace node_gdal { class Point : public GeometryBase { - public: static Nan::Persistent constructor; using GeometryBase::GeometryBase; diff --git a/src/gdal_polygon.cpp b/src/geometry/gdal_polygon.cpp similarity index 64% rename from src/gdal_polygon.cpp rename to src/geometry/gdal_polygon.cpp index 194d8de48..31cabf099 100644 --- a/src/gdal_polygon.cpp +++ b/src/geometry/gdal_polygon.cpp @@ -1,7 +1,7 @@ #include "gdal_polygon.hpp" -#include "collections/polygon_rings.hpp" -#include "gdal_common.hpp" +#include "../collections/polygon_rings.hpp" +#include "../gdal_common.hpp" #include "gdal_geometry.hpp" #include @@ -28,6 +28,10 @@ void Polygon::Initialize(Local target) { constructor.Reset(lcons); } +void Polygon::SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE _this, v8::Local value) { + Nan::SetPrivate(_this, Nan::New("rings_").ToLocalChecked(), value); +}; + /** * Concrete class representing polygons. * @@ -35,34 +39,6 @@ void Polygon::Initialize(Local target) { * @class gdal.Polygon * @extends gdal.Geometry */ -NAN_METHOD(Polygon::New) { - Nan::HandleScope scope; - Polygon *f; - - if (!info.IsConstructCall()) { - Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword"); - return; - } - - if (info[0]->IsExternal()) { - Local ext = info[0].As(); - void *ptr = ext->Value(); - f = static_cast(ptr); - - } else { - if (info.Length() != 0) { - Nan::ThrowError("Polygon constructor doesn't take any arguments"); - return; - } - f = new Polygon(new OGRPolygon()); - } - - Local rings = PolygonRings::New(info.This()); - Nan::SetPrivate(info.This(), Nan::New("rings_").ToLocalChecked(), rings); - - f->Wrap(info.This()); - info.GetReturnValue().Set(info.This()); -} NAN_METHOD(Polygon::toString) { Nan::HandleScope scope; diff --git a/src/gdal_polygon.hpp b/src/geometry/gdal_polygon.hpp similarity index 54% rename from src/gdal_polygon.hpp rename to src/geometry/gdal_polygon.hpp index 238e80808..529e80348 100644 --- a/src/gdal_polygon.hpp +++ b/src/geometry/gdal_polygon.hpp @@ -6,31 +6,35 @@ #include // nan -#include "nan-wrapper.h" +#include "../nan-wrapper.h" // ogr #include -#include "gdal_geometry.hpp" +#include "gdal_curve.hpp" +#include "../collections/polygon_rings.hpp" using namespace v8; using namespace node; namespace node_gdal { -class Polygon : public GeometryBase { +class Polygon : public Curve { + friend Curve; public: static Nan::Persistent constructor; - using GeometryBase::GeometryBase; + using Curve::Curve; static void Initialize(Local target); - static NAN_METHOD(New); - using GeometryBase::New; + using Curve::New; static NAN_METHOD(toString); static NAN_METHOD(getArea); static NAN_GETTER(ringsGetter); + + protected: + static void SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE, v8::Local); }; } // namespace node_gdal diff --git a/src/node_gdal.cpp b/src/node_gdal.cpp index 485d65d23..3b37c243f 100644 --- a/src/node_gdal.cpp +++ b/src/node_gdal.cpp @@ -62,16 +62,17 @@ #include "gdal_feature.hpp" #include "gdal_feature_defn.hpp" #include "gdal_field_defn.hpp" -#include "gdal_geometry.hpp" -#include "gdal_geometrycollection.hpp" +#include "geometry/gdal_geometry.hpp" +#include "geometry/gdal_geometrycollection.hpp" #include "gdal_layer.hpp" -#include "gdal_linearring.hpp" -#include "gdal_linestring.hpp" -#include "gdal_multilinestring.hpp" -#include "gdal_multipoint.hpp" -#include "gdal_multipolygon.hpp" -#include "gdal_point.hpp" -#include "gdal_polygon.hpp" +#include "geometry/gdal_linearring.hpp" +#include "geometry/gdal_linestring.hpp" +#include "geometry/gdal_circularstring.hpp" +#include "geometry/gdal_multilinestring.hpp" +#include "geometry/gdal_multipoint.hpp" +#include "geometry/gdal_multipolygon.hpp" +#include "geometry/gdal_point.hpp" +#include "geometry/gdal_polygon.hpp" #include "gdal_spatial_reference.hpp" #include "gdal_memfile.hpp" @@ -284,6 +285,7 @@ static void Init(Local target, Local, void *) { Geometry::Initialize(target); Point::Initialize(target); LineString::Initialize(target); + CircularString::Initialize(target); LinearRing::Initialize(target); Polygon::Initialize(target); GeometryCollection::Initialize(target); @@ -1015,6 +1017,12 @@ static void Init(Local target, Local, void *) { * @type {integer} */ Nan::Set(target, Nan::New("wkbLineString").ToLocalChecked(), Nan::New(wkbLineString)); + /** + * @final + * @property gdal.wkbLineString + * @type {integer} + */ + Nan::Set(target, Nan::New("wkbCircularString").ToLocalChecked(), Nan::New(wkbCircularString)); /** * @final * @property gdal.wkbPolygon diff --git a/src/utils/warp_options.cpp b/src/utils/warp_options.cpp index 8ac39ba8d..0f5f3c2e0 100644 --- a/src/utils/warp_options.cpp +++ b/src/utils/warp_options.cpp @@ -1,6 +1,6 @@ #include "warp_options.hpp" #include "../gdal_common.hpp" -#include "../gdal_geometry.hpp" +#include "../geometry/gdal_geometry.hpp" #include namespace node_gdal { diff --git a/test/api_geometry.test.js b/test/api_geometry.test.js index 0bd105902..f89466de0 100644 --- a/test/api_geometry.test.js +++ b/test/api_geometry.test.js @@ -225,6 +225,7 @@ describe('gdal.Geometry', () => { assert.equal(gdal.Geometry.getConstructor(5), gdal.MultiLineString) assert.equal(gdal.Geometry.getConstructor(6), gdal.MultiPolygon) assert.equal(gdal.Geometry.getConstructor(7), gdal.GeometryCollection) + assert.equal(gdal.Geometry.getConstructor(8), gdal.CircularString) assert.equal(gdal.Geometry.getConstructor(101), gdal.LinearRing) }) }) @@ -237,6 +238,7 @@ describe('gdal.Geometry', () => { assert.equal(gdal.MultiLineString.wkbType, 5) assert.equal(gdal.MultiPolygon.wkbType, 6) assert.equal(gdal.GeometryCollection.wkbType, 7) + assert.equal(gdal.CircularString.wkbType, 8) assert.equal(gdal.LinearRing.wkbType, 101) }) }) @@ -251,6 +253,7 @@ describe('gdal.Geometry', () => { assert.equal(new gdal.MultiLineString().wkbType, 5) assert.equal(new gdal.MultiPolygon().wkbType, 6) assert.equal(new gdal.GeometryCollection().wkbType, 7) + assert.equal(new gdal.CircularString().wkbType, 8) assert.equal(new gdal.LinearRing().wkbType, 101) }) }) @@ -291,7 +294,7 @@ describe('gdal.Geometry', () => { // comparison functions (function () { - let ring, square, point_inner, point_inner_clone, point_outer + let ring, square, point_inner, point_inner_clone, point_outer, arc before(() => { ring = new gdal.LinearRing() ring.points.add({ x: 0, y: 0 }) @@ -306,6 +309,11 @@ describe('gdal.Geometry', () => { point_inner = new gdal.Point(5, 5) point_outer = new gdal.Point(0, 20) point_inner_clone = new gdal.Point(5, 5) + + arc = new gdal.CircularString() + arc.points.add({ x: 0, y: 5 }) + arc.points.add({ x: 10, y: 0 }) + arc.points.add({ x: 0, y: -5 }) }) describe('contains()', () => { it('should return correct result', () => { @@ -363,6 +371,8 @@ describe('gdal.Geometry', () => { assert.equal(point_outer.crosses(square), false) assert.equal(line_cross.crosses(square), true) assert.equal(line_nocross.crosses(square), false) + assert.equal(line_cross.crosses(arc), true) + assert.equal(line_nocross.crosses(arc), false) }) }) describe('overlaps()', () => { From b1f542b09ded9a60df0759344db0783f6495c5f7 Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Mon, 3 May 2021 16:38:15 +0200 Subject: [PATCH 2/6] add a SimpleCurve abstraction level --- binding.gyp | 1 + src/geometry/gdal_circularstring.cpp | 90 +----------- src/geometry/gdal_circularstring.hpp | 13 +- .../{gdal_curve.hpp => gdal_curvebase.hpp} | 10 +- src/geometry/gdal_geometrybase.hpp | 27 ++-- src/geometry/gdal_linearring.hpp | 8 +- src/geometry/gdal_linestring.cpp | 90 +----------- src/geometry/gdal_linestring.hpp | 11 +- src/geometry/gdal_polygon.hpp | 10 +- src/geometry/gdal_simplecurve.cpp | 135 ++++++++++++++++++ src/geometry/gdal_simplecurve.hpp | 39 +++++ src/node_gdal.cpp | 2 + test/api_circularstring.test.js | 39 +++++ test/api_linearring.test.js | 1 + test/api_linestring.test.js | 1 + 15 files changed, 262 insertions(+), 215 deletions(-) rename src/geometry/{gdal_curve.hpp => gdal_curvebase.hpp} (76%) create mode 100644 src/geometry/gdal_simplecurve.cpp create mode 100644 src/geometry/gdal_simplecurve.hpp create mode 100644 test/api_circularstring.test.js diff --git a/binding.gyp b/binding.gyp index 2d548408e..261fe7667 100644 --- a/binding.gyp +++ b/binding.gyp @@ -31,6 +31,7 @@ "src/gdal_field_defn.cpp", "src/geometry/gdal_geometry.cpp", "src/geometry/gdal_point.cpp", + "src/geometry/gdal_simplecurve.cpp", "src/geometry/gdal_linestring.cpp", "src/geometry/gdal_circularstring.cpp", "src/geometry/gdal_linearring.cpp", diff --git a/src/geometry/gdal_circularstring.cpp b/src/geometry/gdal_circularstring.cpp index bc4cd8883..5e0bf8d09 100644 --- a/src/geometry/gdal_circularstring.cpp +++ b/src/geometry/gdal_circularstring.cpp @@ -4,6 +4,7 @@ #include "../collections/linestring_points.hpp" #include "../gdal_common.hpp" #include "gdal_geometry.hpp" +#include "gdal_simplecurve.hpp" #include "gdal_point.hpp" #include @@ -16,16 +17,11 @@ void CircularString::Initialize(Local target) { Nan::HandleScope scope; Local lcons = Nan::New(CircularString::New); - lcons->Inherit(Nan::New(Geometry::constructor)); + lcons->Inherit(Nan::New(SimpleCurve::constructor)); lcons->InstanceTemplate()->SetInternalFieldCount(1); lcons->SetClassName(Nan::New("CircularString").ToLocalChecked()); Nan::SetPrototypeMethod(lcons, "toString", toString); - Nan::SetPrototypeMethod(lcons, "getLength", getLength); - Nan::SetPrototypeMethod(lcons, "value", value); - Nan::SetPrototypeMethod(lcons, "addSubLineString", addSubLineString); - - ATTR(lcons, "points", pointsGetter, READ_ONLY_SETTER); Nan::Set(target, Nan::New("CircularString").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); @@ -51,86 +47,4 @@ NAN_METHOD(CircularString::toString) { info.GetReturnValue().Set(Nan::New("CircularString").ToLocalChecked()); } -/** - * Computes the length of the arc. - * - * @method getLength - * @return Number - */ -NODE_WRAPPED_METHOD_WITH_RESULT(CircularString, getLength, Number, get_Length); - -/** - * Returns the point at the specified distance along the arc. - * - * @method value - * @param {Number} distance - * @return {gdal.Point} - */ -NAN_METHOD(CircularString::value) { - Nan::HandleScope scope; - - CircularString *geom = Nan::ObjectWrap::Unwrap(info.This()); - - OGRPoint *pt = new OGRPoint(); - double dist; - - NODE_ARG_DOUBLE(0, "distance", dist); - - geom->this_->Value(dist, pt); - - info.GetReturnValue().Set(Point::New(pt)); -} - -/** - * Add a segment of another LineString to this one. - * - * Adds the request range of vertices to the end of this compound curve in an - * efficient manner. If the start index is larger than the end index then the - * vertices will be reversed as they are copied. - * - * @method addSubLineString - * @param {gdal.LineString} LineString to be added - * @param {int} [start=0] the first vertex to copy, defaults to 0 to start with - * the first vertex in the other LineString - * @param {int} [end=-1] the last vertex to copy, defaults to -1 indicating the - * last vertex of the other LineString - * @return {void} - */ -NAN_METHOD(CircularString::addSubLineString) { - Nan::HandleScope scope; - - CircularString *geom = Nan::ObjectWrap::Unwrap(info.This()); - LineString *other; - int start = 0; - int end = -1; - - NODE_ARG_WRAPPED(0, "line", LineString, other); - NODE_ARG_INT_OPT(1, "start", start); - NODE_ARG_INT_OPT(2, "end", end); - - int n = other->get()->getNumPoints(); - - if (start < 0 || end < -1 || start >= n || end >= n) { - Nan::ThrowRangeError("Invalid start or end index for CircularString"); - return; - } - - geom->this_->addSubLineString(other->get(), start, end); - - UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(geom); - - return; -} - -/** - * Points that make up the arc. - * - * @attribute points - * @type {gdal.CircularStringPoints} - */ -NAN_GETTER(CircularString::pointsGetter) { - Nan::HandleScope scope; - info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("points_").ToLocalChecked()).ToLocalChecked()); -} - } // namespace node_gdal diff --git a/src/geometry/gdal_circularstring.hpp b/src/geometry/gdal_circularstring.hpp index 51ec3ec5b..965930c32 100644 --- a/src/geometry/gdal_circularstring.hpp +++ b/src/geometry/gdal_circularstring.hpp @@ -11,7 +11,7 @@ // ogr #include -#include "gdal_curve.hpp" +#include "gdal_curvebase.hpp" #include "../collections/linestring_points.hpp" using namespace v8; @@ -19,20 +19,15 @@ using namespace node; namespace node_gdal { -class CircularString : public Curve { +class CircularString : public CurveBase { public: static Nan::Persistent constructor; - using Curve::Curve; + using CurveBase::CurveBase; static void Initialize(Local target); - using Curve::New; + using CurveBase::New; static NAN_METHOD(toString); - static NAN_METHOD(getLength); - static NAN_METHOD(value); - static NAN_METHOD(addSubLineString); - - static NAN_GETTER(pointsGetter); }; } // namespace node_gdal diff --git a/src/geometry/gdal_curve.hpp b/src/geometry/gdal_curvebase.hpp similarity index 76% rename from src/geometry/gdal_curve.hpp rename to src/geometry/gdal_curvebase.hpp index ec5fef7cd..f0d9c4e54 100644 --- a/src/geometry/gdal_curve.hpp +++ b/src/geometry/gdal_curvebase.hpp @@ -1,5 +1,5 @@ -#ifndef __NODE_OGR_CURVE_H__ -#define __NODE_OGR_CURVE_H__ +#ifndef __NODE_OGR_CURVEBASE_H__ +#define __NODE_OGR_CURVEBASE_H__ // node #include @@ -18,7 +18,7 @@ using namespace node; namespace node_gdal { -template class Curve : public GeometryBase { +template class CurveBase : public GeometryBase { public: using GeometryBase::GeometryBase; static NAN_METHOD(New); @@ -28,7 +28,7 @@ template class Curve : public GeometryBase); }; -template NAN_METHOD((Curve::New)) { +template NAN_METHOD((CurveBase::New)) { Nan::HandleScope scope; T *f; @@ -58,7 +58,7 @@ template NAN_METHOD((Curve: } template -void Curve::SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE _this, v8::Local value) { +void CurveBase::SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE _this, v8::Local value) { Nan::SetPrivate(_this, Nan::New("points_").ToLocalChecked(), value); }; diff --git a/src/geometry/gdal_geometrybase.hpp b/src/geometry/gdal_geometrybase.hpp index 63ac2027f..159ad2927 100644 --- a/src/geometry/gdal_geometrybase.hpp +++ b/src/geometry/gdal_geometrybase.hpp @@ -19,21 +19,30 @@ namespace node_gdal { * Geometry class inheritance hierarchy * It uses CRTP - https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern * to get around the fact that the methods exposed to JS are static and cannot be virtual + * * * C++ + * (maximizes code reuse) + * * GeometryBase<> - * | \ \ \ - * Geometry Point Curve<> GeometryCollectionBase<> - * | \ \ \ | \ - * LineString Polygon LinearRing CircularString GeometryCollection Multi* + * | \ \ \ + * Geometry Point CurveBase<> GeometryCollectionBase<> + * | \ \ \ \ | \ + * SimpleCurve LineString Polygon LinearRing CircularString GeometryCollection Multi* + * + * * * JS + * (tries to follow GDAL and the ISO specification) + * * Geometry - * | \ \ \ \ - * Point LineString Polygon CircularString GeometryCollection - * | | - * LinearRing Multi* - * + * | \ \ \ + * Point SimpleCurve Polygon GeometryCollection + * | \ | + * LineString CircularString Multi* + * | + * LinearRing + * * * The full GDAL OGRGeometry class hierarchy * https://gdal.org/doxygen/classOGRGeometry.html diff --git a/src/geometry/gdal_linearring.hpp b/src/geometry/gdal_linearring.hpp index 9ba3bdfc4..f56bcffa8 100644 --- a/src/geometry/gdal_linearring.hpp +++ b/src/geometry/gdal_linearring.hpp @@ -11,7 +11,7 @@ // ogr #include -#include "gdal_curve.hpp" +#include "gdal_curvebase.hpp" #include "../collections/linestring_points.hpp" using namespace v8; @@ -19,14 +19,14 @@ using namespace node; namespace node_gdal { -class LinearRing : public Curve { +class LinearRing : public CurveBase { public: static Nan::Persistent constructor; - using Curve::Curve; + using CurveBase::CurveBase; static void Initialize(Local target); - using Curve::New; + using CurveBase::New; static NAN_METHOD(toString); static NAN_METHOD(getArea); }; diff --git a/src/geometry/gdal_linestring.cpp b/src/geometry/gdal_linestring.cpp index 92771bed2..24fdaaef9 100644 --- a/src/geometry/gdal_linestring.cpp +++ b/src/geometry/gdal_linestring.cpp @@ -3,6 +3,7 @@ #include "../collections/linestring_points.hpp" #include "../gdal_common.hpp" #include "gdal_geometry.hpp" +#include "gdal_simplecurve.hpp" #include "gdal_point.hpp" #include @@ -15,16 +16,11 @@ void LineString::Initialize(Local target) { Nan::HandleScope scope; Local lcons = Nan::New(LineString::New); - lcons->Inherit(Nan::New(Geometry::constructor)); + lcons->Inherit(Nan::New(SimpleCurve::constructor)); lcons->InstanceTemplate()->SetInternalFieldCount(1); lcons->SetClassName(Nan::New("LineString").ToLocalChecked()); Nan::SetPrototypeMethod(lcons, "toString", toString); - Nan::SetPrototypeMethod(lcons, "getLength", getLength); - Nan::SetPrototypeMethod(lcons, "value", value); - Nan::SetPrototypeMethod(lcons, "addSubLineString", addSubLineString); - - ATTR(lcons, "points", pointsGetter, READ_ONLY_SETTER); Nan::Set(target, Nan::New("LineString").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); @@ -50,86 +46,4 @@ NAN_METHOD(LineString::toString) { info.GetReturnValue().Set(Nan::New("LineString").ToLocalChecked()); } -/** - * Computes the length of the line string. - * - * @method getLength - * @return Number - */ -NODE_WRAPPED_METHOD_WITH_RESULT(LineString, getLength, Number, get_Length); - -/** - * Returns the point at the specified distance along the line string. - * - * @method value - * @param {Number} distance - * @return {gdal.Point} - */ -NAN_METHOD(LineString::value) { - Nan::HandleScope scope; - - LineString *geom = Nan::ObjectWrap::Unwrap(info.This()); - - OGRPoint *pt = new OGRPoint(); - double dist; - - NODE_ARG_DOUBLE(0, "distance", dist); - - geom->this_->Value(dist, pt); - - info.GetReturnValue().Set(Point::New(pt)); -} - -/** - * Add a segment of another linestring to this one. - * - * Adds the request range of vertices to the end of this line string in an - * efficient manner. If the start index is larger than the end index then the - * vertices will be reversed as they are copied. - * - * @method addSubLineString - * @param {gdal.LineString} line the other linestring - * @param {int} [start=0] the first vertex to copy, defaults to 0 to start with - * the first vertex in the other linestring - * @param {int} [end=-1] the last vertex to copy, defaults to -1 indicating the - * last vertex of the other linestring - * @return {void} - */ -NAN_METHOD(LineString::addSubLineString) { - Nan::HandleScope scope; - - LineString *geom = Nan::ObjectWrap::Unwrap(info.This()); - LineString *other; - int start = 0; - int end = -1; - - NODE_ARG_WRAPPED(0, "line", LineString, other); - NODE_ARG_INT_OPT(1, "start", start); - NODE_ARG_INT_OPT(2, "end", end); - - int n = other->get()->getNumPoints(); - - if (start < 0 || end < -1 || start >= n || end >= n) { - Nan::ThrowRangeError("Invalid start or end index for linestring"); - return; - } - - geom->this_->addSubLineString(other->get(), start, end); - - UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(geom); - - return; -} - -/** - * Points that make up the line string. - * - * @attribute points - * @type {gdal.LineStringPoints} - */ -NAN_GETTER(LineString::pointsGetter) { - Nan::HandleScope scope; - info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("points_").ToLocalChecked()).ToLocalChecked()); -} - } // namespace node_gdal diff --git a/src/geometry/gdal_linestring.hpp b/src/geometry/gdal_linestring.hpp index 2156c8c55..f7eb60cd4 100644 --- a/src/geometry/gdal_linestring.hpp +++ b/src/geometry/gdal_linestring.hpp @@ -11,7 +11,7 @@ // ogr #include -#include "gdal_curve.hpp" +#include "gdal_curvebase.hpp" #include "../collections/linestring_points.hpp" using namespace v8; @@ -19,18 +19,15 @@ using namespace node; namespace node_gdal { -class LineString : public Curve { +class LineString : public CurveBase { public: static Nan::Persistent constructor; - using Curve::Curve; + using CurveBase::CurveBase; static void Initialize(Local target); - using Curve::New; + using CurveBase::New; static NAN_METHOD(toString); - static NAN_METHOD(getLength); - static NAN_METHOD(value); - static NAN_METHOD(addSubLineString); static NAN_GETTER(pointsGetter); }; diff --git a/src/geometry/gdal_polygon.hpp b/src/geometry/gdal_polygon.hpp index 529e80348..7b4664cdb 100644 --- a/src/geometry/gdal_polygon.hpp +++ b/src/geometry/gdal_polygon.hpp @@ -11,7 +11,7 @@ // ogr #include -#include "gdal_curve.hpp" +#include "gdal_curvebase.hpp" #include "../collections/polygon_rings.hpp" using namespace v8; @@ -19,15 +19,15 @@ using namespace node; namespace node_gdal { -class Polygon : public Curve { - friend Curve; +class Polygon : public CurveBase { + friend CurveBase; public: static Nan::Persistent constructor; - using Curve::Curve; + using CurveBase::CurveBase; static void Initialize(Local target); - using Curve::New; + using CurveBase::New; static NAN_METHOD(toString); static NAN_METHOD(getArea); diff --git a/src/geometry/gdal_simplecurve.cpp b/src/geometry/gdal_simplecurve.cpp new file mode 100644 index 000000000..e3c198b84 --- /dev/null +++ b/src/geometry/gdal_simplecurve.cpp @@ -0,0 +1,135 @@ + +#include "gdal_simplecurve.hpp" +#include "../collections/linestring_points.hpp" +#include "../gdal_common.hpp" +#include "gdal_geometry.hpp" +#include "gdal_point.hpp" +#include "gdal_linestring.hpp" + +#include + +namespace node_gdal { + +Nan::Persistent SimpleCurve::constructor; + +void SimpleCurve::Initialize(Local target) { + Nan::HandleScope scope; + + Local lcons = Nan::New(SimpleCurve::New); + lcons->Inherit(Nan::New(Geometry::constructor)); + lcons->InstanceTemplate()->SetInternalFieldCount(1); + lcons->SetClassName(Nan::New("SimpleCurve").ToLocalChecked()); + + Nan::SetPrototypeMethod(lcons, "toString", toString); + Nan::SetPrototypeMethod(lcons, "getLength", getLength); + Nan::SetPrototypeMethod(lcons, "value", value); + Nan::SetPrototypeMethod(lcons, "addSubLineString", addSubLineString); + + ATTR(lcons, "points", pointsGetter, READ_ONLY_SETTER); + + Nan::Set(target, Nan::New("SimpleCurve").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); + + constructor.Reset(lcons); +} + +/** + * Abstract class representing all SimpleCurves. + * + * @constructor + * @class gdal.SimpleCurve + * @extends gdal.Geometry + */ +NAN_METHOD(SimpleCurve::New) { + Nan::HandleScope scope; + Nan::ThrowError("SimpleCurve is an abstract class and cannot be instantiated"); +} + +NAN_METHOD(SimpleCurve::toString) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::New("SimpleCurve").ToLocalChecked()); +} + +/** + * Returns the point at the specified distance along the SimpleCurve. + * + * @method value + * @param {Number} distance + * @return {gdal.Point} + */ +NAN_METHOD(SimpleCurve::value) { + Nan::HandleScope scope; + + SimpleCurve *geom = Nan::ObjectWrap::Unwrap(info.This()); + + OGRPoint *pt = new OGRPoint(); + double dist; + + NODE_ARG_DOUBLE(0, "distance", dist); + + geom->this_->Value(dist, pt); + + info.GetReturnValue().Set(Point::New(pt)); +} + +/** + * Compute the length of a multiSimpleCurve. + * + * @method getLength + * @return Number + */ +NODE_WRAPPED_METHOD_WITH_RESULT(SimpleCurve, getLength, Number, get_Length); + + +/** + * The points that make up the SimpleCurve geometry. + * + * @attribute rings + * @type {gdal.SimpleCurveRings} + */ +NAN_GETTER(SimpleCurve::pointsGetter) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("points_").ToLocalChecked()).ToLocalChecked()); +} + +/** + * Add a segment of another LineString to this SimpleCurve subtype. + * + * Adds the request range of vertices to the end of this compound curve in an + * efficient manner. If the start index is larger than the end index then the + * vertices will be reversed as they are copied. + * + * @method addSubLineString + * @param {gdal.LineString} LineString to be added + * @param {int} [start=0] the first vertex to copy, defaults to 0 to start with + * the first vertex in the other LineString + * @param {int} [end=-1] the last vertex to copy, defaults to -1 indicating the + * last vertex of the other LineString + * @return {void} + */ +NAN_METHOD(SimpleCurve::addSubLineString) { + Nan::HandleScope scope; + + SimpleCurve *geom = Nan::ObjectWrap::Unwrap(info.This()); + LineString *other; + int start = 0; + int end = -1; + + NODE_ARG_WRAPPED(0, "line", LineString, other); + NODE_ARG_INT_OPT(1, "start", start); + NODE_ARG_INT_OPT(2, "end", end); + + int n = other->get()->getNumPoints(); + + if (start < 0 || end < -1 || start >= n || end >= n) { + Nan::ThrowRangeError("Invalid start or end index for LineString"); + return; + } + + geom->this_->addSubLineString(other->get(), start, end); + + UPDATE_AMOUNT_OF_GEOMETRY_MEMORY(geom); + + return; +} + +} // namespace node_gdal diff --git a/src/geometry/gdal_simplecurve.hpp b/src/geometry/gdal_simplecurve.hpp new file mode 100644 index 000000000..d27c80948 --- /dev/null +++ b/src/geometry/gdal_simplecurve.hpp @@ -0,0 +1,39 @@ +#ifndef __NODE_OGR_CURVE_H__ +#define __NODE_OGR_CURVE_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +#include "gdal_curvebase.hpp" +#include "../collections/linestring_points.hpp" + +using namespace v8; +using namespace node; + +namespace node_gdal { + +class SimpleCurve : public CurveBase { + + public: + static Nan::Persistent constructor; + using CurveBase::CurveBase; + + static void Initialize(Local target); + static NAN_METHOD(New); + static NAN_METHOD(toString); + static NAN_METHOD(value); + static NAN_METHOD(getLength); + static NAN_METHOD(addSubLineString); + + static NAN_GETTER(pointsGetter); +}; + +} // namespace node_gdal +#endif diff --git a/src/node_gdal.cpp b/src/node_gdal.cpp index 3b37c243f..c6bbba450 100644 --- a/src/node_gdal.cpp +++ b/src/node_gdal.cpp @@ -65,6 +65,7 @@ #include "geometry/gdal_geometry.hpp" #include "geometry/gdal_geometrycollection.hpp" #include "gdal_layer.hpp" +#include "geometry/gdal_simplecurve.hpp" #include "geometry/gdal_linearring.hpp" #include "geometry/gdal_linestring.hpp" #include "geometry/gdal_circularstring.hpp" @@ -284,6 +285,7 @@ static void Init(Local target, Local, void *) { FieldDefn::Initialize(target); Geometry::Initialize(target); Point::Initialize(target); + SimpleCurve::Initialize(target); LineString::Initialize(target); CircularString::Initialize(target); LinearRing::Initialize(target); diff --git a/test/api_circularstring.test.js b/test/api_circularstring.test.js new file mode 100644 index 000000000..1acddab1a --- /dev/null +++ b/test/api_circularstring.test.js @@ -0,0 +1,39 @@ +const assert = require('chai').assert +const gdal = require('../lib/gdal.js') + +describe('gdal.CircularString', () => { + afterEach(gc) + + it('should be instantiable', () => { + new gdal.CircularString() + }) + it('should inherit from Curve', () => { + assert.instanceOf(new gdal.CircularString(), gdal.CircularString) + assert.instanceOf(new gdal.CircularString(), gdal.SimpleCurve) + assert.instanceOf(new gdal.CircularString(), gdal.Geometry) + }) + describe('instance', () => { + it('should support getArea()', () => { + const arc = new gdal.CircularString() + arc.points.add(-5, 0) + arc.points.add(0, 2.5) + arc.points.add(5, 0) + assert.closeTo(arc.getLength(), 11.5911, 0.001) + }) + it('should support addSubLineString', () => { + const arc = new gdal.CircularString() + arc.points.add(-5, 0) + arc.points.add(0, 2.5) + arc.points.add(5, 0) + + const line = new gdal.LineString() + line.points.add(0, 0, 0) + line.points.add(10, 10, 0) + line.points.add(10, 20, 0) + + arc.addSubLineString(line) + + assert.equal(arc.points.count(), 6) + }) + }) +}) diff --git a/test/api_linearring.test.js b/test/api_linearring.test.js index 38485e8f7..14c733fee 100644 --- a/test/api_linearring.test.js +++ b/test/api_linearring.test.js @@ -10,6 +10,7 @@ describe('gdal.LinearRing', () => { it('should inherit from LineString', () => { assert.instanceOf(new gdal.LinearRing(), gdal.LinearRing) assert.instanceOf(new gdal.LinearRing(), gdal.LineString) + assert.instanceOf(new gdal.LinearRing(), gdal.SimpleCurve) assert.instanceOf(new gdal.LinearRing(), gdal.Geometry) }) describe('instance', () => { diff --git a/test/api_linestring.test.js b/test/api_linestring.test.js index 5218381c8..55be39dec 100644 --- a/test/api_linestring.test.js +++ b/test/api_linestring.test.js @@ -9,6 +9,7 @@ describe('gdal.LineString', () => { }) it('should inherit from Geometry', () => { assert.instanceOf(new gdal.LineString(), gdal.LineString) + assert.instanceOf(new gdal.LineString(), gdal.SimpleCurve) assert.instanceOf(new gdal.LineString(), gdal.Geometry) }) describe('instance', () => { From a0532350a6ed323a5ef403ce089991501c0cb253 Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Mon, 3 May 2021 20:06:55 +0200 Subject: [PATCH 3/6] Add support for CompoundCurve and MultiCurve --- binding.gyp | 3 + lib/gdal.js | 2 + src/collections/compound_curves.cpp | 179 ++++++++++++++++++++++++++++ src/collections/compound_curves.hpp | 41 +++++++ src/geometry/gdal_compoundcurve.cpp | 65 ++++++++++ src/geometry/gdal_compoundcurve.hpp | 40 +++++++ src/geometry/gdal_geometry.cpp | 7 ++ src/geometry/gdal_geometrybase.hpp | 17 +-- src/geometry/gdal_linestring.hpp | 2 - src/geometry/gdal_multicurve.cpp | 55 +++++++++ src/geometry/gdal_multicurve.hpp | 34 ++++++ src/node_gdal.cpp | 18 +++ test/api_curve.test.js | 45 +++++++ test/api_geometry.test.js | 6 + 14 files changed, 504 insertions(+), 10 deletions(-) create mode 100644 src/collections/compound_curves.cpp create mode 100644 src/collections/compound_curves.hpp create mode 100644 src/geometry/gdal_compoundcurve.cpp create mode 100644 src/geometry/gdal_compoundcurve.hpp create mode 100644 src/geometry/gdal_multicurve.cpp create mode 100644 src/geometry/gdal_multicurve.hpp create mode 100644 test/api_curve.test.js diff --git a/binding.gyp b/binding.gyp index 261fe7667..6b5ed2afd 100644 --- a/binding.gyp +++ b/binding.gyp @@ -32,6 +32,7 @@ "src/geometry/gdal_geometry.cpp", "src/geometry/gdal_point.cpp", "src/geometry/gdal_simplecurve.cpp", + "src/geometry/gdal_compoundcurve.cpp", "src/geometry/gdal_linestring.cpp", "src/geometry/gdal_circularstring.cpp", "src/geometry/gdal_linearring.cpp", @@ -39,6 +40,7 @@ "src/geometry/gdal_geometrycollection.cpp", "src/geometry/gdal_multipoint.cpp", "src/geometry/gdal_multilinestring.cpp", + "src/geometry/gdal_multicurve.cpp", "src/geometry/gdal_multipolygon.cpp", "src/gdal_layer.cpp", "src/gdal_coordinate_transformation.cpp", @@ -55,6 +57,7 @@ "src/collections/geometry_collection_children.cpp", "src/collections/polygon_rings.cpp", "src/collections/linestring_points.cpp", + "src/collections/compound_curves.cpp", "src/collections/rasterband_overviews.cpp", "src/collections/rasterband_pixels.cpp", "src/collections/gdal_drivers.cpp" diff --git a/lib/gdal.js b/lib/gdal.js index 156b258c5..70ee8b89e 100644 --- a/lib/gdal.js +++ b/lib/gdal.js @@ -824,9 +824,11 @@ gdal.Point.wkbType = gdal.wkbPoint gdal.LineString.wkbType = gdal.wkbLineString gdal.CircularString.wkbType = gdal.wkbCircularString gdal.LinearRing.wkbType = gdal.wkbLinearRing +gdal.CompoundCurve.wkbType = gdal.wkbCompoundCurve gdal.Polygon.wkbType = gdal.wkbPolygon gdal.MultiPoint.wkbType = gdal.wkbMultiPoint gdal.MultiLineString.wkbType = gdal.wkbMultiLineString +gdal.MultiCurve.wkbType = gdal.wkbMultiCurve gdal.MultiPolygon.wkbType = gdal.wkbMultiPolygon gdal.GeometryCollection.wkbType = gdal.wkbGeometryCollection diff --git a/src/collections/compound_curves.cpp b/src/collections/compound_curves.cpp new file mode 100644 index 000000000..1cc0edea5 --- /dev/null +++ b/src/collections/compound_curves.cpp @@ -0,0 +1,179 @@ +#include "compound_curves.hpp" +#include "../gdal_common.hpp" +#include "../geometry/gdal_geometry.hpp" +#include "../geometry/gdal_linearring.hpp" +#include "../geometry/gdal_simplecurve.hpp" +#include "../geometry/gdal_compoundcurve.hpp" + +namespace node_gdal { + +Nan::Persistent CompoundCurves::constructor; + +void CompoundCurves::Initialize(Local target) { + Nan::HandleScope scope; + + Local lcons = Nan::New(CompoundCurves::New); + lcons->InstanceTemplate()->SetInternalFieldCount(1); + lcons->SetClassName(Nan::New("CompoundCurves").ToLocalChecked()); + + Nan::SetPrototypeMethod(lcons, "toString", toString); + Nan::SetPrototypeMethod(lcons, "count", count); + Nan::SetPrototypeMethod(lcons, "get", get); + Nan::SetPrototypeMethod(lcons, "add", add); + + Nan::Set(target, Nan::New("CompoundCurves").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); + + constructor.Reset(lcons); +} + +CompoundCurves::CompoundCurves() : Nan::ObjectWrap() { +} + +CompoundCurves::~CompoundCurves() { +} + +/** + * A collection of connected curves, used by {{#crossLink + * "gdal.CompoundCurve"}}gdal.CompoundCurve{{/crossLink}}. + * + * @class gdal.CompoundCurves + */ +NAN_METHOD(CompoundCurves::New) { + Nan::HandleScope scope; + + if (!info.IsConstructCall()) { + Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword"); + return; + } + if (info[0]->IsExternal()) { + Local ext = info[0].As(); + void *ptr = ext->Value(); + CompoundCurves *geom = static_cast(ptr); + geom->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + return; + } else { + Nan::ThrowError("Cannot create CompoundCurves directly"); + return; + } +} + +Local CompoundCurves::New(Local geom) { + Nan::EscapableHandleScope scope; + + CompoundCurves *wrapped = new CompoundCurves(); + + v8::Local ext = Nan::New(wrapped); + v8::Local obj = + Nan::NewInstance(Nan::GetFunction(Nan::New(CompoundCurves::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked(); + Nan::SetPrivate(obj, Nan::New("parent_").ToLocalChecked(), geom); + + return scope.Escape(obj); +} + +NAN_METHOD(CompoundCurves::toString) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::New("CompoundCurves").ToLocalChecked()); +} + +/** + * Returns the number of curves that exist in the collection. + * + * @method count + * @return Integer + */ +NAN_METHOD(CompoundCurves::count) { + Nan::HandleScope scope; + + Local parent = + Nan::GetPrivate(info.This(), Nan::New("parent_").ToLocalChecked()).ToLocalChecked().As(); + CompoundCurve *geom = Nan::ObjectWrap::Unwrap(parent); + + info.GetReturnValue().Set(Nan::New(geom->get()->getNumCurves())); +} + +/** + * Returns the curve at the specified index. + * + * @example + * ``` + * var curve0 = compound.curves.get(0); + * var curve1 = compound.curves.get(1);``` + * + * @method get + * @return {gdal.CompoundCurve|gdal.SimpleCurve} + */ +NAN_METHOD(CompoundCurves::get) { + Nan::HandleScope scope; + + Local parent = + Nan::GetPrivate(info.This(), Nan::New("parent_").ToLocalChecked()).ToLocalChecked().As(); + CompoundCurve *geom = Nan::ObjectWrap::Unwrap(parent); + + int i; + NODE_ARG_INT(0, "index", i); + + if (i >= 0 && i < geom->get()->getNumCurves()) + info.GetReturnValue().Set(Geometry::New(geom->get()->getCurve(i), false)); +} + +/** + * Adds a curve to the collection. + * + * @example + * ``` + * var ring1 = new gdal.CircularString(); + * ring1.points.add(0,0); + * ring1.points.add(1,0); + * ring1.points.add(1,1); + * ring1.points.add(0,1); + * ring1.points.add(0,0); + * + * // one at a time: + * compound.curves.add(ring1); + * + * // many at once: + * compound.curves.add([ring1, ...]);``` + * + * @method add + * @param {gdal.CompoundCurve|gdal.SimpleCurve} curve(s) + */ +NAN_METHOD(CompoundCurves::add) { + Nan::HandleScope scope; + + Local parent = + Nan::GetPrivate(info.This(), Nan::New("parent_").ToLocalChecked()).ToLocalChecked().As(); + CompoundCurve *geom = Nan::ObjectWrap::Unwrap(parent); + + SimpleCurve *ring; + + if (info.Length() < 1) { + Nan::ThrowError("ring(s) must be given"); + return; + } + if (info[0]->IsArray()) { + // set from array of geometry objects + Local array = info[0].As(); + int length = array->Length(); + for (int i = 0; i < length; i++) { + Local element = Nan::Get(array, i).ToLocalChecked(); + if (IS_WRAPPED(element, SimpleCurve)) { + ring = Nan::ObjectWrap::Unwrap(element.As()); + geom->get()->addCurve(ring->get()); + } else { + Nan::ThrowError("All array elements must be SimpleCurves"); + return; + } + } + } else if (IS_WRAPPED(info[0], SimpleCurve)) { + ring = Nan::ObjectWrap::Unwrap(info[0].As()); + geom->get()->addCurve(ring->get()); + } else { + Nan::ThrowError("ring(s) must be a SimpleCurve or array of SimpleCurves"); + return; + } + + return; +} + +} // namespace node_gdal diff --git a/src/collections/compound_curves.hpp b/src/collections/compound_curves.hpp new file mode 100644 index 000000000..f2aaea905 --- /dev/null +++ b/src/collections/compound_curves.hpp @@ -0,0 +1,41 @@ +#ifndef __NODE_GDAL_COMPOUND_CURVES_H__ +#define __NODE_GDAL_COMPOUND_CURVES_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// gdal +#include + +using namespace v8; +using namespace node; + +// Polygon.rings + +namespace node_gdal { + +class CompoundCurves : public Nan::ObjectWrap { + public: + static Nan::Persistent constructor; + + static void Initialize(Local target); + static NAN_METHOD(New); + static Local New(Local geom); + static NAN_METHOD(toString); + + static NAN_METHOD(get); + static NAN_METHOD(count); + static NAN_METHOD(add); + + CompoundCurves(); + + private: + ~CompoundCurves(); +}; + +} // namespace node_gdal +#endif diff --git a/src/geometry/gdal_compoundcurve.cpp b/src/geometry/gdal_compoundcurve.cpp new file mode 100644 index 000000000..ffa2b66e5 --- /dev/null +++ b/src/geometry/gdal_compoundcurve.cpp @@ -0,0 +1,65 @@ + +#include "gdal_compoundcurve.hpp" +#include "../collections/linestring_points.hpp" +#include "../gdal_common.hpp" +#include "gdal_geometry.hpp" +#include "gdal_point.hpp" + +#include + +namespace node_gdal { + +Nan::Persistent CompoundCurve::constructor; + +void CompoundCurve::Initialize(Local target) { + Nan::HandleScope scope; + + Local lcons = Nan::New(CompoundCurve::New); + lcons->Inherit(Nan::New(Geometry::constructor)); + lcons->InstanceTemplate()->SetInternalFieldCount(1); + lcons->SetClassName(Nan::New("CompoundCurve").ToLocalChecked()); + + Nan::SetPrototypeMethod(lcons, "toString", toString); + + ATTR(lcons, "curves", curvesGetter, READ_ONLY_SETTER); + + Nan::Set(target, Nan::New("CompoundCurve").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); + + constructor.Reset(lcons); +} + +void CompoundCurve::SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE _this, v8::Local value) { + Nan::SetPrivate(_this, Nan::New("curves_").ToLocalChecked(), value); +}; + +/** + * Concrete representation of a compound contionuos curve. + * + * @example + * ``` + * var CompoundCurve = new gdal.CompoundCurve(); + * CompoundCurve.points.add(new gdal.Point(0,0)); + * CompoundCurve.points.add(new gdal.Point(0,10));``` + * + * @constructor + * @class gdal.CompoundCurve + * @extends gdal.Geometry + */ + +NAN_METHOD(CompoundCurve::toString) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::New("CompoundCurve").ToLocalChecked()); +} + +/** + * Points that make up the compound curve. + * + * @attribute points + * @type {gdal.CompoundCurvePoints} + */ +NAN_GETTER(CompoundCurve::curvesGetter) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::GetPrivate(info.This(), Nan::New("curves_").ToLocalChecked()).ToLocalChecked()); +} + +} // namespace node_gdal diff --git a/src/geometry/gdal_compoundcurve.hpp b/src/geometry/gdal_compoundcurve.hpp new file mode 100644 index 000000000..79f363254 --- /dev/null +++ b/src/geometry/gdal_compoundcurve.hpp @@ -0,0 +1,40 @@ +#ifndef __NODE_OGR_COMPOUNDCURVE_H__ +#define __NODE_OGR_COMPOUNDCURVE_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +#include "gdal_curvebase.hpp" +#include "../collections/compound_curves.hpp" + +using namespace v8; +using namespace node; + +namespace node_gdal { + +class CompoundCurve : public CurveBase { + friend CurveBase; + + public: + static Nan::Persistent constructor; + using CurveBase::CurveBase; + + static void Initialize(Local target); + using CurveBase::New; + static NAN_METHOD(toString); + + static NAN_GETTER(curvesGetter); + + protected: + static void SetPrivate(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE, v8::Local); +}; + +} // namespace node_gdal +#endif diff --git a/src/geometry/gdal_geometry.cpp b/src/geometry/gdal_geometry.cpp index 28e5e2c81..3fbe4f3ec 100644 --- a/src/geometry/gdal_geometry.cpp +++ b/src/geometry/gdal_geometry.cpp @@ -6,7 +6,9 @@ #include "gdal_linearring.hpp" #include "gdal_linestring.hpp" #include "gdal_circularstring.hpp" +#include "gdal_compoundcurve.hpp" #include "gdal_multilinestring.hpp" +#include "gdal_multicurve.hpp" #include "gdal_multipoint.hpp" #include "gdal_multipolygon.hpp" #include "gdal_point.hpp" @@ -135,11 +137,13 @@ Local Geometry::New(OGRGeometry *geom, bool owned) { case wkbLineString: return scope.Escape(LineString::New(static_cast(geom), owned)); case wkbLinearRing: return scope.Escape(LinearRing::New(static_cast(geom), owned)); case wkbPolygon: return scope.Escape(Polygon::New(static_cast(geom), owned)); + case wkbCompoundCurve: return scope.Escape(CompoundCurve::New(static_cast(geom), owned)); case wkbCircularString: return scope.Escape(CircularString::New(static_cast(geom), owned)); case wkbGeometryCollection: return scope.Escape(GeometryCollection::New(static_cast(geom), owned)); case wkbMultiPoint: return scope.Escape(MultiPoint::New(static_cast(geom), owned)); case wkbMultiLineString: return scope.Escape(MultiLineString::New(static_cast(geom), owned)); + case wkbMultiCurve: return scope.Escape(MultiCurve::New(static_cast(geom), owned)); case wkbMultiPolygon: return scope.Escape(MultiPolygon::New(static_cast(geom), owned)); default: Nan::ThrowError("Tried to create unsupported geometry type"); return scope.Escape(Nan::Undefined()); } @@ -1558,12 +1562,15 @@ Local Geometry::getConstructor(OGRwkbGeometryType type) { case wkbLineString: return scope.Escape(Nan::GetFunction(Nan::New(LineString::constructor)).ToLocalChecked()); case wkbCircularString: return scope.Escape(Nan::GetFunction(Nan::New(CircularString::constructor)).ToLocalChecked()); case wkbLinearRing: return scope.Escape(Nan::GetFunction(Nan::New(LinearRing::constructor)).ToLocalChecked()); + case wkbCompoundCurve: return scope.Escape(Nan::GetFunction(Nan::New(CompoundCurve::constructor)).ToLocalChecked()); case wkbPolygon: return scope.Escape(Nan::GetFunction(Nan::New(Polygon::constructor)).ToLocalChecked()); case wkbGeometryCollection: return scope.Escape(Nan::GetFunction(Nan::New(GeometryCollection::constructor)).ToLocalChecked()); case wkbMultiPoint: return scope.Escape(Nan::GetFunction(Nan::New(MultiPoint::constructor)).ToLocalChecked()); case wkbMultiLineString: return scope.Escape(Nan::GetFunction(Nan::New(MultiLineString::constructor)).ToLocalChecked()); + case wkbMultiCurve: + return scope.Escape(Nan::GetFunction(Nan::New(MultiCurve::constructor)).ToLocalChecked()); case wkbMultiPolygon: return scope.Escape(Nan::GetFunction(Nan::New(MultiPolygon::constructor)).ToLocalChecked()); default: return scope.Escape(Nan::Null()); } diff --git a/src/geometry/gdal_geometrybase.hpp b/src/geometry/gdal_geometrybase.hpp index 159ad2927..51914920f 100644 --- a/src/geometry/gdal_geometrybase.hpp +++ b/src/geometry/gdal_geometrybase.hpp @@ -24,11 +24,12 @@ namespace node_gdal { * C++ * (maximizes code reuse) * + * * GeometryBase<> - * | \ \ \ - * Geometry Point CurveBase<> GeometryCollectionBase<> - * | \ \ \ \ | \ - * SimpleCurve LineString Polygon LinearRing CircularString GeometryCollection Multi* + * | \ \ \ + * Geometry Point CurveBase<> GeometryCollectionBase<> + * | \ \ \ \ \ | \ + * SimpleCurve LineString Polygon LinearRing CircularString CompoundCurve GeometryCollection Multi* * * * @@ -36,10 +37,10 @@ namespace node_gdal { * (tries to follow GDAL and the ISO specification) * * Geometry - * | \ \ \ - * Point SimpleCurve Polygon GeometryCollection - * | \ | - * LineString CircularString Multi* + * | \ \ \ \ + * Point SimpleCurve Polygon CompoundCurve GeometryCollection + * | \ | + * LineString CircularString Multi* * | * LinearRing * diff --git a/src/geometry/gdal_linestring.hpp b/src/geometry/gdal_linestring.hpp index f7eb60cd4..76e07e01f 100644 --- a/src/geometry/gdal_linestring.hpp +++ b/src/geometry/gdal_linestring.hpp @@ -28,8 +28,6 @@ class LineString : public CurveBase static void Initialize(Local target); using CurveBase::New; static NAN_METHOD(toString); - - static NAN_GETTER(pointsGetter); }; } // namespace node_gdal diff --git a/src/geometry/gdal_multicurve.cpp b/src/geometry/gdal_multicurve.cpp new file mode 100644 index 000000000..528ed372c --- /dev/null +++ b/src/geometry/gdal_multicurve.cpp @@ -0,0 +1,55 @@ + +#include "gdal_multicurve.hpp" +#include "../collections/geometry_collection_children.hpp" +#include "../gdal_common.hpp" +#include "gdal_geometry.hpp" +#include "gdal_geometrycollection.hpp" + +#include + +namespace node_gdal { + +Nan::Persistent MultiCurve::constructor; + +void MultiCurve::Initialize(Local target) { + Nan::HandleScope scope; + + Local lcons = Nan::New(MultiCurve::New); + lcons->Inherit(Nan::New(GeometryCollection::constructor)); + lcons->InstanceTemplate()->SetInternalFieldCount(1); + lcons->SetClassName(Nan::New("MultiCurve").ToLocalChecked()); + + Nan::SetPrototypeMethod(lcons, "toString", toString); + Nan::SetPrototypeMethod(lcons, "polygonize", polygonize); + + Nan::Set(target, Nan::New("MultiCurve").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked()); + + constructor.Reset(lcons); +} + +/** + * @constructor + * @class gdal.MultiCurve + * @extends gdal.GeometryCollection + */ + +NAN_METHOD(MultiCurve::toString) { + Nan::HandleScope scope; + info.GetReturnValue().Set(Nan::New("MultiCurve").ToLocalChecked()); +} + +/** + * Converts it to a polygon. + * + * @method polygonize + * @return {gdal.Polygon} + */ +NAN_METHOD(MultiCurve::polygonize) { + Nan::HandleScope scope; + + MultiCurve *geom = Nan::ObjectWrap::Unwrap(info.This()); + + info.GetReturnValue().Set(Geometry::New(geom->this_->Polygonize())); +} + +} // namespace node_gdal diff --git a/src/geometry/gdal_multicurve.hpp b/src/geometry/gdal_multicurve.hpp new file mode 100644 index 000000000..d0e7a7647 --- /dev/null +++ b/src/geometry/gdal_multicurve.hpp @@ -0,0 +1,34 @@ +#ifndef __NODE_OGR_MULTICURVE_H__ +#define __NODE_OGR_MULTICURVE_H__ + +// node +#include +#include + +// nan +#include "../nan-wrapper.h" + +// ogr +#include + +#include "gdal_geometrycollectionbase.hpp" + +using namespace v8; +using namespace node; + +namespace node_gdal { + +class MultiCurve : public GeometryCollectionBase { + + public: + static Nan::Persistent constructor; + using GeometryCollectionBase::GeometryCollectionBase; + + static void Initialize(Local target); + using GeometryCollectionBase::New; + static NAN_METHOD(toString); + static NAN_METHOD(polygonize); +}; + +} // namespace node_gdal +#endif diff --git a/src/node_gdal.cpp b/src/node_gdal.cpp index c6bbba450..e64659035 100644 --- a/src/node_gdal.cpp +++ b/src/node_gdal.cpp @@ -69,7 +69,9 @@ #include "geometry/gdal_linearring.hpp" #include "geometry/gdal_linestring.hpp" #include "geometry/gdal_circularstring.hpp" +#include "geometry/gdal_compoundcurve.hpp" #include "geometry/gdal_multilinestring.hpp" +#include "geometry/gdal_multicurve.hpp" #include "geometry/gdal_multipoint.hpp" #include "geometry/gdal_multipolygon.hpp" #include "geometry/gdal_point.hpp" @@ -91,6 +93,7 @@ #include "collections/layer_fields.hpp" #include "collections/linestring_points.hpp" #include "collections/polygon_rings.hpp" +#include "collections/compound_curves.hpp" #include "collections/rasterband_overviews.hpp" #include "collections/rasterband_pixels.hpp" @@ -289,10 +292,12 @@ static void Init(Local target, Local, void *) { LineString::Initialize(target); CircularString::Initialize(target); LinearRing::Initialize(target); + CompoundCurve::Initialize(target); Polygon::Initialize(target); GeometryCollection::Initialize(target); MultiPoint::Initialize(target); MultiLineString::Initialize(target); + MultiCurve::Initialize(target); MultiPolygon::Initialize(target); SpatialReference::Initialize(target); CoordinateTransformation::Initialize(target); @@ -306,6 +311,7 @@ static void Init(Local target, Local, void *) { GeometryCollectionChildren::Initialize(target); PolygonRings::Initialize(target); LineStringPoints::Initialize(target); + CompoundCurves::Initialize(target); RasterBandOverviews::Initialize(target); RasterBandPixels::Initialize(target); @@ -1025,6 +1031,18 @@ static void Init(Local target, Local, void *) { * @type {integer} */ Nan::Set(target, Nan::New("wkbCircularString").ToLocalChecked(), Nan::New(wkbCircularString)); + /** + * @final + * @property gdal.wkbLineString + * @type {integer} + */ + Nan::Set(target, Nan::New("wkbCompoundCurve").ToLocalChecked(), Nan::New(wkbCompoundCurve)); + /** + * @final + * @property gdal.wkbLineString + * @type {integer} + */ + Nan::Set(target, Nan::New("wkbMultiCurve").ToLocalChecked(), Nan::New(wkbMultiCurve)); /** * @final * @property gdal.wkbPolygon diff --git a/test/api_curve.test.js b/test/api_curve.test.js new file mode 100644 index 000000000..c77ff2b59 --- /dev/null +++ b/test/api_curve.test.js @@ -0,0 +1,45 @@ +const assert = require('chai').assert +const gdal = require('../lib/gdal.js') + +describe('gdal.CompoundCurve', () => { + afterEach(gc) + + it('should be instantiable', () => { + new gdal.CompoundCurve() + }) + it('should inherit from Geometry', () => { + assert.instanceOf(new gdal.CompoundCurve(), gdal.CompoundCurve) + assert.instanceOf(new gdal.CompoundCurve(), gdal.Geometry) + }) + describe('instance', () => { + let compoundcurve + it('should be parsed from WKT', () => { + compoundcurve = gdal.Geometry.fromWKT('COMPOUNDCURVE (CIRCULARSTRING (692012.37315768 594722.610031277,692057.127042054 594649.528941062,692067.186040178 594564.425636366),(692067.186040178 594564.425636366,692026.997800346 594156.751911029),CIRCULARSTRING (692026.997800346 594156.751911029,692061.574244064 594070.897749602,692151.782327678 594050.18617928))') + assert.instanceOf(compoundcurve, gdal.CompoundCurve) + assert.equal(compoundcurve.curves.count(), 3) + assert.instanceOf(compoundcurve.curves.get(0), gdal.CircularString) + }) + }) +}) + +describe('gdal.MultiCurve', () => { + afterEach(gc) + + it('should be instantiable', () => { + new gdal.MultiCurve() + }) + it('should inherit from GeometryCollection', () => { + assert.instanceOf(new gdal.MultiCurve(), gdal.MultiCurve) + assert.instanceOf(new gdal.MultiCurve(), gdal.GeometryCollection) + assert.instanceOf(new gdal.MultiCurve(), gdal.Geometry) + }) + describe('instance', () => { + let multicurve + it('should be parsed from WKT', () => { + multicurve = gdal.Geometry.fromWKT('MULTICURVE (COMPOUNDCURVE (CIRCULARSTRING (692012.37315768 594722.610031277,692057.127042054 594649.528941062,692067.186040178 594564.425636366),(692067.186040178 594564.425636366,692026.997800346 594156.751911029),CIRCULARSTRING (692026.997800346 594156.751911029,692061.574244064 594070.897749602,692151.782327678 594050.18617928)))') + assert.instanceOf(multicurve, gdal.MultiCurve) + assert.equal(multicurve.children.count(), 1) + assert.instanceOf(multicurve.children.get(0), gdal.CompoundCurve) + }) + }) +}) diff --git a/test/api_geometry.test.js b/test/api_geometry.test.js index f89466de0..042de7411 100644 --- a/test/api_geometry.test.js +++ b/test/api_geometry.test.js @@ -226,6 +226,8 @@ describe('gdal.Geometry', () => { assert.equal(gdal.Geometry.getConstructor(6), gdal.MultiPolygon) assert.equal(gdal.Geometry.getConstructor(7), gdal.GeometryCollection) assert.equal(gdal.Geometry.getConstructor(8), gdal.CircularString) + assert.equal(gdal.Geometry.getConstructor(9), gdal.CompoundCurve) + assert.equal(gdal.Geometry.getConstructor(11), gdal.MultiCurve) assert.equal(gdal.Geometry.getConstructor(101), gdal.LinearRing) }) }) @@ -239,6 +241,8 @@ describe('gdal.Geometry', () => { assert.equal(gdal.MultiPolygon.wkbType, 6) assert.equal(gdal.GeometryCollection.wkbType, 7) assert.equal(gdal.CircularString.wkbType, 8) + assert.equal(gdal.CompoundCurve.wkbType, 9) + assert.equal(gdal.MultiCurve.wkbType, 11) assert.equal(gdal.LinearRing.wkbType, 101) }) }) @@ -254,6 +258,8 @@ describe('gdal.Geometry', () => { assert.equal(new gdal.MultiPolygon().wkbType, 6) assert.equal(new gdal.GeometryCollection().wkbType, 7) assert.equal(new gdal.CircularString().wkbType, 8) + assert.equal(new gdal.CompoundCurve().wkbType, 9) + assert.equal(new gdal.MultiCurve().wkbType, 11) assert.equal(new gdal.LinearRing().wkbType, 101) }) }) From 4297ca5fab4eb8ff4b82b1b924da6311ceabb2b3 Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Mon, 3 May 2021 20:16:09 +0200 Subject: [PATCH 4/6] more compact and readable diagram --- src/geometry/gdal_geometrybase.hpp | 33 +++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/geometry/gdal_geometrybase.hpp b/src/geometry/gdal_geometrybase.hpp index 51914920f..88388d345 100644 --- a/src/geometry/gdal_geometrybase.hpp +++ b/src/geometry/gdal_geometrybase.hpp @@ -24,25 +24,34 @@ namespace node_gdal { * C++ * (maximizes code reuse) * - * * GeometryBase<> - * | \ \ \ - * Geometry Point CurveBase<> GeometryCollectionBase<> - * | \ \ \ \ \ | \ - * SimpleCurve LineString Polygon LinearRing CircularString CompoundCurve GeometryCollection Multi* - * + * Geometry + * Point + * CurveBase<> + * SimpleCurve + * LineString + * CircularString + * LinearRing + * Polygon + * CompoundCurve + * GeometryCollectionBase<> + * GeometryCollection + * Multi* * * * JS * (tries to follow GDAL and the ISO specification) * * Geometry - * | \ \ \ \ - * Point SimpleCurve Polygon CompoundCurve GeometryCollection - * | \ | - * LineString CircularString Multi* - * | - * LinearRing + * Point + * SimpleCurve + * LineString + * LinearRing + * CircularString + * Polygon + * CompoundCurve + * GeometryCollection + * Multi* * * * The full GDAL OGRGeometry class hierarchy From 68967b51eafe489e7a571f5f54963b3aeee16e2e Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Tue, 4 May 2021 20:24:40 +0200 Subject: [PATCH 5/6] add map/forEach for CompoundCurves --- lib/gdal.js | 29 +++++++++++++++++++++++++++++ test/api_curve.test.js | 13 +++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/gdal.js b/lib/gdal.js index 70ee8b89e..c85c407a2 100644 --- a/lib/gdal.js +++ b/lib/gdal.js @@ -413,6 +413,35 @@ gdal.LineStringPoints.prototype.forEach = defaultForEach */ gdal.LineStringPoints.prototype.map = defaultMap +/** + * Iterates through all curves using a callback function. + * + * @example + * ``` + * compoundCurve.curves.forEach(function(point, i) { ... });``` + * + * @for gdal.CompoundCurves + * @method forEach + * @param {Function} callback The callback to be called with each {{#crossLink "gdal.SimpleCurve"}}SimpleCurve{{/crossLink}} + */ +gdal.CompoundCurves.prototype.forEach = defaultForEach + +/** + * Iterates through all curves using a callback function and builds + * an array of the returned values. + * + * @example + * ``` + * var result = compoundCurves.points.map(function(point, i) { + * return value; + * });``` + * + * @for gdal.CompoundCurves + * @method map + * @param {Function} callback The callback to be called with each {{#crossLink "gdal.SimpleCurve"}}SimpleCurve{{/crossLink}} + */ +gdal.CompoundCurves.prototype.map = defaultMap + /** * Iterates through all child geometries using a callback function. * diff --git a/test/api_curve.test.js b/test/api_curve.test.js index c77ff2b59..5ed4c2f28 100644 --- a/test/api_curve.test.js +++ b/test/api_curve.test.js @@ -19,6 +19,19 @@ describe('gdal.CompoundCurve', () => { assert.equal(compoundcurve.curves.count(), 3) assert.instanceOf(compoundcurve.curves.get(0), gdal.CircularString) }) + it('should support forEach', () => { + let i = 0 + compoundcurve.curves.forEach((curve) => { + assert.instanceOf(curve, gdal.SimpleCurve) + i++ + }) + assert.equal(i, 3) + }) + it('should support map', () => { + const curves = compoundcurve.curves.map((curve) => curve) + assert.isArray(curves) + assert.equal(curves.length, 3) + }) }) }) From c7b8ebf6c0529177bf58740fb73361c865dace74 Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Tue, 4 May 2021 21:33:37 +0200 Subject: [PATCH 6/6] add iterators to all collections --- lib/gdal.js | 176 +++++++++++++++++++++++++++++++++++- test/api_curve.test.js | 12 ++- test/api_dataset.test.js | 19 ++++ test/api_drivers.test.js | 12 +++ test/api_layer.test.js | 20 ++++ test/api_linestring.test.js | 14 +++ test/api_polygon.test.js | 107 ++++++++++++---------- test/api_rasterband.test.js | 27 ++++++ 8 files changed, 335 insertions(+), 52 deletions(-) diff --git a/lib/gdal.js b/lib/gdal.js index c85c407a2..777c15b4d 100644 --- a/lib/gdal.js +++ b/lib/gdal.js @@ -141,6 +141,20 @@ function defaultToArray() { return array } +function defaultIterator() { + let i = 0 + + return { + next: () => { + const done = !(i < this.count()) + return { + done, + value: done ? null : this.get(i++) + } + } + } +} + /** * Iterates through all bands using a callback function. * Note: GDAL band indexes start at 1, not 0. @@ -176,6 +190,31 @@ gdal.DatasetBands.prototype.forEach = function (callback) { */ gdal.DatasetBands.prototype.map = defaultMap +/** + * Iterates through all bands using an iterator + * + * @example + * ``` + * for (const band of dataset.bands) { + * }``` + * + * @for gdal.DatasetBands + * @method Symbol.iterator + */ +gdal.DatasetBands.prototype[Symbol.iterator] = function defaultIterator() { + let i = 1 + + return { + next: () => { + const done = !(i <= this.count()) + return { + done, + value: done ? null : this.get(i++) + } + } + } +} + /** * Iterates through all features using a callback function. * @@ -212,6 +251,31 @@ gdal.LayerFeatures.prototype.forEach = function (callback) { */ gdal.LayerFeatures.prototype.map = defaultMap +/** + * Iterates through all features using an iterator + * + * @example + * ``` + * for (const feature of layer.features) { + * }``` + * + * @for gdal.LayerFeatures + * @method Symbol.iterator + */ +gdal.LayerFeatures.prototype[Symbol.iterator]= function defaultIterator() { + let feature + + return { + next: () => { + feature = feature ? this.next() : this.first() + return { + done: !feature, + value: feature + } + } + } +} + /** * Iterates through all fields using a callback function. * @@ -297,6 +361,19 @@ gdal.LayerFields.prototype.forEach = defaultForEach */ gdal.LayerFields.prototype.map = defaultMap +/** + * Iterates through all field definitions using an iterator + * + * @example + * ``` + * for (const curve of layer.fields) { + * }``` + * + * @for gdal.LayerFields + * @method Symbol.iterator + */ +gdal.LayerFields.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all layers using a callback function. * @@ -326,6 +403,19 @@ gdal.DatasetLayers.prototype.forEach = defaultForEach */ gdal.DatasetLayers.prototype.map = defaultMap +/** + * Iterates through all layers using an iterator + * + * @example + * ``` + * for (const curve of dataset.layers) { + * }``` + * + * @for gdal.DatasetLayers + * @method Symbol.iterator + */ +gdal.DatasetLayers.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all field definitions using a callback function. * @@ -345,7 +435,7 @@ gdal.FeatureDefnFields.prototype.forEach = defaultForEach * * @example * ``` - * var result = dataset.layers.map(function(field, i) { + * var result = featureDefn.map(function(field, i) { * return value; * });``` * @@ -355,6 +445,19 @@ gdal.FeatureDefnFields.prototype.forEach = defaultForEach */ gdal.FeatureDefnFields.prototype.map = defaultMap +/** + * Iterates through all field definitions using an iterator + * + * @example + * ``` + * for (const defn of featureDefn) { + * }``` + * + * @for gdal.FeatureDefnFields + * @method Symbol.iterator + */ +gdal.FeatureDefnFields.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all rings using a callback function. * @@ -378,12 +481,25 @@ gdal.PolygonRings.prototype.forEach = defaultForEach * return value; * });``` * - * @for gdal.LineStringPoints + * @for gdal.PolygonRings * @method map * @param {Function} callback The callback to be called with each {{#crossLink "gdal.LineString"}}LineString{{/crossLink}} */ gdal.PolygonRings.prototype.map = defaultMap +/** + * Iterates through all rings using an iterator + * + * @example + * ``` + * for (const ring of polygon.rings) { + * }``` + * + * @for gdal.PolygonRings + * @method Symbol.iterator + */ +gdal.PolygonRings.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all points using a callback function. * @@ -413,12 +529,25 @@ gdal.LineStringPoints.prototype.forEach = defaultForEach */ gdal.LineStringPoints.prototype.map = defaultMap +/** + * Iterates through all points using an iterator + * + * @example + * ``` + * for (const point of lineString.points) { + * }``` + * + * @for gdal.LineStringPoints + * @method Symbol.iterator + */ +gdal.LineStringPoints.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all curves using a callback function. * * @example * ``` - * compoundCurve.curves.forEach(function(point, i) { ... });``` + * compoundCurve.curves.forEach(function(curve, i) { ... });``` * * @for gdal.CompoundCurves * @method forEach @@ -432,7 +561,7 @@ gdal.CompoundCurves.prototype.forEach = defaultForEach * * @example * ``` - * var result = compoundCurves.points.map(function(point, i) { + * var result = compoundCurves.curves.map(function(curve, i) { * return value; * });``` * @@ -442,6 +571,19 @@ gdal.CompoundCurves.prototype.forEach = defaultForEach */ gdal.CompoundCurves.prototype.map = defaultMap +/** + * Iterates through all curves using an iterator + * + * @example + * ``` + * for (const curve of compoundCurves.curves) { + * } + * + * @for gdal.CompoundCurves + * @method Symbol.iterator + */ +gdal.CompoundCurves.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all child geometries using a callback function. * @@ -500,6 +642,19 @@ gdal.RasterBandOverviews.prototype.forEach = defaultForEach */ gdal.RasterBandOverviews.prototype.map = defaultMap +/** + * Iterates through all overview using an iterator + * + * @example + * ``` + * for (const overview of band.overviews) { + * } + * + * @for gdal.RasterBandOverviews + * @method Symbol.iterator + */ +gdal.RasterBandOverviews.prototype[Symbol.iterator] = defaultIterator + /** * Iterates through all registered drivers using a callback function. * @@ -529,6 +684,19 @@ gdal.GDALDrivers.prototype.forEach = defaultForEach */ gdal.GDALDrivers.prototype.map = defaultMap +/** + * Iterates through all drivers using an iterator + * + * @example + * ``` + * for (const curve of gdal.drivers) { + * } + * + * @for gdal.GDALDrivers + * @method Symbol.iterator + */ +gdal.GDALDrivers.prototype[Symbol.iterator] = defaultIterator + /** * Outputs all geometries as a regular javascript array. * diff --git a/test/api_curve.test.js b/test/api_curve.test.js index 5ed4c2f28..99930d8a4 100644 --- a/test/api_curve.test.js +++ b/test/api_curve.test.js @@ -13,8 +13,10 @@ describe('gdal.CompoundCurve', () => { }) describe('instance', () => { let compoundcurve - it('should be parsed from WKT', () => { + before(() => { compoundcurve = gdal.Geometry.fromWKT('COMPOUNDCURVE (CIRCULARSTRING (692012.37315768 594722.610031277,692057.127042054 594649.528941062,692067.186040178 594564.425636366),(692067.186040178 594564.425636366,692026.997800346 594156.751911029),CIRCULARSTRING (692026.997800346 594156.751911029,692061.574244064 594070.897749602,692151.782327678 594050.18617928))') + }) + it('should be parsed from WKT', () => { assert.instanceOf(compoundcurve, gdal.CompoundCurve) assert.equal(compoundcurve.curves.count(), 3) assert.instanceOf(compoundcurve.curves.get(0), gdal.CircularString) @@ -32,6 +34,14 @@ describe('gdal.CompoundCurve', () => { assert.isArray(curves) assert.equal(curves.length, 3) }) + it('should have an iterator', () => { + let i = 0 + for (const curve of compoundcurve.curves) { + assert.instanceOf(curve, gdal.SimpleCurve) + i++ + } + assert.equal(i, 3) + }) }) }) diff --git a/test/api_dataset.test.js b/test/api_dataset.test.js index f0c775488..4236a5503 100644 --- a/test/api_dataset.test.js +++ b/test/api_dataset.test.js @@ -132,6 +132,25 @@ describe('gdal.Dataset', () => { }) }) }) + describe('@@iterator', () => { + it('should iterate over all values', () => { + const ds = gdal.open(`${__dirname}/data/sample.tif`) + const expected_ids = [ 1 ] + const ids = [] + for (const band of ds.bands) { + assert.instanceOf(band, gdal.RasterBand) + ids.push(band.id) + } + assert.deepEqual(ids, expected_ids) + }) + it('should throw if dataset is closed', () => { + const ds = gdal.open(`${__dirname}/data/sample.tif`) + ds.close() + assert.throws(() => { + for (const band of ds.bands) band + }) + }) + }) describe('map()', () => { it('should operate normally', () => { const ds = gdal.open(`${__dirname}/data/sample.tif`) diff --git a/test/api_drivers.test.js b/test/api_drivers.test.js index 1a9bb6ea6..369675685 100644 --- a/test/api_drivers.test.js +++ b/test/api_drivers.test.js @@ -129,6 +129,18 @@ describe('gdal.drivers', () => { assert.equal(j, n) }) }) + describe('@@iterator()', () => { + it('should iterate through all Driver objects', () => { + const n = gdal.drivers.count() + let count = 0 + for (const driver of gdal.drivers) { + assert.instanceOf(driver, gdal.Driver) + assert.equal(driver, gdal.drivers.get(count)) + count++ + } + assert.equal(count, n) + }) + }) describe('map()', () => { it('should operate normally', () => { diff --git a/test/api_layer.test.js b/test/api_layer.test.js index df6afc917..ebf74dc09 100644 --- a/test/api_layer.test.js +++ b/test/api_layer.test.js @@ -490,6 +490,26 @@ describe('gdal.Layer', () => { }) }) }) + describe('@@iterator()', () => { + it('should support iterating over the values', () => { + prepare_dataset_layer_test('r', (dataset, layer) => { + let count = 0 + for (const feature of layer.features) { + assert.instanceOf(feature, gdal.Feature) + count++ + } + assert.equal(count, layer.features.count()) + }) + }) + it('should throw error if dataset is destroyed', () => { + prepare_dataset_layer_test('r', (dataset, layer) => { + dataset.close() + assert.throws(() => { + for (const l of layer.features) l + }, /already destroyed/) + }) + }) + }) describe('map()', () => { it('should operate normally', () => { prepare_dataset_layer_test('r', (dataset, layer) => { diff --git a/test/api_linestring.test.js b/test/api_linestring.test.js index 55be39dec..6b5c3f49f 100644 --- a/test/api_linestring.test.js +++ b/test/api_linestring.test.js @@ -235,6 +235,20 @@ describe('gdal.LineString', () => { assert.deepEqual(x_actual, x_expected) }) }) + describe('@@iterator()', () => { + it('should iterate through all points', () => { + const x_expected = [ 1, 2, 3 ] + const x_actual = [] + const line = new gdal.LineString() + line.points.add(1, 2, 3) + line.points.add(2, 3, 4) + line.points.add(3, 4, 5) + for (const pt of line.points) { + x_actual.push(pt.x) + } + assert.deepEqual(x_actual, x_expected) + }) + }) describe('map()', () => { it('should operate normally', () => { const line = new gdal.LineString() diff --git a/test/api_polygon.test.js b/test/api_polygon.test.js index af7e43817..4422c7acb 100644 --- a/test/api_polygon.test.js +++ b/test/api_polygon.test.js @@ -78,31 +78,55 @@ describe('gdal.Polygon', () => { assert.equal(polygon.rings.count(), 2) }) }) - describe('forEach()', () => { - let polygon - before(() => { - const ring1 = new gdal.LinearRing() - ring1.points.add(0, 0, 0) - ring1.points.add(10, 0, 0) - ring1.points.add(10, 11, 0) - ring1.points.add(0, 10, 0) - ring1.points.add(0, 0, 0) - const ring2 = new gdal.LinearRing() - ring2.points.add(1, 0, 0) - ring2.points.add(11, 0, 0) - ring2.points.add(11, 11, 0) - ring2.points.add(1, 10, 0) - ring2.points.add(1, 0, 0) - const ring3 = new gdal.LinearRing() - ring3.points.add(2, 0, 0) - ring3.points.add(20, 0, 0) - ring3.points.add(20, 11, 0) - ring3.points.add(3, 10, 0) - ring3.points.add(3, 0, 0) + let polygon + const createRings = () => { + const ring1 = new gdal.LinearRing() + ring1.points.add(0, 0, 0) + ring1.points.add(10, 0, 0) + ring1.points.add(10, 11, 0) + ring1.points.add(0, 10, 0) + ring1.points.add(0, 0, 0) + const ring2 = new gdal.LinearRing() + ring2.points.add(1, 0, 0) + ring2.points.add(11, 0, 0) + ring2.points.add(11, 11, 0) + ring2.points.add(1, 10, 0) + ring2.points.add(1, 0, 0) + const ring3 = new gdal.LinearRing() + ring3.points.add(2, 0, 0) + ring3.points.add(20, 0, 0) + ring3.points.add(20, 11, 0) + ring3.points.add(3, 10, 0) + ring3.points.add(3, 0, 0) - polygon = new gdal.Polygon() - polygon.rings.add([ ring1, ring2, ring3 ]) - }) + polygon = new gdal.Polygon() + polygon.rings.add([ ring1, ring2, ring3 ]) + } + const original = [ + [ + '{ "type": "Point", "coordinates": [ 0.0, 0.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 10.0, 0.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 10.0, 11.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 0.0, 10.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 0.0, 0.0, 0.0 ] }' + ], + [ + '{ "type": "Point", "coordinates": [ 1.0, 0.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 11.0, 0.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 11.0, 11.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 1.0, 10.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 1.0, 0.0, 0.0 ] }' + ], + [ + '{ "type": "Point", "coordinates": [ 2.0, 0.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 20.0, 0.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 20.0, 11.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 3.0, 10.0, 0.0 ] }', + '{ "type": "Point", "coordinates": [ 3.0, 0.0, 0.0 ] }' + ] + ] + describe('forEach()', () => { + before(createRings) it('should stop if callback returns false', () => { let count = 0 polygon.rings.forEach((pt, i) => { @@ -118,29 +142,7 @@ describe('gdal.Polygon', () => { result.push(ring.points.toArray().map((pt) => pt.toJSON())) }) - assert.deepEqual(result, [ - [ - '{ "type": "Point", "coordinates": [ 0.0, 0.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 10.0, 0.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 10.0, 11.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 0.0, 10.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 0.0, 0.0, 0.0 ] }' - ], - [ - '{ "type": "Point", "coordinates": [ 1.0, 0.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 11.0, 0.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 11.0, 11.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 1.0, 10.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 1.0, 0.0, 0.0 ] }' - ], - [ - '{ "type": "Point", "coordinates": [ 2.0, 0.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 20.0, 0.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 20.0, 11.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 3.0, 10.0, 0.0 ] }', - '{ "type": "Point", "coordinates": [ 3.0, 0.0, 0.0 ] }' - ] - ]) + assert.deepEqual(result, original) }) }) describe('map()', () => { @@ -165,6 +167,17 @@ describe('gdal.Polygon', () => { assert.equal(result[0], 'a') }) }) + describe('@@iterator()', () => { + before(createRings) + it('should iterate through all points', () => { + const result = [] + for (const ring of polygon.rings) { + result.push(ring.points.toArray().map((pt) => pt.toJSON())) + } + + assert.deepEqual(result, original) + }) + }) describe('toArray()', () => { it('should return array of LinearRing instances', () => { const polygon = new gdal.Polygon() diff --git a/test/api_rasterband.test.js b/test/api_rasterband.test.js index c19115ba6..ea972f2fb 100644 --- a/test/api_rasterband.test.js +++ b/test/api_rasterband.test.js @@ -1105,6 +1105,33 @@ describe('gdal.RasterBand', () => { }) }) }) + describe('@@iterator()', () => { + it('should iterate through the overviews', () => { + const ds = gdal.open( + fileUtils.clone(`${__dirname}/data/sample.tif`), + 'r+' + ) + const band = ds.bands.get(1) + ds.buildOverviews('NEAREST', [ 2, 4 ]) + const w = [] + for (const overview of band.overviews) { + w.push(overview.size.x) + } + assert.sameMembers(w, [ ds.rasterSize.x / 2, ds.rasterSize.x / 4 ]) + }) + it('should throw error if dataset already closed', () => { + const ds = gdal.open( + fileUtils.clone(`${__dirname}/data/sample.tif`), + 'r+' + ) + const band = ds.bands.get(1) + ds.buildOverviews('NEAREST', [ 2 ]) + ds.close() + assert.throws(() => { + for (const overview of band.overviews) overview + }) + }) + }) describe('map()', () => { it('should operate normally', () => { const ds = gdal.open(