Skip to content

Commit 00bc255

Browse files
authored
Merge pull request OSGeo#11700 from rouault/fix_GeodesicLength
Fix GeodesicLength() that was quite severely broken as working only on closed linestrings (3.10.0 regression)
2 parents 3984098 + aa5fde1 commit 00bc255

File tree

2 files changed

+49
-15
lines changed

2 files changed

+49
-15
lines changed

autotest/ogr/ogr_geom.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -4559,12 +4559,34 @@ def test_ogr_geom_GeodesicArea():
45594559
@gdaltest.enable_exceptions()
45604560
def test_ogr_geom_GeodesicLength():
45614561

4562+
# Lat, lon order, not forming a polygon
4563+
g = ogr.CreateGeometryFromWkt("LINESTRING(49 2,49 3)")
4564+
srs = osr.SpatialReference()
4565+
srs.ImportFromEPSG(4326)
4566+
g.AssignSpatialReference(srs)
4567+
l1 = g.GeodesicLength()
4568+
assert l1 == pytest.approx(73171.26435678436)
4569+
4570+
g = ogr.CreateGeometryFromWkt("LINESTRING(49 3,48 3)")
4571+
srs = osr.SpatialReference()
4572+
srs.ImportFromEPSG(4326)
4573+
g.AssignSpatialReference(srs)
4574+
l2 = g.GeodesicLength()
4575+
assert l2 == pytest.approx(111200.0367623785)
4576+
4577+
g = ogr.CreateGeometryFromWkt("LINESTRING(48 3,49 2)")
4578+
srs = osr.SpatialReference()
4579+
srs.ImportFromEPSG(4326)
4580+
g.AssignSpatialReference(srs)
4581+
l3 = g.GeodesicLength()
4582+
assert l3 == pytest.approx(133514.4852804854)
4583+
45624584
# Lat, lon order
45634585
g = ogr.CreateGeometryFromWkt("LINESTRING(49 2,49 3,48 3,49 2)")
45644586
srs = osr.SpatialReference()
45654587
srs.ImportFromEPSG(4326)
45664588
g.AssignSpatialReference(srs)
4567-
assert g.GeodesicLength() == pytest.approx(317885.78639964823)
4589+
assert g.GeodesicLength() == pytest.approx(l1 + l2 + l3)
45684590

45694591
# Lat, lon order
45704592
g = ogr.CreateGeometryFromWkt("POLYGON((49 2,49 3,48 3,49 2))")

ogr/ogrlinestring.cpp

+26-14
Original file line numberDiff line numberDiff line change
@@ -3124,12 +3124,14 @@ double OGRLineString::get_Area() const
31243124
}
31253125

31263126
/************************************************************************/
3127-
/* GetGeodesicAreaOrLength() */
3127+
/* GetGeodesicInputs() */
31283128
/************************************************************************/
31293129

3130-
static bool GetGeodesicAreaOrLength(const OGRLineString *poLS,
3131-
const OGRSpatialReference *poSRSOverride,
3132-
double *pdfArea, double *pdfLength)
3130+
static bool GetGeodesicInputs(const OGRLineString *poLS,
3131+
const OGRSpatialReference *poSRSOverride,
3132+
const char *pszComputationType, geod_geodesic &g,
3133+
std::vector<double> &adfLat,
3134+
std::vector<double> &adfLon)
31333135
{
31343136
if (!poSRSOverride)
31353137
poSRSOverride = poLS->getSpatialReference();
@@ -3138,7 +3140,7 @@ static bool GetGeodesicAreaOrLength(const OGRLineString *poLS,
31383140
{
31393141
CPLError(CE_Failure, CPLE_AppDefined,
31403142
"Cannot compute %s on ellipsoid due to missing SRS",
3141-
pdfArea ? "area" : "length");
3143+
pszComputationType);
31423144
return false;
31433145
}
31443146

@@ -3150,12 +3152,9 @@ static bool GetGeodesicAreaOrLength(const OGRLineString *poLS,
31503152
if (eErr != OGRERR_NONE)
31513153
return false;
31523154

3153-
geod_geodesic g;
31543155
geod_init(&g, dfSemiMajor,
31553156
dfInvFlattening != 0 ? 1.0 / dfInvFlattening : 0.0);
31563157

3157-
std::vector<double> adfLat;
3158-
std::vector<double> adfLon;
31593158
const int nPointCount = poLS->getNumPoints();
31603159
adfLat.reserve(nPointCount);
31613160
adfLon.reserve(nPointCount);
@@ -3208,8 +3207,6 @@ static bool GetGeodesicAreaOrLength(const OGRLineString *poLS,
32083207
adfLat[i] *= dfToDegrees;
32093208
}
32103209

3211-
geod_polygonarea(&g, adfLat.data(), adfLon.data(),
3212-
static_cast<int>(adfLat.size()), pdfArea, pdfLength);
32133210
return true;
32143211
}
32153212

@@ -3220,9 +3217,14 @@ static bool GetGeodesicAreaOrLength(const OGRLineString *poLS,
32203217
double
32213218
OGRLineString::get_GeodesicArea(const OGRSpatialReference *poSRSOverride) const
32223219
{
3223-
double dfArea = 0;
3224-
if (!GetGeodesicAreaOrLength(this, poSRSOverride, &dfArea, nullptr))
3220+
geod_geodesic g;
3221+
std::vector<double> adfLat;
3222+
std::vector<double> adfLon;
3223+
if (!GetGeodesicInputs(this, poSRSOverride, "area", g, adfLat, adfLon))
32253224
return -1.0;
3225+
double dfArea = -1.0;
3226+
geod_polygonarea(&g, adfLat.data(), adfLon.data(),
3227+
static_cast<int>(adfLat.size()), &dfArea, nullptr);
32263228
return std::fabs(dfArea);
32273229
}
32283230

@@ -3233,9 +3235,19 @@ OGRLineString::get_GeodesicArea(const OGRSpatialReference *poSRSOverride) const
32333235
double OGRLineString::get_GeodesicLength(
32343236
const OGRSpatialReference *poSRSOverride) const
32353237
{
3238+
geod_geodesic g;
3239+
std::vector<double> adfLat;
3240+
std::vector<double> adfLon;
3241+
if (!GetGeodesicInputs(this, poSRSOverride, "length", g, adfLat, adfLon))
3242+
return -1.0;
32363243
double dfLength = 0;
3237-
if (!GetGeodesicAreaOrLength(this, poSRSOverride, nullptr, &dfLength))
3238-
return -1;
3244+
for (size_t i = 0; i + 1 < adfLon.size(); ++i)
3245+
{
3246+
double dfSegmentLength = 0;
3247+
geod_inverse(&g, adfLat[i], adfLon[i], adfLat[i + 1], adfLon[i + 1],
3248+
&dfSegmentLength, nullptr, nullptr);
3249+
dfLength += dfSegmentLength;
3250+
}
32393251
return dfLength;
32403252
}
32413253

0 commit comments

Comments
 (0)