Skip to content

Commit

Permalink
basic metadata support (#47)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mmomtchev authored Oct 17, 2024
1 parent 3290559 commit e552194
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 37 deletions.
8 changes: 4 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
39 changes: 39 additions & 0 deletions src/metadata.i
Original file line number Diff line number Diff line change
@@ -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<std::size_t, swig_type_info *> 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<std::shared_ptr<osgeo::proj::metadata::Identifier>> {
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";
21 changes: 20 additions & 1 deletion src/operation.i
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::size_t, swig_type_info *> 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<std::shared_ptr<osgeo::proj::operation::CoordinateOperation>> {
%typemap(out, fragment="coordinate_operation_downcast_table") dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::operation::CoordinateOperation>> {
TRY_DOWNCASTING($1, $result, osgeo::proj::operation::CoordinateOperation, coordinate_operation_downcast_table)
}
63 changes: 53 additions & 10 deletions src/optional.i
Original file line number Diff line number Diff line change
@@ -1,36 +1,79 @@
// C++ osgeo::proj::util::optional<std::string> <-> 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<std::string> {
// 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<std::string>{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<std::string> & (osgeo::proj::util::optional<std::string> 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<double> & (osgeo::proj::util::optional<double> 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<std::string>, const osgeo::proj::util::optional<std::string> & "string";
/*
* Return values
*/

// Return value, plain object, conversion by copying
%typemap(out) osgeo::proj::util::optional<std::string> {
%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<std::string> & {
if ($1->has_value()) {
$typemap(out, const std::string &, 1=(*$1), result=$result, argnum=$argnum);
} else {
$result = env.Null();
}
}
%typemap(out) const osgeo::proj::util::optional<double> & {
if ($1->has_value()) {
$typemap(out, double, 1=(**$1), result=$result, argnum=$argnum);
} else {
$result = env.Null();
}
}
2 changes: 2 additions & 0 deletions src/proj.i
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -112,6 +113,7 @@ using namespace NS_PROJ;

%include <proj/util.hpp>
%include <proj/io.hpp>
%include <proj/metadata.hpp>
%include <proj/common.hpp>
%include <proj/datum.hpp>
%include <proj/coordinatesystem.hpp>
Expand Down
43 changes: 22 additions & 21 deletions src/util.i
Original file line number Diff line number Diff line change
Expand Up @@ -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 <map>
}

%fragment("baseobject_downcast_table", "header", fragment="include_map") {
std::map<std::size_t, swig_type_info *> baseobject_downcast_table;
std::map<std::size_t, swig_type_info *> 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);
Expand All @@ -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();
}

/*
Expand All @@ -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<BASE_TYPE>(*&INPUT);
auto finalizer = new SWIG_NAPI_Finalizer([owner](){
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/shared/coordinateoperation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof qPROJ>;

let dbContext: PROJ.DatabaseContext;
Expand Down
14 changes: 14 additions & 0 deletions test/shared/crs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

});
41 changes: 41 additions & 0 deletions test/shared/metadata.test.ts
Original file line number Diff line number Diff line change
@@ -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<typeof qPROJ>;

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());
}
});

});

0 comments on commit e552194

Please sign in to comment.