From 0016d47f22bb8d9f6170477f3cc45e1ecaa00af3 Mon Sep 17 00:00:00 2001 From: Asa Packer Date: Wed, 23 Oct 2019 12:33:40 -0700 Subject: Fix errors running gie-based tests in Debug mode on Windows --- cmake/ProjTest.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/ProjTest.cmake b/cmake/ProjTest.cmake index 277e107a..ad64d0ba 100644 --- a/cmake/ProjTest.cmake +++ b/cmake/ProjTest.cmake @@ -36,7 +36,7 @@ endfunction() function(proj_add_gie_test TESTNAME TESTCASE) - set(GIE_BIN "gie") + set(GIE_BIN $) set(TESTFILE ${CMAKE_SOURCE_DIR}/test/${TESTCASE}) add_test(NAME ${TESTNAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test -- cgit v1.2.3 From fc769bbd9a4fb61e96e500788d24d1d12019a4d0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 29 Oct 2019 23:34:59 +0100 Subject: news.rst: fix hyperlink --- docs/source/news.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/news.rst b/docs/source/news.rst index 2a6e8e47..1fba43b9 100644 --- a/docs/source/news.rst +++ b/docs/source/news.rst @@ -10,7 +10,7 @@ News Updates ------- - * Introduced PROJJSON, a JSON encoding of WKT2 (`#1547 `_) + * Introduced PROJJSON, a JSON encoding of WKT2 (`#1547 `_) * Support CRS instantiation of OGC URN's (`#1505 `_) * Expose scope and remarks of database objects (`#1537 `_) -- cgit v1.2.3 From 6ce8ef30389480b3fabc3991bdf2b476d9435b60 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 30 Oct 2019 16:22:17 +0100 Subject: Document PROJJSON More could probably be written, but at least this can serve as a landing/reference page for other documents/specifications to point to. --- docs/source/news.rst | 2 +- docs/source/usage/index.rst | 2 + docs/source/usage/projjson.rst | 261 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 docs/source/usage/projjson.rst diff --git a/docs/source/news.rst b/docs/source/news.rst index 1fba43b9..bb8f63f9 100644 --- a/docs/source/news.rst +++ b/docs/source/news.rst @@ -10,7 +10,7 @@ News Updates ------- - * Introduced PROJJSON, a JSON encoding of WKT2 (`#1547 `_) + * Introduced :ref:`PROJJSON`, a JSON encoding of WKT2 (`#1547 `_) * Support CRS instantiation of OGC URN's (`#1505 `_) * Expose scope and remarks of database objects (`#1537 `_) diff --git a/docs/source/usage/index.rst b/docs/source/usage/index.rst index 823e8fe7..c31c6dce 100644 --- a/docs/source/usage/index.rst +++ b/docs/source/usage/index.rst @@ -17,3 +17,5 @@ command line applications or the C API that is a part of the software package. transformation environmentvars differences + projjson + diff --git a/docs/source/usage/projjson.rst b/docs/source/usage/projjson.rst new file mode 100644 index 00000000..09123711 --- /dev/null +++ b/docs/source/usage/projjson.rst @@ -0,0 +1,261 @@ +.. _projjson: + +================================================================================ +PROJJSON +================================================================================ + +PROJJSON is a JSON encoding of +`WKT2:2019 / ISO-19162:2019 `_, +which itself implements the model of +`OGC Topic 2: Referencing by coordinates `_. +Apart from the difference of encodings, the semantics is intented to be exactly +the same as WKT2:2019. + +PROJJSON is available as input and output of PROJ since PROJ 6.2. + +The current version is 0.1. + +Schema +------ + +A JSON schema of its grammar is available at +https://proj.org/schemas/v0.1/projjson.schema.json + +Content +------- + +The high level objects are: + +* Coordinate Reference Systems (CRS): + + - Common ones: + + + ``GeographicCRS`` + + ``GeodeticCRS`` + + ``ProjectedCRS`` + + ``CompoundCRS`` + + ``BoundCRS`` + + - More esoteric ones: + + + ``VerticalCRS`` + + ``EngineeringCRS`` + + ``TemporalCRS`` + + ``ParametricCRS`` + + ``DerivedGeographicCRS`` + + ``DerivedGeodeticCRS`` + + ``DerivedProjectedCRS`` + + ``DerivedVerticalCRS`` + + ``DerivedEngineeringCRS`` + + ``DerivedTemporalCRS`` + + ``DerivedParametricCRS`` + +* Coordinate operations: + + - ``Transformation`` + - ``Conversion`` + - ``ConcatenatedOperation`` + +* Others: + + - ``PrimeMeridian`` + - ``Ellipsoid`` + - ``Datum`` + - ``DatumEnsemble`` + +Examples +-------- + +GeographicCRS ++++++++++++++ + +The following invokation + +:: + + projinfo EPSG:4326 -o PROJJSON -q + +will output: + +.. code-block:: json + + { + "$schema": "https://proj.org/schemas/v0.1/projjson.schema.json", + "type": "GeographicCRS", + "name": "WGS 84", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "World Geodetic System 1984", + "ellipsoid": { + "name": "WGS 84", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257223563 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + }, + "area": "World", + "bbox": { + "south_latitude": -90, + "west_longitude": -180, + "north_latitude": 90, + "east_longitude": 180 + }, + "id": { + "authority": "EPSG", + "code": 4326 + } + } + + +ProjectedCRS +++++++++++++ + +The following invokation + +:: + + projinfo EPSG:32631 -o PROJJSON -q + +will output: + +.. code-block:: json + + { + "$schema": "https://proj.org/schemas/v0.1/projjson.schema.json", + "type": "ProjectedCRS", + "name": "WGS 84 / UTM zone 31N", + "base_crs": { + "name": "WGS 84", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "World Geodetic System 1984", + "ellipsoid": { + "name": "WGS 84", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257223563 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 4326 + } + }, + "conversion": { + "name": "UTM zone 31N", + "method": { + "name": "Transverse Mercator", + "id": { + "authority": "EPSG", + "code": 9807 + } + }, + "parameters": [ + { + "name": "Latitude of natural origin", + "value": 0, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8801 + } + }, + { + "name": "Longitude of natural origin", + "value": 3, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8802 + } + }, + { + "name": "Scale factor at natural origin", + "value": 0.9996, + "unit": "unity", + "id": { + "authority": "EPSG", + "code": 8805 + } + }, + { + "name": "False easting", + "value": 500000, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8806 + } + }, + { + "name": "False northing", + "value": 0, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8807 + } + } + ] + }, + "coordinate_system": { + "subtype": "Cartesian", + "axis": [ + { + "name": "Easting", + "abbreviation": "E", + "direction": "east", + "unit": "metre" + }, + { + "name": "Northing", + "abbreviation": "N", + "direction": "north", + "unit": "metre" + } + ] + }, + "area": "World - N hemisphere - 0°E to 6°E - by country", + "bbox": { + "south_latitude": 0, + "west_longitude": 0, + "north_latitude": 84, + "east_longitude": 6 + }, + "id": { + "authority": "EPSG", + "code": 32631 + } + } -- cgit v1.2.3 From 1920c5cee79f258a69f1000124fd6fbde20f1490 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 30 Oct 2019 17:41:10 +0100 Subject: Doc: add a redirects extension, and make a projjson.html redirect at the top to its current location --- docs/source/_extensions/redirects.py | 39 ++++++++++++++++++++++++++++++++++++ docs/source/conf.py | 5 +++++ 2 files changed, 44 insertions(+) create mode 100644 docs/source/_extensions/redirects.py diff --git a/docs/source/_extensions/redirects.py b/docs/source/_extensions/redirects.py new file mode 100644 index 00000000..6a59b622 --- /dev/null +++ b/docs/source/_extensions/redirects.py @@ -0,0 +1,39 @@ +import os + +# https://tech.signavio.com/2017/managing-sphinx-redirects + + +template=""" + + + + +""" + + +def gather_redirects(): + output = {} + + output.update({ 'projjson.html' : 'usage/projjson.html' }) + return output + +from shutil import copyfile +# copy legacy redirects +def copy_legacy_redirects(app, docname): # Sphinx expects two arguments + if app.builder.name == 'html': + for key in app.config.redirect_files: + src = key + tgt = app.config.redirect_files[key] + html = template % (tgt, tgt) + with open(os.path.join(app.outdir, src), 'wb') as f: + f.write(html.encode('utf-8')) + f.close() + + + +def setup(app): + app.add_config_value('redirect_files', {}, 'html') + app.connect('build-finished', copy_legacy_redirects) + return { 'parallel_read_safe': False, 'parallel_write_safe': True } diff --git a/docs/source/conf.py b/docs/source/conf.py index 243c93b7..cd004777 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,6 +20,7 @@ import datetime # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('_extensions')) import bibstyle @@ -35,6 +36,7 @@ extensions = [ 'sphinx.ext.mathjax', 'sphinxcontrib.bibtex', 'breathe', + 'redirects', ] # Add any paths that contain templates here, relative to this directory. @@ -370,3 +372,6 @@ texinfo_documents = [ breathe_projects = { "cpp_stuff":"../build/xml/", } + +import redirects +redirect_files = redirects.gather_redirects() -- cgit v1.2.3 From 9095cf6fa351b5e6208cec811b86eb3d958c6f06 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 30 Oct 2019 16:56:41 +0100 Subject: createFromWkt(): be tolerant to missing scale_factor parameter (fixes #1700) This is invalid WKT, but GDAL 2.4 used to accept it and make a reasonable use of it... Currently we default it to 0 which is non sensical. Better use 1 as GDAL 2.4 did, and emit a warning. Other fix: proj_create_from_wkt() was documented to operate by default in non-strict validation mode, but it was actually in strict mode. So do as documented. --- src/iso19111/c_api.cpp | 20 ++++++++++--- src/iso19111/coordinateoperation.cpp | 33 +++++++++++++--------- src/iso19111/io.cpp | 55 +++++++++++++++++++++++++++++++++++- test/unit/test_c_api.cpp | 6 ++-- test/unit/test_io.cpp | 34 ++++++++++++++++++++++ 5 files changed, 128 insertions(+), 20 deletions(-) diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index f5f7ba55..337de313 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -498,7 +498,7 @@ template static PROJ_STRING_LIST to_string_list(T &&set) { * proj_string_list_destroy(). * @param out_grammar_errors Pointer to a PROJ_STRING_LIST object, or NULL. * If provided, *out_grammar_errors will contain a list of errors regarding the - * WKT grammaer. It must be freed with proj_string_list_destroy(). + * WKT grammar. It must be freed with proj_string_list_destroy(). * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ @@ -522,6 +522,7 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, if (dbContext) { parser.attachDatabaseContext(NN_NO_CHECK(dbContext)); } + parser.setStrict(false); for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "STRICT="))) { @@ -536,10 +537,19 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, auto obj = nn_dynamic_pointer_cast( parser.createFromWKT(wkt)); + std::vector warningsFromParsing; if (out_grammar_errors) { - auto warnings = parser.warningList(); - if (!warnings.empty()) { - *out_grammar_errors = to_string_list(warnings); + auto rawWarnings = parser.warningList(); + std::vector grammarWarnings; + for (const auto &msg : rawWarnings) { + if (msg.find("Default it to") != std::string::npos) { + warningsFromParsing.push_back(msg); + } else { + grammarWarnings.push_back(msg); + } + } + if (!grammarWarnings.empty()) { + *out_grammar_errors = to_string_list(grammarWarnings); } } @@ -548,6 +558,8 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, if (derivedCRS) { auto warnings = derivedCRS->derivingConversionRef()->validateParameters(); + warnings.insert(warnings.end(), warningsFromParsing.begin(), + warningsFromParsing.end()); if (!warnings.empty()) { *out_warnings = to_string_list(warnings); } diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index c7581642..5ac81aa1 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -6056,24 +6056,31 @@ void Conversion::_exportToPROJString( if (!param->proj_name) { continue; } - auto value = + const auto value = parameterValueMeasure(param->wkt2_name, param->epsg_code); + double valueConverted = 0; + if (value == nullMeasure) { + // Deal with missing values. In an ideal world, this would + // not happen + if (param->epsg_code == + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN) { + valueConverted = 1.0; + } + } else if (param->unit_type == + common::UnitOfMeasure::Type::ANGULAR) { + valueConverted = + value.convertToUnit(common::UnitOfMeasure::DEGREE); + } else { + valueConverted = value.getSIValue(); + } + if (mapping->epsg_code == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP && strcmp(param->proj_name, "lat_1") == 0) { - formatter->addParam( - param->proj_name, - value.convertToUnit(common::UnitOfMeasure::DEGREE)); - formatter->addParam( - "lat_0", - value.convertToUnit(common::UnitOfMeasure::DEGREE)); - } else if (param->unit_type == - common::UnitOfMeasure::Type::ANGULAR) { - formatter->addParam( - param->proj_name, - value.convertToUnit(common::UnitOfMeasure::DEGREE)); + formatter->addParam(param->proj_name, valueConverted); + formatter->addParam("lat_0", valueConverted); } else { - formatter->addParam(param->proj_name, value.getSIValue()); + formatter->addParam(param->proj_name, valueConverted); } } diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index fc66b6c9..50ad5a87 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -3476,6 +3476,14 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( } propertiesMethod.set(IdentifiedObject::NAME_KEY, projectionName); + std::vector foundParameters; + if (mapping) { + size_t countParams = 0; + while (mapping->params[countParams] != nullptr) { + ++countParams; + } + foundParameters.resize(countParams); + } for (const auto &childNode : projCRSNode->GP()->children()) { if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) { const auto &childNodeChildren = childNode->GP()->children(); @@ -3496,11 +3504,18 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( continue; } } - const auto *paramMapping = + auto *paramMapping = mapping ? getMappingFromWKT1(mapping, parameterName) : nullptr; if (mapping && mapping->epsg_code == EPSG_CODE_METHOD_MERCATOR_VARIANT_B && ci_equal(parameterName, "latitude_of_origin")) { + for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { + if (mapping->params[idx]->epsg_code == + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) { + foundParameters[idx] = true; + break; + } + } parameterName = EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN; propertiesParameter.set( Identifier::CODE_KEY, @@ -3508,6 +3523,12 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } else if (paramMapping) { + for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { + if (mapping->params[idx] == paramMapping) { + foundParameters[idx] = true; + break; + } + } parameterName = paramMapping->wkt2_name; if (paramMapping->epsg_code != 0) { propertiesParameter.set(Identifier::CODE_KEY, @@ -3531,6 +3552,38 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard( } } + // Add back important parameters that should normally be present, but + // are sometimes missing. Currently we only deal with Scale factor at + // natural origin. This is to avoid a default value of 0 to slip in later. + // But such WKT should be considered invalid. + if (mapping) { + for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { + if (!foundParameters[idx] && + mapping->params[idx]->epsg_code == + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN) { + + emitRecoverableWarning( + "The WKT string lacks a value " + "for " EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN + ". Default it to 1."); + + PropertyMap propertiesParameter; + propertiesParameter.set( + Identifier::CODE_KEY, + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); + propertiesParameter.set(Identifier::CODESPACE_KEY, + Identifier::EPSG); + propertiesParameter.set( + IdentifiedObject::NAME_KEY, + EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); + parameters.push_back( + OperationParameter::create(propertiesParameter)); + values.push_back(ParameterValue::create( + Measure(1.0, UnitOfMeasure::SCALE_UNITY))); + } + } + } + return Conversion::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), propertiesMethod, parameters, values) diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 8ac5a511..b8310ce5 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -231,7 +231,8 @@ TEST_F(CApi, proj_create_from_wkt) { " PRIMEM[\"Greenwich\",0],\n" " UNIT[\"degree\",0.0174532925199433]]", nullptr, nullptr, nullptr); - EXPECT_EQ(obj, nullptr); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); } { PROJ_STRING_LIST warningList = nullptr; @@ -244,7 +245,8 @@ TEST_F(CApi, proj_create_from_wkt) { " PRIMEM[\"Greenwich\",0],\n" " UNIT[\"degree\",0.0174532925199433]]", nullptr, &warningList, &errorList); - EXPECT_EQ(obj, nullptr); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); EXPECT_EQ(warningList, nullptr); proj_string_list_destroy(warningList); EXPECT_NE(errorList, nullptr); diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 074a59b9..8aff0908 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1174,6 +1174,40 @@ TEST(wkt_parse, wkt1_Mercator_1SP_with_latitude_origin_0) { // --------------------------------------------------------------------------- +TEST(wkt_parse, wkt1_Mercator_1SP_without_scale_factor) { + // See https://github.com/OSGeo/PROJ/issues/1700 + auto wkt = "PROJCS[\"unnamed\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"unknown\",\n" + " SPHEROID[\"WGS84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Mercator_1SP\"],\n" + " PARAMETER[\"central_meridian\",0],\n" + " PARAMETER[\"false_easting\",0],\n" + " PARAMETER[\"false_northing\",0],\n" + " UNIT[\"Meter\",1],\n" + " AXIS[\"Easting\",EAST],\n" + " AXIS[\"Northing\",NORTH]]"; + WKTParser parser; + parser.setStrict(false).attachDatabaseContext(DatabaseContext::create()); + auto obj = parser.createFromWKT(wkt); + EXPECT_TRUE(!parser.warningList().empty()); + + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); + EXPECT_TRUE(got_wkt.find("PARAMETER[\"scale_factor\",1]") != + std::string::npos) + << got_wkt; + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +units=m " + "+no_defs +type=crs"); +} + +// --------------------------------------------------------------------------- + TEST(wkt_parse, wkt1_krovak_south_west) { auto wkt = "PROJCS[\"S-JTSK / Krovak\"," -- cgit v1.2.3 From 1e960f99c719a4c51913b1cec168253c1ededb7f Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Fri, 1 Nov 2019 19:40:16 +0100 Subject: Update website with 6.2.1 release --- docs/source/download.rst | 6 ++-- docs/source/news.rst | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/source/download.rst b/docs/source/download.rst index 9ec03368..36e2a897 100644 --- a/docs/source/download.rst +++ b/docs/source/download.rst @@ -13,7 +13,7 @@ distribution of the source code and various resource file archives. See Current Release -------------------------------------------------------------------------------- -* **2019-09-01** `proj-6.2.0.tar.gz`_ (`md5`_) +* **2019-11-01** `proj-6.2.1.tar.gz`_ (`md5`_) * **2018-09-15** `proj-datumgrid-1.8.zip`_ * **2019-09-01** `proj-datumgrid-europe-1.4.zip`_ * **2019-03-01** `proj-datumgrid-north-america-1.2.zip`_ @@ -24,6 +24,7 @@ Current Release Past Releases -------------------------------------------------------------------------------- +* **2019-09-01** `proj-6.2.0.tar.gz`_ * **2019-07-01** `proj-6.1.1.tar.gz`_ * **2019-05-15** `proj-6.1.0.tar.gz`_ * **2019-03-01** `proj-6.0.0.tar.gz`_ @@ -44,6 +45,7 @@ Past Releases * **2018-03-01** `proj-datumgrid-europe-1.0.zip`_ * **2018-03-01** `proj-datumgrid-north-america-1.0.zip`_ +.. _`proj-6.2.1.tar.gz`: https://download.osgeo.org/proj/proj-6.2.1.tar.gz .. _`proj-6.2.0.tar.gz`: https://download.osgeo.org/proj/proj-6.2.0.tar.gz .. _`proj-6.1.1.tar.gz`: https://download.osgeo.org/proj/proj-6.1.1.tar.gz .. _`proj-6.1.0.tar.gz`: https://download.osgeo.org/proj/proj-6.1.0.tar.gz @@ -68,5 +70,5 @@ Past Releases .. _`proj-datumgrid-north-america-1.2.zip`: https://download.osgeo.org/proj/proj-datumgrid-north-america-1.2.zip .. _`proj-datumgrid-oceania-1.0.zip`: https://download.osgeo.org/proj/proj-datumgrid-oceania-1.0.zip .. _`proj-datumgrid-world-1.0.zip`: https://download.osgeo.org/proj/proj-datumgrid-world-1.0.zip -.. _`md5`: https://download.osgeo.org/proj/proj-6.2.0.tar.gz.md5 +.. _`md5`: https://download.osgeo.org/proj/proj-6.2.1.tar.gz.md5 .. _`proj.pdf`: https://raw.githubusercontent.com/OSGeo/PROJ/gh-pages/proj.pdf diff --git a/docs/source/news.rst b/docs/source/news.rst index 2a6e8e47..e70c6924 100644 --- a/docs/source/news.rst +++ b/docs/source/news.rst @@ -3,6 +3,77 @@ News ############################################################################### +6.2.1 Release Notes +++++++++++++++++++++++++++++++++++++++++ +*November 1st 2019* + +Updates +------- + +* Update the EPSG database to version 9.8.2 + +Bug fixes +---------- + +* Fixed erroneous spelling of "Potsdam" (`#1573 `_) + +* Calculate y-coordinate correctly in :ref:`bertin1953` in all cases (`#1579 `_) + +* :c:func:`proj_create_crs_to_crs_from_pj()`: make the PJ* arguments const PJ* (`#1583 `_) + +* :c:func:`PROJStringParser::createFromPROJString()`: avoid potential infinite + recursion (`#1574 `_) + +* Avoid core dump when setting ``ctx==NULL`` in functions + :c:func:`proj_coordoperation_is_instantiable` and + :c:func:`proj_coordoperation_has_ballpark_transformation` (`#1590 `_) + +* :c:func:`createOperations()`: fix conversion from/to PROJ.4 CRS strings with + non-ISO-kosher options and ``+towgs84``/``+nadgrids`` (`#1602 `_) + +* :c:func:`proj_trans_generic()`: properly set coordinate time to ``HUGE_VAL`` + when no value is passed to the function (`#1604 `_) + +* Fix support for ``+proj=ob_tran +o_proj=lonlat/latlong/latlon`` instead of only + only allowing ``+o_proj=longlat`` (`#1601 `_) + +* Improve backwards compatibility of vertical transforms (`#1613 `_) + +* Improve emulation of deprecated ``+init`` style initialization (`#1614 `_) + +* :program:`cs2cs`: autopromote CRS to 3D when there's a mix of 2D and 3D (`#1563 `_) + +* Avoid divisions by zero in odd situations (`#1620 `_) + +* Avoid compile error on Solaris (`#1639 `_) + +* :c:func:`proj_create_crs_to_crs()`: fix when there are only transformations with + ballpark steps (`#1643 `_) + +* PROJ string CRS ingester: recognize more unit-less parameters, and general + handling of ``+key=string_value`` parameters (`#1645 `_) + +* Only call pkg-config in configure when necessary (`#1652 `_) + +* :ref:`aeqd`: for spherical forward path, go to higher precision ellipsoidal + case when the point coordinates are super close to the origin (`#1654 `_) + +* :c:func:`proj_create_crs_to_crs()`: remove elimination of Ballpark operations + that caused transformation failures in some cases (`#1665 `_) + +* :c:func:`createOperations()`: allow transforming from a compoundCRS of a bound + verticalCRS to a 2D CRS (`#1667 `_) + +* Avoid segfaults in case of out-of-memory situations (`#1679 `_) + +* :c:func:`createOperations()`: fix double vertical unit conversion from CompoundCRS + to other CRS when the horizontal part of the projected CRS uses non-metre + unit (#1683)(`#1683 `_) + +* :c:func:`importFromWkt()`: fix axis orientation for non-standard ESRI WKT (`#1690 `_) + + + 6.2.0 Release Notes ++++++++++++++++++++++++++++++++++++++++ *September 1st 2019* -- cgit v1.2.3 From 5e5997a61062041c5833a4c6e7a46e8181d39c64 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 3 Nov 2019 15:33:55 +0100 Subject: createOperations(): in some circumstances we wrongly promoted a Helmert geog2D transformation to a geog3D Fixes for example EPSG:4979 to EPSG:2189, as raised in https://github.com/OSGeo/gdal/issues/1972#issuecomment-548814354 --- src/iso19111/coordinateoperation.cpp | 51 +++++++++++++++++++++++++++++------- test/unit/test_operation.cpp | 43 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index de5d56ea..0983c471 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -6718,6 +6718,24 @@ static void getTransformationType(const crs::CRSNNPtr &sourceCRSIn, isGeog2D = nSrcAxisCount == 2 && nTargetAxisCount == 2; isGeog3D = !isGeog2D && nSrcAxisCount >= 2 && nTargetAxisCount >= 2; } + +// --------------------------------------------------------------------------- + +static int +useOperationMethodEPSGCodeIfPresent(const util::PropertyMap &properties, + int nDefaultOperationMethodEPSGCode) { + const auto *operationMethodEPSGCode = + properties.get("OPERATION_METHOD_EPSG_CODE"); + if (operationMethodEPSGCode) { + const auto boxedValue = dynamic_cast( + (*operationMethodEPSGCode).get()); + if (boxedValue && + boxedValue->type() == util::BoxedValue::Type::INTEGER) { + return boxedValue->integerValue(); + } + } + return nDefaultOperationMethodEPSGCode; +} //! @endcond // --------------------------------------------------------------------------- @@ -6746,12 +6764,13 @@ TransformationNNPtr Transformation::createGeocentricTranslations( isGeog3D); return create( properties, sourceCRSIn, targetCRSIn, nullptr, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D)), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION), @@ -6804,11 +6823,12 @@ TransformationNNPtr Transformation::createPositionVector( isGeog3D); return createSevenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, accuracies); @@ -6853,11 +6873,12 @@ TransformationNNPtr Transformation::createCoordinateFrameRotation( isGeog3D); return createSevenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, accuracies); @@ -6996,12 +7017,13 @@ TransformationNNPtr Transformation::createTimeDependentPositionVector( isGeog3D); return createFifteenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, rateTranslationX, @@ -7073,12 +7095,13 @@ TransformationNNPtr Transformation::createTimeDependentCoordinateFrameRotation( isGeog3D); return createFifteenParamsTransform( properties, - createMethodMapNameEPSGCode( + createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( + properties, isGeocentric ? EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D - : EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D), + : EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, rateTranslationX, @@ -7616,6 +7639,14 @@ createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom, addModifiedIdentifier(map, op, true, derivedFrom); + const auto so = dynamic_cast(op); + if (so) { + const int soMethodEPSGCode = so->method()->getEPSGCode(); + if (soMethodEPSGCode > 0) { + map.set("OPERATION_METHOD_EPSG_CODE", soMethodEPSGCode); + } + } + return map; } diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 7d457dab..8df785b1 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -5523,6 +5523,49 @@ TEST(operation, projCRS_no_id_to_geogCRS_context) { // --------------------------------------------------------------------------- +TEST(operation, geogCRS_3D_to_projCRS_with_2D_geocentric_translation) { + + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto src = + authFactory->createCoordinateReferenceSystem("4979"); // WGS 84 3D + + // Azores Central 1948 / UTM zone 26N + auto dst = authFactory->createCoordinateReferenceSystem("2189"); + + auto list = + CoordinateOperationFactory::create()->createOperations(src, dst, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=push +v_3 " // this is what we check + "+step +proj=cart +ellps=WGS84 " + "+step +proj=helmert +x=104 +y=-167 +z=38 " + "+step +inv +proj=cart +ellps=intl " + "+step +proj=pop +v_3 " // this is what we check + "+step +proj=utm +zone=26 +ellps=intl"); + + auto listReverse = + CoordinateOperationFactory::create()->createOperations(dst, src, ctxt); + ASSERT_GE(listReverse.size(), 1U); + EXPECT_EQ( + listReverse[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=utm +zone=26 +ellps=intl " + "+step +proj=push +v_3 " // this is what we check + "+step +proj=cart +ellps=intl " + "+step +proj=helmert +x=-104 +y=167 +z=-38 " + "+step +inv +proj=cart +ellps=WGS84 " + "+step +proj=pop +v_3 " // this is what we check + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + TEST(operation, projCRS_to_projCRS) { auto op = CoordinateOperationFactory::create()->createOperation( -- cgit v1.2.3