From e552194546d17ce95a9d2d5f3f85b0796ea1c49c Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Thu, 17 Oct 2024 18:44:48 +0200 Subject: [PATCH] basic metadata support (#47) * basic metadata support * include the table and type name in the debug message * render the optional typemaps generic * fully generic optional * simplify the optional typemap using $*1_ltype * use colored YES/NO * final version * this is not a problem anymore * fix optional returning an array in TS and test one string --- meson.build | 8 ++-- src/metadata.i | 39 +++++++++++++++ src/operation.i | 21 ++++++++- src/optional.i | 63 +++++++++++++++++++++---- src/proj.i | 2 + src/util.i | 43 ++++++++--------- test/shared/coordinateoperation.test.ts | 2 +- test/shared/crs.test.ts | 14 ++++++ test/shared/metadata.test.ts | 41 ++++++++++++++++ 9 files changed, 196 insertions(+), 37 deletions(-) create mode 100644 src/metadata.i create mode 100644 test/shared/metadata.test.ts diff --git a/meson.build b/meson.build index a7117be..6ea2a75 100644 --- a/meson.build +++ b/meson.build @@ -52,10 +52,10 @@ endif # Build PROJ and its dependencies cmake = import('cmake') summary({ - 'libcurl enabled' : enable_curl, - 'libtiff enabled' : enable_tiff, - 'inline proj.db' : inline_projdb -}) + 'libcurl' : enable_curl, + 'libtiff' : enable_tiff, + 'inline proj.db' : inline_projdb +}, bool_yn: true) if inline_projdb add_global_link_arguments([ diff --git a/src/metadata.i b/src/metadata.i new file mode 100644 index 0000000..95d0185 --- /dev/null +++ b/src/metadata.i @@ -0,0 +1,39 @@ +%nn_shared_ptr(osgeo::proj::metadata::Identifier); +%nn_shared_ptr(osgeo::proj::metadata::Citation); +%nn_shared_ptr(osgeo::proj::metadata::Extent); +%nn_shared_ptr(osgeo::proj::metadata::GeographicBoundingBox); +%nn_shared_ptr(osgeo::proj::metadata::TemporalExtent); +%nn_shared_ptr(osgeo::proj::metadata::VerticalExtent); +%nn_shared_ptr(osgeo::proj::metadata::PositionalAccuracy); + +%define IDENTIFIER_DOWNCAST_TABLE_ENTRY(TYPE) +identifier_downcast_table.insert({typeid(TYPE).hash_code(), $descriptor(TYPE *)}) +%enddef + +%fragment("identifier_downcast_table", "header", fragment="include_map") { + std::map identifier_downcast_table; + + void init_identifier_downcast_table() { + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::Identifier); + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::Citation); + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::Extent); + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::GeographicBoundingBox); + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::TemporalExtent); + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::VerticalExtent); + IDENTIFIER_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::PositionalAccuracy); + } +} + +%init { + init_identifier_downcast_table(); +} + +// See util.i for the downcasting mechanics +// This overwrites the typemap created by %nn_shared_ptr +%typemap(out, fragment="identifier_downcast_table") dropbox::oxygen::nn> { + TRY_DOWNCASTING($1, $result, osgeo::proj::metadata::Identifier, identifier_downcast_table) +} + +// Extent is nullable when not an nn pointer +%typemap(ts) osgeo::proj::metadata::ExtentPtr "Extent | null"; +%typemap(ts) const osgeo::proj::metadata::ExtentPtr & "Extent | null"; diff --git a/src/operation.i b/src/operation.i index 2dd42d2..23848c6 100644 --- a/src/operation.i +++ b/src/operation.i @@ -16,8 +16,27 @@ %nn_unique_ptr(osgeo::proj::operation::CoordinateOperationFactory); %nn_unique_ptr(osgeo::proj::operation::CoordinateTransformer); +%define COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(TYPE) +coordinate_operation_downcast_table.insert({typeid(TYPE).hash_code(), $descriptor(TYPE *)}) +%enddef + +%fragment("coordinate_operation_downcast_table", "header", fragment="include_map") { + std::map coordinate_operation_downcast_table; + + void init_coordinate_operation_downcast_table() { + COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::CoordinateOperation); + COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::SingleOperation); + COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::ConcatenatedOperation); + COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::Conversion); + } +} + +%init { + init_coordinate_operation_downcast_table(); +} + // See util.i for the downcasting mechanics // This overwrites the typemap created by %nn_shared_ptr -%typemap(out, fragment="downcast_tables") dropbox::oxygen::nn> { +%typemap(out, fragment="coordinate_operation_downcast_table") dropbox::oxygen::nn> { TRY_DOWNCASTING($1, $result, osgeo::proj::operation::CoordinateOperation, coordinate_operation_downcast_table) } diff --git a/src/optional.i b/src/optional.i index 0c59c0b..d9b694a 100644 --- a/src/optional.i +++ b/src/optional.i @@ -1,32 +1,68 @@ -// C++ osgeo::proj::util::optional <-> JS string +// osgeo::proj::util::optional -// This is almost a string, but not quite +/* + * Input arguments + */ -// Input argument, plain object, conversion by copying -%typemap(in) osgeo::proj::util::optional { +// Input argument, plain object, conversion by copying using the underlying object typemap +%typemap(in) osgeo::proj::util::optional { if (!$input.IsNull()) { - std::string opt_string; - $typemap(in, std::string, input=$input, 1=opt_string, argnum=$argnum); - $1 = osgeo::proj::util::optional{opt_string}; + $T0type obj; + $typemap(in, $T0type, input=$input, 1=obj, argnum=$argnum); + $1 = osgeo::proj::util::optional<$T0type>{obj}; + } +} +// Input argument, const reference, conversion by constructing an 'optional' +// around the object held by JS +// (DataEpoch & Measure do not support operator=, thus the unique_ptr gymnastics) +%typemap(in) const osgeo::proj::util::optional & (std::unique_ptr<$*1_ltype> opt_object) { + if (!$input.IsNull()) { + $T0type *obj; + $typemap(in, const $T0type &, input=$input, 1=obj, argnum=$argnum); + opt_object = std::make_unique<$*1_ltype>(*obj); + } else { + opt_object = std::make_unique<$*1_ltype>(); } + $1 = opt_object.get(); } + +// string and double are special cases, these are copied %typemap(in) const osgeo::proj::util::optional & (osgeo::proj::util::optional opt_string) { if (!$input.IsNull()) { $typemap(in, std::string, input=$input, 1=opt_string, argnum=$argnum); } $1 = &opt_string; } +%typemap(in) const osgeo::proj::util::optional & (osgeo::proj::util::optional opt_double) { + if (!$input.IsNull()) { + $typemap(in, double, input=$input, 1=opt_double, argnum=$argnum); + } + $1 = &opt_double; +} + +%typemap(ts) osgeo::proj::util::optional, const osgeo::proj::util::optional & "$typemap(ts, $T0type)"; -%typemap(ts) osgeo::proj::util::optional, const osgeo::proj::util::optional & "string"; +/* + * Return values + */ // Return value, plain object, conversion by copying -%typemap(out) osgeo::proj::util::optional { +%typemap(out) osgeo::proj::util::optional { if ($1.has_value()) { - $typemap(out, std::string, 1=(*$1), result=$result, argnum=$argnum); + $typemap(out, $T0type, 1=(*$1), result=$result, argnum=$argnum); } else { $result = env.Null(); } } +// Return value, const reference, conversion by wrapping a constant object +%typemap(out) const osgeo::proj::util::optional & { + if ($1->has_value()) { + $typemap(out, const $T0type &, 1=&(*$1), result=$result, argnum=$argnum); + } else { + $result = env.Null(); + } +} +// string and double are special cases, these are copied %typemap(out) const osgeo::proj::util::optional & { if ($1->has_value()) { $typemap(out, const std::string &, 1=(*$1), result=$result, argnum=$argnum); @@ -34,3 +70,10 @@ $result = env.Null(); } } +%typemap(out) const osgeo::proj::util::optional & { + if ($1->has_value()) { + $typemap(out, double, 1=(**$1), result=$result, argnum=$argnum); + } else { + $result = env.Null(); + } +} diff --git a/src/proj.i b/src/proj.i index 44f3e7e..9cf35d7 100644 --- a/src/proj.i +++ b/src/proj.i @@ -62,6 +62,7 @@ using namespace NS_PROJ; %include "util.i" %include "common.i" %include "io.i" +%include "metadata.i" %include "operation.i" %include "datum.i" %include "coordinatesystem.i" @@ -112,6 +113,7 @@ using namespace NS_PROJ; %include %include +%include %include %include %include diff --git a/src/util.i b/src/util.i index 5bda794..dd17ae5 100644 --- a/src/util.i +++ b/src/util.i @@ -42,16 +42,14 @@ baseobject_downcast_table.insert({typeid(TYPE).hash_code(), $descriptor(TYPE *)}) %enddef -%define COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(TYPE) -coordinate_operation_downcast_table.insert({typeid(TYPE).hash_code(), $descriptor(TYPE *)}) -%enddef - -%fragment("downcast_tables", "header") { +%fragment("include_map", "header") { #include +} + +%fragment("baseobject_downcast_table", "header", fragment="include_map") { std::map baseobject_downcast_table; - std::map coordinate_operation_downcast_table; - void init_downcast_tables() { + void init_baseobject_downcast_table() { BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::crs::ProjectedCRS); BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::crs::GeographicCRS); BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::crs::VerticalCRS); @@ -72,16 +70,24 @@ coordinate_operation_downcast_table.insert({typeid(TYPE).hash_code(), $descripto BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::crs::DerivedEngineeringCRS); BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::crs::DerivedParametricCRS); BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::crs::DerivedTemporalCRS); - - COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::CoordinateOperation); - COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::SingleOperation); - COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::ConcatenatedOperation); - COORDINATE_OPERATION_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::Conversion); + // These can be downcasted both from BaseObject and from CoordinateOperation (in operation.i) + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::CoordinateOperation); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::SingleOperation); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::ConcatenatedOperation); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::operation::Conversion); + // These can be downcasted both from BaseObject and from Identifier (in metadata.i) + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::Identifier); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::Citation); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::Extent); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::GeographicBoundingBox); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::TemporalExtent); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::VerticalExtent); + BASEOBJECT_DOWNCAST_TABLE_ENTRY(osgeo::proj::metadata::PositionalAccuracy); } } %init { - init_downcast_tables(); + init_baseobject_downcast_table(); } /* @@ -93,10 +99,10 @@ coordinate_operation_downcast_table.insert({typeid(TYPE).hash_code(), $descripto */ %define TRY_DOWNCASTING(INPUT, OUTPUT, BASE_TYPE, TABLE) std::size_t rtti_code = typeid(*INPUT.get()).hash_code(); - SWIG_VERBOSE("downcasting for type %s: ", typeid(*INPUT.get()).name()); + SWIG_VERBOSE(#TABLE ": downcasting for type %s: ", typeid(*INPUT.get()).name()); if (TABLE.count(rtti_code) > 0) { - SWIG_VERBOSE("found\n"); swig_type_info *info = TABLE.at(rtti_code); + SWIG_VERBOSE("found %s (%s)\n", info->str, info->name); OUTPUT = SWIG_NewPointerObj(INPUT.get(), info, SWIG_POINTER_OWN | %newpointer_flags); auto *owner = new std::shared_ptr(*&INPUT); auto finalizer = new SWIG_NAPI_Finalizer([owner](){ @@ -109,15 +115,10 @@ coordinate_operation_downcast_table.insert({typeid(TYPE).hash_code(), $descripto } %enddef -%typemap(out, fragment="downcast_tables") osgeo::proj::util::BaseObjectNNPtr { +%typemap(out, fragment="baseobject_downcast_table") osgeo::proj::util::BaseObjectNNPtr { TRY_DOWNCASTING($1, $result, osgeo::proj::util::BaseObject, baseobject_downcast_table) } -/* - * CoordinateOperation downcasting is in operation.i, it must come - * after the %nn_shared_ptr definition for CoordinateOperation - */ - /* * TypeScript has static compile-time checking just like C++, which means that * the explicit cast cannot be avoided diff --git a/test/shared/coordinateoperation.test.ts b/test/shared/coordinateoperation.test.ts index 1aaa791..f4152fc 100644 --- a/test/shared/coordinateoperation.test.ts +++ b/test/shared/coordinateoperation.test.ts @@ -3,7 +3,7 @@ import { assert } from 'chai'; import qPROJ from 'proj.js'; import type * as PROJ from 'proj.js'; -describe.only('CoordinateOperation with automatic import', () => { +describe('CoordinateOperation with automatic import', () => { let PROJ: Awaited; let dbContext: PROJ.DatabaseContext; diff --git a/test/shared/crs.test.ts b/test/shared/crs.test.ts index 2dafc89..5fb7f44 100644 --- a/test/shared/crs.test.ts +++ b/test/shared/crs.test.ts @@ -53,4 +53,18 @@ describe('CRS with automatic import', () => { assert.instanceOf(crs, PROJ.CRS); assert.instanceOf(crs, PROJ.ProjectedCRS); }); + + it('isDynamic (return bool)', () => { + const crs = authFactoryEPSG.createCoordinateReferenceSystem('3857'); + assert.isBoolean(crs.isDynamic()); + }); + + it('extract GeographicCRS (return CRS)', () => { + const crs = authFactoryEPSG.createCoordinateReferenceSystem('3857'); + const geographic = crs.extractGeographicCRS(); + assert.instanceOf(geographic, PROJ.CRS); + assert.instanceOf(geographic, PROJ.SingleCRS); + assert.instanceOf(geographic, PROJ.GeographicCRS); + }); + }); diff --git a/test/shared/metadata.test.ts b/test/shared/metadata.test.ts new file mode 100644 index 0000000..afb835f --- /dev/null +++ b/test/shared/metadata.test.ts @@ -0,0 +1,41 @@ +import { assert } from 'chai'; + +import qPROJ from 'proj.js'; +import type * as PROJ from 'proj.js'; + +describe('metadata with automatic import', () => { + let PROJ: Awaited; + + let dbContext: PROJ.DatabaseContext; + let authFactory: PROJ.AuthorityFactory; + let authFactoryEPSG: PROJ.AuthorityFactory; + + before('init', async () => { + PROJ = await qPROJ; + dbContext = PROJ.DatabaseContext.create(); + authFactory = PROJ.AuthorityFactory.create(dbContext, 'string'); + authFactoryEPSG = PROJ.AuthorityFactory.create(dbContext, 'EPSG'); + }); + + it('static properties', () => { + assert.isString(PROJ.Identifier.AUTHORITY_KEY); + assert.isString(PROJ.Identifier.CODESPACE_KEY); + }); + + it('IdentifiedObject properties', () => { + const crs = authFactoryEPSG.createCoordinateReferenceSystem('3857'); + const metadata = crs.identifiers(); + assert.isArray(metadata); + for (const m of metadata) { + assert.instanceOf(m, PROJ.Identifier); + assert.instanceOf(m, PROJ.BaseObject); + assert.isString(m.code()); + assert.isString(m.codeSpace()); + assert.strictEqual(m.code(), '3857'); + assert.strictEqual(m.codeSpace(), 'EPSG'); + assert.isNull(m.authority()); + assert.isNull(m.uri()); + } + }); + +});