@@ -245,8 +245,24 @@ int pj_get_suggested_operation(PJ_CONTEXT *,
245
245
const auto &alt = opList[i];
246
246
bool spatialCriterionOK = false ;
247
247
if (direction == PJ_FWD) {
248
- if (coord.xyzt .x >= alt.minxSrc && coord.xyzt .y >= alt.minySrc &&
249
- coord.xyzt .x <= alt.maxxSrc && coord.xyzt .y <= alt.maxySrc ) {
248
+ if (alt.pjSrcGeocentricToLonLat ) {
249
+ if (alt.minxSrc == -180 && alt.minySrc == -90 &&
250
+ alt.maxxSrc == 180 && alt.maxySrc == 90 ) {
251
+ spatialCriterionOK = true ;
252
+ } else {
253
+ PJ_COORD tmp = coord;
254
+ pj_fwd4d (tmp, alt.pjSrcGeocentricToLonLat );
255
+ if (tmp.xyzt .x >= alt.minxSrc &&
256
+ tmp.xyzt .y >= alt.minySrc &&
257
+ tmp.xyzt .x <= alt.maxxSrc &&
258
+ tmp.xyzt .y <= alt.maxySrc ) {
259
+ spatialCriterionOK = true ;
260
+ }
261
+ }
262
+ } else if (coord.xyzt .x >= alt.minxSrc &&
263
+ coord.xyzt .y >= alt.minySrc &&
264
+ coord.xyzt .x <= alt.maxxSrc &&
265
+ coord.xyzt .y <= alt.maxySrc ) {
250
266
spatialCriterionOK = true ;
251
267
} else if (alt.srcIsLonLatDegree && coord.xyzt .y >= alt.minySrc &&
252
268
coord.xyzt .y <= alt.maxySrc ) {
@@ -264,8 +280,24 @@ int pj_get_suggested_operation(PJ_CONTEXT *,
264
280
}
265
281
}
266
282
} else {
267
- if (coord.xyzt .x >= alt.minxDst && coord.xyzt .y >= alt.minyDst &&
268
- coord.xyzt .x <= alt.maxxDst && coord.xyzt .y <= alt.maxyDst ) {
283
+ if (alt.pjDstGeocentricToLonLat ) {
284
+ if (alt.minxDst == -180 && alt.minyDst == -90 &&
285
+ alt.maxxDst == 180 && alt.maxyDst == 90 ) {
286
+ spatialCriterionOK = true ;
287
+ } else {
288
+ PJ_COORD tmp = coord;
289
+ pj_fwd4d (tmp, alt.pjDstGeocentricToLonLat );
290
+ if (tmp.xyzt .x >= alt.minxDst &&
291
+ tmp.xyzt .y >= alt.minyDst &&
292
+ tmp.xyzt .x <= alt.maxxDst &&
293
+ tmp.xyzt .y <= alt.maxyDst ) {
294
+ spatialCriterionOK = true ;
295
+ }
296
+ }
297
+ } else if (coord.xyzt .x >= alt.minxDst &&
298
+ coord.xyzt .y >= alt.minyDst &&
299
+ coord.xyzt .x <= alt.maxxDst &&
300
+ coord.xyzt .y <= alt.maxyDst ) {
269
301
spatialCriterionOK = true ;
270
302
} else if (alt.dstIsLonLatDegree && coord.xyzt .y >= alt.minyDst &&
271
303
coord.xyzt .y <= alt.maxyDst ) {
@@ -320,6 +352,14 @@ int pj_get_suggested_operation(PJ_CONTEXT *,
320
352
}
321
353
322
354
// ! @cond Doxygen_Suppress
355
+ /* *************************************************************************************/
356
+ PJCoordOperation::~PJCoordOperation () {
357
+ /* *************************************************************************************/
358
+ proj_destroy (pj);
359
+ proj_destroy (pjSrcGeocentricToLonLat);
360
+ proj_destroy (pjDstGeocentricToLonLat);
361
+ }
362
+
323
363
/* *************************************************************************************/
324
364
bool PJCoordOperation::isInstantiable () const {
325
365
/* *************************************************************************************/
@@ -1659,11 +1699,11 @@ static void reproject_bbox(PJ *pjGeogToCrs, double west_lon, double south_lat,
1659
1699
}
1660
1700
1661
1701
/* ****************************************************************************/
1662
- static PJ *add_coord_op_to_list (int idxInOriginalList, PJ *op, double west_lon,
1663
- double south_lat , double east_lon ,
1664
- double north_lat, PJ *pjGeogToSrc,
1665
- PJ *pjGeogToDst, bool isOffshore ,
1666
- std::vector<PJCoordOperation> &altCoordOps) {
1702
+ static PJ *add_coord_op_to_list (
1703
+ int idxInOriginalList, PJ *op, double west_lon , double south_lat ,
1704
+ double east_lon, double north_lat, PJ *pjGeogToSrc, PJ *pjGeogToDst ,
1705
+ const PJ *pjSrcGeocentricToLonLat, const PJ *pjDstGeocentricToLonLat ,
1706
+ bool isOffshore, std::vector<PJCoordOperation> &altCoordOps) {
1667
1707
/* ****************************************************************************/
1668
1708
1669
1709
double minxSrc;
@@ -1675,19 +1715,35 @@ static PJ *add_coord_op_to_list(int idxInOriginalList, PJ *op, double west_lon,
1675
1715
double maxxDst;
1676
1716
double maxyDst;
1677
1717
1678
- reproject_bbox (pjGeogToSrc, west_lon, south_lat, east_lon, north_lat,
1679
- minxSrc, minySrc, maxxSrc, maxySrc);
1680
- reproject_bbox (pjGeogToDst, west_lon, south_lat, east_lon, north_lat,
1681
- minxDst, minyDst, maxxDst, maxyDst);
1718
+ if (pjSrcGeocentricToLonLat) {
1719
+ minxSrc = west_lon;
1720
+ minySrc = south_lat;
1721
+ maxxSrc = east_lon;
1722
+ maxySrc = north_lat;
1723
+ } else {
1724
+ reproject_bbox (pjGeogToSrc, west_lon, south_lat, east_lon, north_lat,
1725
+ minxSrc, minySrc, maxxSrc, maxySrc);
1726
+ }
1727
+
1728
+ if (pjDstGeocentricToLonLat) {
1729
+ minxDst = west_lon;
1730
+ minyDst = south_lat;
1731
+ maxxDst = east_lon;
1732
+ maxyDst = north_lat;
1733
+ } else {
1734
+ reproject_bbox (pjGeogToDst, west_lon, south_lat, east_lon, north_lat,
1735
+ minxDst, minyDst, maxxDst, maxyDst);
1736
+ }
1682
1737
1683
1738
if (minxSrc <= maxxSrc && minxDst <= maxxDst) {
1684
1739
const char *c_name = proj_get_name (op);
1685
1740
std::string name (c_name ? c_name : " " );
1686
1741
1687
1742
const double accuracy = proj_coordoperation_get_accuracy (op->ctx , op);
1688
- altCoordOps.emplace_back (idxInOriginalList, minxSrc, minySrc, maxxSrc,
1689
- maxySrc, minxDst, minyDst, maxxDst, maxyDst,
1690
- op, name, accuracy, isOffshore);
1743
+ altCoordOps.emplace_back (
1744
+ idxInOriginalList, minxSrc, minySrc, maxxSrc, maxySrc, minxDst,
1745
+ minyDst, maxxDst, maxyDst, op, name, accuracy, isOffshore,
1746
+ pjSrcGeocentricToLonLat, pjDstGeocentricToLonLat);
1691
1747
op = nullptr ;
1692
1748
}
1693
1749
return op;
@@ -1798,6 +1854,57 @@ static PJ *create_operation_to_geog_crs(PJ_CONTEXT *ctx, const PJ *crs) {
1798
1854
return opGeogToCrs;
1799
1855
}
1800
1856
1857
+ /* ****************************************************************************/
1858
+ static PJ *create_operation_geocentric_crs_to_geog_crs (PJ_CONTEXT *ctx,
1859
+ const PJ *geocentric_crs)
1860
+ /* ****************************************************************************/
1861
+ {
1862
+ assert (proj_get_type (geocentric_crs) == PJ_TYPE_GEOCENTRIC_CRS);
1863
+
1864
+ auto datum = proj_crs_get_datum_forced (ctx, geocentric_crs);
1865
+ assert (datum);
1866
+ auto cs = proj_create_ellipsoidal_2D_cs (ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE,
1867
+ nullptr , 0 );
1868
+ auto ellps = proj_get_ellipsoid (ctx, datum);
1869
+ proj_destroy (datum);
1870
+ double semi_major_metre = 0 ;
1871
+ double inv_flattening = 0 ;
1872
+ proj_ellipsoid_get_parameters (ctx, ellps, &semi_major_metre, nullptr ,
1873
+ nullptr , &inv_flattening);
1874
+ // It is critical to set the prime meridian to 0
1875
+ auto lon_lat_crs = proj_create_geographic_crs (
1876
+ ctx, " unnamed crs" , " unnamed datum" , proj_get_name (ellps),
1877
+ semi_major_metre, inv_flattening, " Reference prime meridian" , 0 ,
1878
+ nullptr , 0 , cs);
1879
+ proj_destroy (ellps);
1880
+ proj_destroy (cs);
1881
+
1882
+ // Create the transformation from this geocentric CRS to the lon-lat one
1883
+ auto operation_ctx = proj_create_operation_factory_context (ctx, nullptr );
1884
+ proj_operation_factory_context_set_spatial_criterion (
1885
+ ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION);
1886
+ proj_operation_factory_context_set_grid_availability_use (
1887
+ ctx, operation_ctx,
1888
+ PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
1889
+ auto op_list =
1890
+ proj_create_operations (ctx, geocentric_crs, lon_lat_crs, operation_ctx);
1891
+ proj_operation_factory_context_destroy (operation_ctx);
1892
+ proj_destroy (lon_lat_crs);
1893
+
1894
+ const int nOpCount = op_list == nullptr ? 0 : proj_list_get_count (op_list);
1895
+ if (nOpCount != 1 ) {
1896
+ proj_context_log_debug (ctx, " Cannot compute transformation from "
1897
+ " geocentric CRS to geographic CRS" );
1898
+ proj_list_destroy (op_list);
1899
+ return nullptr ;
1900
+ }
1901
+
1902
+ auto op = proj_list_get (ctx, op_list, 0 );
1903
+ assert (op);
1904
+ proj_list_destroy (op_list);
1905
+ return op;
1906
+ }
1907
+
1801
1908
/* ****************************************************************************/
1802
1909
PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs,
1803
1910
const char *target_crs, PJ_AREA *area) {
@@ -1846,21 +1953,46 @@ pj_create_prepared_operations(PJ_CONTEXT *ctx, const PJ *source_crs,
1846
1953
const PJ *target_crs, PJ_OBJ_LIST *op_list)
1847
1954
/* ****************************************************************************/
1848
1955
{
1849
- auto pjGeogToSrc = create_operation_to_geog_crs (ctx, source_crs);
1850
- if (!pjGeogToSrc) {
1851
- proj_context_log_debug (ctx,
1852
- " Cannot create transformation from geographic "
1853
- " CRS of source CRS to source CRS" );
1854
- return {};
1956
+ PJ *pjGeogToSrc = nullptr ;
1957
+ PJ *pjSrcGeocentricToLonLat = nullptr ;
1958
+ if (proj_get_type (source_crs) == PJ_TYPE_GEOCENTRIC_CRS) {
1959
+ pjSrcGeocentricToLonLat =
1960
+ create_operation_geocentric_crs_to_geog_crs (ctx, source_crs);
1961
+ if (!pjSrcGeocentricToLonLat) {
1962
+ // shouldn't happen
1963
+ return {};
1964
+ }
1965
+ } else {
1966
+ pjGeogToSrc = create_operation_to_geog_crs (ctx, source_crs);
1967
+ if (!pjGeogToSrc) {
1968
+ proj_context_log_debug (
1969
+ ctx, " Cannot create transformation from geographic "
1970
+ " CRS of source CRS to source CRS" );
1971
+ return {};
1972
+ }
1855
1973
}
1856
1974
1857
- auto pjGeogToDst = create_operation_to_geog_crs (ctx, target_crs);
1858
- if (!pjGeogToDst) {
1859
- proj_context_log_debug (ctx,
1860
- " Cannot create transformation from geographic "
1861
- " CRS of target CRS to target CRS" );
1862
- proj_destroy (pjGeogToSrc);
1863
- return {};
1975
+ PJ *pjGeogToDst = nullptr ;
1976
+ PJ *pjDstGeocentricToLonLat = nullptr ;
1977
+ if (proj_get_type (target_crs) == PJ_TYPE_GEOCENTRIC_CRS) {
1978
+ pjDstGeocentricToLonLat =
1979
+ create_operation_geocentric_crs_to_geog_crs (ctx, target_crs);
1980
+ if (!pjDstGeocentricToLonLat) {
1981
+ // shouldn't happen
1982
+ proj_destroy (pjSrcGeocentricToLonLat);
1983
+ proj_destroy (pjGeogToSrc);
1984
+ return {};
1985
+ }
1986
+ } else {
1987
+ pjGeogToDst = create_operation_to_geog_crs (ctx, target_crs);
1988
+ if (!pjGeogToDst) {
1989
+ proj_context_log_debug (
1990
+ ctx, " Cannot create transformation from geographic "
1991
+ " CRS of target CRS to target CRS" );
1992
+ proj_destroy (pjSrcGeocentricToLonLat);
1993
+ proj_destroy (pjGeogToSrc);
1994
+ return {};
1995
+ }
1864
1996
}
1865
1997
1866
1998
try {
@@ -1888,18 +2020,21 @@ pj_create_prepared_operations(PJ_CONTEXT *ctx, const PJ *source_crs,
1888
2020
1889
2021
const bool isOffshore = areaName && strstr (areaName, " - offshore" );
1890
2022
if (west_lon <= east_lon) {
1891
- op = add_coord_op_to_list (i, op, west_lon, south_lat, east_lon,
1892
- north_lat, pjGeogToSrc, pjGeogToDst,
1893
- isOffshore, preparedOpList);
2023
+ op = add_coord_op_to_list (
2024
+ i, op, west_lon, south_lat, east_lon, north_lat,
2025
+ pjGeogToSrc, pjGeogToDst, pjSrcGeocentricToLonLat,
2026
+ pjDstGeocentricToLonLat, isOffshore, preparedOpList);
1894
2027
} else {
1895
2028
auto op_clone = proj_clone (ctx, op);
1896
2029
1897
- op = add_coord_op_to_list (i, op, west_lon, south_lat, 180 ,
1898
- north_lat, pjGeogToSrc, pjGeogToDst,
1899
- isOffshore, preparedOpList);
2030
+ op = add_coord_op_to_list (
2031
+ i, op, west_lon, south_lat, 180 , north_lat, pjGeogToSrc,
2032
+ pjGeogToDst, pjSrcGeocentricToLonLat,
2033
+ pjDstGeocentricToLonLat, isOffshore, preparedOpList);
1900
2034
op_clone = add_coord_op_to_list (
1901
2035
i, op_clone, -180 , south_lat, east_lon, north_lat,
1902
- pjGeogToSrc, pjGeogToDst, isOffshore, preparedOpList);
2036
+ pjGeogToSrc, pjGeogToDst, pjSrcGeocentricToLonLat,
2037
+ pjDstGeocentricToLonLat, isOffshore, preparedOpList);
1903
2038
proj_destroy (op_clone);
1904
2039
}
1905
2040
@@ -1908,10 +2043,14 @@ pj_create_prepared_operations(PJ_CONTEXT *ctx, const PJ *source_crs,
1908
2043
1909
2044
proj_destroy (pjGeogToSrc);
1910
2045
proj_destroy (pjGeogToDst);
2046
+ proj_destroy (pjSrcGeocentricToLonLat);
2047
+ proj_destroy (pjDstGeocentricToLonLat);
1911
2048
return preparedOpList;
1912
2049
} catch (const std::exception &) {
1913
2050
proj_destroy (pjGeogToSrc);
1914
2051
proj_destroy (pjGeogToDst);
2052
+ proj_destroy (pjSrcGeocentricToLonLat);
2053
+ proj_destroy (pjDstGeocentricToLonLat);
1915
2054
return {};
1916
2055
}
1917
2056
}
@@ -2070,13 +2209,10 @@ PJ *proj_create_crs_to_crs_from_pj(PJ_CONTEXT *ctx, const PJ *source_crs,
2070
2209
}
2071
2210
2072
2211
const auto backup_errno = proj_context_errno (ctx);
2073
- if ((P == nullptr ||
2074
- (op_count == 1 &&
2075
- (!mayNeedToReRunWithDiscardMissing ||
2076
- errorIfBestTransformationNotAvailable ||
2077
- singleOpIsInstanciable == static_cast <int >(true ))) ||
2078
- proj_get_type (source_crs) == PJ_TYPE_GEOCENTRIC_CRS ||
2079
- proj_get_type (target_crs) == PJ_TYPE_GEOCENTRIC_CRS)) {
2212
+ if (P == nullptr ||
2213
+ (op_count == 1 && (!mayNeedToReRunWithDiscardMissing ||
2214
+ errorIfBestTransformationNotAvailable ||
2215
+ singleOpIsInstanciable == static_cast <int >(true )))) {
2080
2216
proj_list_destroy (op_list);
2081
2217
ctx->forceOver = false ;
2082
2218
@@ -2835,21 +2971,28 @@ static bool isSpecialCaseForWGS84_to_GDA2020(const std::string &opName) {
2835
2971
return opName.find (" GDA2020 to WGS 84 (2)" ) != std::string::npos;
2836
2972
}
2837
2973
2838
- PJCoordOperation::PJCoordOperation (int idxInOriginalListIn, double minxSrcIn,
2839
- double minySrcIn, double maxxSrcIn,
2840
- double maxySrcIn, double minxDstIn,
2841
- double minyDstIn, double maxxDstIn,
2842
- double maxyDstIn, PJ *pjIn,
2843
- const std::string &nameIn, double accuracyIn,
2844
- bool isOffshoreIn)
2974
+ PJCoordOperation::PJCoordOperation (
2975
+ int idxInOriginalListIn, double minxSrcIn, double minySrcIn,
2976
+ double maxxSrcIn, double maxySrcIn, double minxDstIn, double minyDstIn,
2977
+ double maxxDstIn, double maxyDstIn, PJ *pjIn, const std::string &nameIn,
2978
+ double accuracyIn, bool isOffshoreIn, const PJ *pjSrcGeocentricToLonLatIn,
2979
+ const PJ *pjDstGeocentricToLonLatIn)
2845
2980
: idxInOriginalList(idxInOriginalListIn), minxSrc(minxSrcIn),
2846
2981
minySrc(minySrcIn), maxxSrc(maxxSrcIn), maxySrc(maxySrcIn),
2847
2982
minxDst(minxDstIn), minyDst(minyDstIn), maxxDst(maxxDstIn),
2848
2983
maxyDst(maxyDstIn), pj(pjIn), name(nameIn), accuracy(accuracyIn),
2849
2984
isOffshore(isOffshoreIn),
2850
2985
isPriorityOp(isSpecialCaseForNAD83_to_NAD83HARN(name) ||
2851
2986
isSpecialCaseForGDA94_to_WGS84(name) ||
2852
- isSpecialCaseForWGS84_to_GDA2020(name)) {
2987
+ isSpecialCaseForWGS84_to_GDA2020(name)),
2988
+ pjSrcGeocentricToLonLat(pjSrcGeocentricToLonLatIn
2989
+ ? proj_clone(pjSrcGeocentricToLonLatIn->ctx,
2990
+ pjSrcGeocentricToLonLatIn)
2991
+ : nullptr),
2992
+ pjDstGeocentricToLonLat(pjDstGeocentricToLonLatIn
2993
+ ? proj_clone(pjDstGeocentricToLonLatIn->ctx,
2994
+ pjDstGeocentricToLonLatIn)
2995
+ : nullptr) {
2853
2996
const auto IsLonLatOrLatLon = [](const PJ *crs, bool &isLonLatDegreeOut,
2854
2997
bool &isLatLonDegreeOut) {
2855
2998
const auto eType = proj_get_type (crs);
0 commit comments