Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+975
View File
@@ -0,0 +1,975 @@
/**
* @RDE_HEADER@
* adapting GeISiR source to SituationMap
* $Id$
* $HeadURL$
* $Author$
*/
#include "elevation_map.h"
#include <QtConcurrent>
#include <QPolygonF>
#include <MathUtils/math_const.h>
#include <QElapsedTimer>
#include <gdal.h>
#include <iostream>
#include <limits>
#include <memory>
#include <math.h>
namespace SituationMap
{
class BoundingRect
{
double m_top = 0.0;
double m_bottom = 0.0;
double m_left = 0.0;
double m_right = 0.0;
void swapBounds()
{
if (m_top < m_bottom)
std::swap(m_bottom, m_top);
if (m_right < m_left)
std::swap(m_left, m_right);
} // void swapBounds()
public:
BoundingRect() = default;
BoundingRect(double left, double bottom, double right, double top)
: m_top(top), m_bottom(bottom), m_left(left), m_right(right)
{
swapBounds();
} // BoundingRect( double left, double bottom, double right, double top)
BoundingRect(const BoundingRect &) = default;
BoundingRect &operator =(const BoundingRect &) = default;
~BoundingRect() = default;
inline void operator()(double left, double bottom, double right, double top)
{
m_top = top;
m_bottom = bottom;
m_left = left;
m_right = right;
swapBounds();
} // inline void operator() (double left, double bottom, double right, double top)
double top() const { return m_top; }
double bottom() const { return m_bottom; }
double left() const { return m_left; }
double right() const { return m_right; }
bool contains(const QPointF &p) const { return ((p.x() >= m_left) && (p.x() <= m_right) && (p.y() >= m_bottom) && (p.y() <= m_top)); }
QRectF toQRectF() const
{
return QRectF(QPointF(m_left, m_top), QPointF(m_right, m_bottom));
} // toQRectF
}; // BoundingRect
struct ElevationMap::Internal
{
BoundingRect _location; // Lage der Karte in Lat/Lon
struct PointDouble
{
void setX(double x) { _x = x;}
void setY(double y) { _y = y;}
double x() { return _x;}
double y() { return _y;}
double _x{0.0};
double _y{0.0};
};
PointDouble _elexelSize; // LatLon Size of 1 elevation sample, redundat
int _totalColumns = 0;
int _totalRows = 0;
double _toMeterFactor = 1.0;
std::vector<int> _elevationField;
template<class BufferType> bool copyBufferData(GDALRasterBandH hBand, int xBlkSize, int yBlkSize)
{
// NLDEVCTRL-804: Konsistenz d. Z-Werte zwischen 2D/3D (also ElevationMap vs. ESRI::BaseSurface)
// This ElevationMap was using an approach similar to following.
// Now it´s more or less identical and the difference using 'Altmark_dt1.tif'
// are gone. AHTC is also fine :-)
/*
CPLErr GetHistogram( GDALRasterBand *poBand, GUIntBig *panHistogram )
{
memset( panHistogram, 0, sizeof(GUIntBig) * 256 );
CPLAssert( poBand->GetRasterDataType() == GDT_Byte );
int nXBlockSize, nYBlockSize;
poBand->GetBlockSize( &nXBlockSize, &nYBlockSize );
int nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize;
int nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize;
GByte *pabyData = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize);
for( int iYBlock = 0; iYBlock < nYBlocks; iYBlock++ )
{
for( int iXBlock = 0; iXBlock < nXBlocks; iXBlock++ )
{
int nXValid, nYValid;
poBand->ReadBlock( iXBlock, iYBlock, pabyData );
// Compute the portion of the block that is valid
// for partial edge blocks.
poBand->GetActualBlockSize(iXBlock, iYBlock, &nXValid, &nYValid)
// Collect the histogram counts.
for( int iY = 0; iY < nYValid; iY++ )
{
for( int iX = 0; iX < nXValid; iX++ )
{
panHistogram[pabyData[iX + iY * nXBlockSize]] += 1;
}
}
}
}
}
Source https://gdal.org/api/gdalrasterband_cpp.html
*/
const size_t bufSize = xBlkSize * yBlkSize;
std::unique_ptr<BufferType[]> bufferPtr(new BufferType[bufSize]);
int nXBlocks = (GDALGetRasterBandXSize(hBand) + xBlkSize - 1) / xBlkSize;
int nYBlocks = (GDALGetRasterBandYSize(hBand) + yBlkSize - 1) / yBlkSize;
for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
{
for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
{
CPLErr err = GDALReadBlock( hBand, iXBlock, iYBlock, bufferPtr.get() ); // Gdal liest spaltenweise !! x <-> y
if (err != CE_None)
{
return false;
}
int nXValid, nYValid;
// Unfortunately GDALGetActualBlockSize is not available at Suse 42.3 (gdal 2.1.3)
// so check bounds using _totalColumns, _totalRows:
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2, 2, 0)
# define HAS_ACTUAL_BLOCKSIZE
#endif
#ifdef HAS_ACTUAL_BLOCKSIZE
GDALGetActualBlockSize(hBand, iXBlock, iYBlock, &nXValid, &nYValid);
#else
nXValid = xBlkSize;
nYValid = yBlkSize;
#endif // HAS_ACTUAL_BLOCKSIZE
const int curYOffset = (iYBlock * yBlkSize);
const int curXOffset = (iXBlock * xBlkSize);
for (int iY = 0; iY < nYValid; ++iY)
{
const int row = iY + curYOffset; // Line
#ifndef HAS_ACTUAL_BLOCKSIZE
if (row >= _totalRows)
continue;
#endif // HAS_ACTUAL_BLOCKSIZE
for (int iX = 0; iX < nXValid; ++iX)
{
// identical to gdallocationinfo
const int column = iX + curXOffset; // Pixel
#ifndef HAS_ACTUAL_BLOCKSIZE
if (column >= _totalColumns)
continue;
#endif // HAS_ACTUAL_BLOCKSIZE
int bi = iX + (iY * xBlkSize);
const int z = static_cast<int>( static_cast<double>(bufferPtr[bi]) / _toMeterFactor );
_elevationField[column + row * _totalColumns] = z;
} // for
} // for
} // for
} // for
return true;
} // template<class BufferType> void copyBufferData(BufferType * bufferPtr, int xBlkSize, int yBlkSize)
}; // struct ElevationMap::Internal
ElevationMap::ElevationMap(const QFileInfo &fileInfo)
: m_internal(new Internal())
, m_aproximation(true)
, m_valid(false)
, m_logger(getLogger("ElevationMap"))
{
GDALAllRegister();
m_fileName = fileInfo.absoluteFilePath();
INFO_LOG(m_logger) << " ctor ElevationMap: map of file '" << m_fileName << "'"
<< ", this=" << this;
if (!fileInfo.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' doesn't exists.";
return;
}
if (!fileInfo.isReadable())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' isn't readable.";
return;
}
auto cb = [this]()
{
return readFromFile();
};
auto cbFinished = [this]()
{
NOTICE_LOG(m_logger) << " Finished reading elevation map of file '" << m_fileName << "'"
<< ", this=" << this;
};
QObject::connect(&m_gdalReadWatcher, &QFutureWatcher<bool>::finished,
&m_gdalReadWatcher, cbFinished);
m_gdalReadWatcher.setFuture(QtConcurrent::run(cb));
// sync init, takes ~800ms during sitmap loading 72MB file
// QElapsedTimer t;
// t.start();
// m_gdalReadWatcher.waitForFinished();
// NOTICE_LOG(m_logger) << "Time loading elevation map: " << t.elapsed();
} // ElevationMap::ElevationMap( const QFile &file )
ElevationMap::~ElevationMap()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_gdalReadWatcher.cancel();
m_gdalReadWatcher.waitForFinished();
delete m_internal;
m_internal = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
} // ElevationMap::~ElevationMap
bool ElevationMap::waitForInitialization()
{
m_gdalReadWatcher.waitForFinished();
return m_valid;
}
bool ElevationMap::isValid() const
{
// N. Thoden, 27.04.2020
// isFinished seems to be unreliable:
// if mainThread request ::isValid() diretly after ::waitForInitialization()
// you get false (by 'if (!m_gdalReadWatcher.isFinished())'
// but m_valid is true. So skip the additional check.
// if (!m_gdalReadWatcher.isFinished())
// return false;
return m_valid;
}
QString ElevationMap::filename() const
{
return m_fileName;
}
bool ElevationMap::readFromFile()
{
m_valid = false;
GDALDatasetH hDataset = GDALOpen(m_fileName.toLocal8Bit().constData(), GA_ReadOnly);
if (nullptr == hDataset)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDALOpen failed, may be calling of GDALAllRegister is missing";
return false;
}
// Detect basic parameters
//
GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
int nRasterXSize = GDALGetRasterXSize(hDataset);
int nRasterYSize = GDALGetRasterYSize(hDataset);
double adfGeoTransform[6];
CPLErr err = GDALGetGeoTransform(hDataset, adfGeoTransform); // XY Spatial Referenz WGS84 (4326) expected!!!
if (err != CE_None)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to get geo raster transform.";
GDALClose(hDataset);
return false;
}
if (nullptr == hDriver) { GDALClose(hDataset); return false; }
if (nRasterXSize <= 0) { GDALClose(hDataset); return false; }
if (nRasterYSize <= 0) { GDALClose(hDataset); return false; }
double pixWidth = adfGeoTransform[1];
double pixHeight = adfGeoTransform[5];
auto toRAD = [](double x) -> double { return x * MathConst::pi / 180.0; };
m_internal->_location(toRAD(adfGeoTransform[0]) // adfGeoTransform is in degrees
, toRAD(adfGeoTransform[3])
, toRAD(adfGeoTransform[0] + double(nRasterXSize) * pixWidth + double(nRasterYSize) * adfGeoTransform[2])
, toRAD(adfGeoTransform[3] + double(nRasterYSize) * pixHeight + double(nRasterXSize) * adfGeoTransform[4]));
m_internal->_elexelSize.setX(fabs(toRAD(pixWidth)));
m_internal->_elexelSize.setY(fabs(toRAD(pixHeight)));
GDALRasterBandH hBand = GDALGetRasterBand(hDataset, 1);
if (nullptr == hBand)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to read raster band.";
GDALClose(hDataset);
return false;
}
int xBlkSize;
int yBlkSize;
GDALGetBlockSize(hBand, &xBlkSize, &yBlkSize);
if (xBlkSize <0 || yBlkSize < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no buffer, next operations will possible fail....";
return false;
}
m_internal->_totalColumns = nRasterXSize;
m_internal->_totalRows = nRasterYSize;
m_internal->_elevationField.resize(nRasterXSize * nRasterYSize);
const GDALDataType type = GDALGetRasterDataType(hBand);
switch (type)
{
case GDT_UInt16:
m_internal->_toMeterFactor = 1.0;
m_valid = m_internal->copyBufferData<unsigned short int> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Int16:
m_internal->_toMeterFactor = 1.0;
m_valid = m_internal->copyBufferData<short int> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Float32:
m_internal->_toMeterFactor = 0.01;
m_valid = m_internal->copyBufferData<float> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Float64:
m_internal->_toMeterFactor = 0.01;
m_valid = m_internal->copyBufferData<double> ( hBand, xBlkSize, yBlkSize );
break;
default:
{
ERROR_LOG(m_logger) << LOGFUNCTION << " data type isn't supported type or a floating point type"
<< " (type=" << type << ")";
GDALClose(hDataset);
return false;
}
} // switch
if (!m_valid)
{
CPLErrorNum errNum = CPLGetLastErrorNo();
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to read data block (error no " << int(errNum) << ")";
}
GDALClose(hDataset);
INFO_LOG(m_logger) << LOGFUNCTION << " loaded, filename =" << m_fileName << "'"
<< ", m_valid=" << m_valid;
return m_valid;
} // bool ElevationMap::readFromFile(const QString & fileName)
QRectF ElevationMap::extent() const
{
if (!isValid())
return QRectF();
return m_internal->_location.toQRectF();
}
QPair<double, double> ElevationMap::elexelSize() const
{
return {m_internal->_elexelSize._x, m_internal->_elexelSize._y};
}
int ElevationMap::getElevation(int column, int row) const
{
row = m_internal->_totalRows - row;
const size_t index = column + row * m_internal->_totalColumns;
if (m_internal->_elevationField.empty() || m_internal->_elevationField.size() < index)
return std::numeric_limits<int>::min();
return m_internal->_elevationField.at(index);
} // int ElevationMap::getElevation( int column, int row)
double ElevationMap::elevation(double lon, double lat) const
{
if (!isValid())
return 0;
return getElevation(lon, lat) * m_internal->_toMeterFactor;
} // double ElevationMap::elevation( double lon, double lat)
double ElevationMap:: getElevation(double lon, double lat) const
{
if (!m_internal->_location.contains(QPointF(lon, lat)))
{
return std::numeric_limits<int>::min(); // ausserhalb der Höhenkarte !!
}
double x = (lon - m_internal->_location.left()) / m_internal->_elexelSize.x();
int column = floor(x);
double y = (lat - m_internal->_location.bottom()) / m_internal->_elexelSize.y();
int row = floor(y);
int elevation = getElevation(column, row);
if (elevation == std::numeric_limits<int>::min())
return elevation;
if (!m_aproximation)
return elevation;
/*
** Approximation: e0, e1, e2, e3 Hoehenwerte der 4 benachbarten Stützpunkte
**
** + e3 + e2
** y
** ^ + er er = e0 + x*(e1-e0) + y*(e3-e0) + x*y(e2-e3+e0-e1)
** |
** |
** |
** | + e0 + e1
** -----> x
**
** Besonderheiten: die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Rasters,
** so dass je nach Groesse von x, y andere Nachbarpunkte zu berücksichtigen sind, und x, sowie y entsprechend
** angepasst werden muesen.
** Bei Randrasterpunkten kann keine Interpolation erfolgen, so dass hier entsprechend extrapoliert wird.
*/
x = (x - (double)column); // Residuum normiert auf Stützpunktabstand
y = (y - (double)row); // Residuum normiert auf Stützpunktabstand
x -= 0.5; // Mitte berücksichtigen
y -= 0.5; // Mitte berücksichtigen
if (x < 0.0) { column--; x += 1.0;} // linke Spalte zur Interpolation nutzen
if (y < 0.0) { row--; y += 1.0;} // untere Reihe zur Interpolation nutzen
// Randrasterpunkte berücksichtigen: extrapolieren statt interpolieren
if (column <= -1) { x = -x; column = 0;}
else if (column >= (m_internal->_totalColumns - 1)) { x += 1.0; column = m_internal->_totalColumns - 2;}
if (row == -1) { y = -y; row = 0;}
else if (row >= (m_internal->_totalRows - 1)) { y += 1.0; row = m_internal->_totalRows - 2;}
// Die Approximation:
double substitute = elevation;
double e0 = getOrSubstituteElevation(column, row, substitute);
double e1 = getOrSubstituteElevation(column + 1, row, substitute);
double e2 = getOrSubstituteElevation(column + 1, row + 1, substitute);
double e3 = getOrSubstituteElevation(column, row + 1, substitute);
double er = e0 + x * (e1 - e0) + y * (e3 - e0) + x * y * (e2 - e3 + e0 - e1);
return er; // floor(er+0.5);; runden herausgenommen 19.8.2008 JT
} // double ElevationMap:: getElevation( double lon, double lat)
double ElevationMap::getOrSubstituteElevation(int column, int row, double substitute) const
{
int elevation = getElevation(column, row);
if (elevation != std::numeric_limits<int>::min())
return elevation;
int substCount = 0;
double calcSubstitute = 0.0;
elevation = getElevation(column + 1, row);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column - 1, row);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column, row + 1);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column, row - 1);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
if (substCount)
substitute = calcSubstitute / substCount;
return substitute;
} // double ElevationMap::getOrSubstituteElevation( int column, int row, double substitute)
double ElevationMap::distance(const QPointF &start, const QPointF &end) const
{
if (!isValid())
return 0;
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
return -1.0; // ausserhalb{ der Höhenkarte !!
}
double start_z = getElevation(start.x(), start.y());
if (start_z <= std::numeric_limits<int>::min()) return -1.0;
double end_z = getElevation(end.x(), end.y());
if (end_z <= std::numeric_limits<int>::min()) return -1.0;
const QPointF &dist = distance_in_meter_WGS84(start, end);
double dz = end_z - start_z;
double d = sqrt(dist.x() * dist.x() + dist.y() * dist.y() + dz * dz) ;
return d;
} // double ElevationMap::distance( const QPointF &start, const QPointF &end )
double ElevationMap::profileDistance(const QPointF &start, const QPointF &end) const
{
if (!isValid())
return 0;
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
return -1.0; // ausserhalb der Höhenkarte !!
}
Vec3dVector elevProfile;
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
itr->setZ(getElevation(itr->x(), itr->y()));
// Einzellaengen aufsummieren, Stützstellen mit undefinierten Hoehen werden ausgelassen!
Point3D curPos(*elevProfile.begin());
double sumLng = 0.0;
for (Vec3dVector::iterator itr = elevProfile.begin() + 1; itr != elevProfile.end(); ++itr)
{
if ((*itr).z() > std::numeric_limits<int>::min())
{
if (curPos.z() > std::numeric_limits<int>::min())
{
const QPointF &dist = distance_in_meter_WGS84(QPointF(curPos.x(), curPos.y()), QPointF(itr->x(), itr->y()));
double dz = itr->z() - curPos.z();
sumLng += sqrt(dist.x() * dist.x() + dist.y() * dist.y() + dz * dz) ;
}
else
{
double dx = itr->x() - curPos.x();
double dy = itr->y() - curPos.y();
sumLng += sqrt(dx * dx + dy * dy);
}
curPos = *itr;
}
}
return sumLng;
} // double ElevationMap::profileDistance( const QPointF &start, const QPointF &end)
unsigned int ElevationMap::elevationProfile(const QPointF &start, const QPointF &end, Vec3dVector &elevProfile) const
{
unsigned int startSize = elevProfile.size();
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin() + startSize; itr != elevProfile.end(); ++itr)
itr->setZ(getElevation(itr->x(), itr->y()));
return (elevProfile.size() - startSize);
} // unsigned int ElevationMap::elevationProfile( const QPointF &start, const QPointF &end, Vec3dVector &elevProfile)
bool ElevationMap::contains(const QPointF &point) const
{
return m_internal->_location.contains(point);
}
bool ElevationMap::intersects(const QPolygonF &polygon) const
{
const BoundingRect &bbox = m_internal->_location;
// maybe QPointF uses float, but we can accept double to be reduced to float:
const QPolygonF &ourBorder = QPolygonF({QPointF(bbox.left() , bbox.bottom()),
QPointF(bbox.left() , bbox.top()),
QPointF(bbox.right(), bbox.top()),
QPointF(bbox.right(), bbox.bottom())});
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
return ourBorder.intersected(polygon).size() > 0;
#else
return ourBorder.intersects(polygon);
#endif
}
void ElevationMap::getSamplePoints(const QPointF &start, const QPointF &end, Vec3dVector &sPnts) const
{
sPnts.clear();
sPnts.push_back(Point3D::fromQPointF(start));
bool endIsClipped = false;
QPointF cStart(start);
QPointF cEnd(end);
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
int res = clipp2Map(start, end, cStart, cEnd);
if (res == 0) // no intersection
{
sPnts.push_back(Point3D::fromQPointF(end));
return;
}
if (res & 1) // start clipped
sPnts.push_back(Point3D::fromQPointF(cStart));
if (res & 2) // end clipped
endIsClipped = true;
}
QPointF delta(cEnd - cStart);
// Berechne alle Schnittpunkte des Vektors( cStart, cEnd) mit den x-Grenzen der Hoehenstuetzrechtecke.
// Die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Raster-Rechtecks
int startCol = floor((cStart.x() - m_internal->_location.left()) / m_internal->_elexelSize.x());
if (startCol < 0) startCol = 0;
else if (startCol >= m_internal->_totalColumns) startCol = m_internal->_totalColumns - 1;
int endCol = floor((cEnd.x() - m_internal->_location.left()) / m_internal->_elexelSize.x());
if (endCol < 0) endCol = 0;
else if (endCol >= m_internal->_totalColumns) endCol = m_internal->_totalColumns - 1;
int incCol = 0;
if (endCol > startCol) incCol = 1;
else if (endCol < startCol) incCol = -1;
double m = delta.y() / delta.x();
Vec2dVector xCuts;
xCuts.reserve(std::abs(endCol - startCol));
for (int col = startCol; col != endCol; col += incCol)
{
double x = m_internal->_location.left() + ((col + 0.5) + (incCol * 0.5)) * m_internal->_elexelSize.x();
double y = cStart.y() + (x - cStart.x()) * m;
xCuts.push_back(QPointF(x, y));
}
// Berechne alle Schnittpunkte des Vektors( cStart, cEnd) mit den y-Grenzen der Hoehenstuetzrechtecke.
// Die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Raster-Rechtecks
int startRow = floor((cStart.y() - m_internal->_location.bottom()) / m_internal->_elexelSize.y());
if (startRow < 0) startRow = 0;
else if (startRow >= m_internal->_totalRows) startRow = m_internal->_totalRows - 1;
int endRow = floor((end.y() - m_internal->_location.bottom()) / m_internal->_elexelSize.y());
if (endRow < 0) endRow = 0;
else if (endRow >= m_internal->_totalRows) endRow = m_internal->_totalRows - 1;
int incRow = 0;
if (endRow > startRow) incRow = 1;
else if (endRow < startRow) incRow = -1;
m = delta.x() / delta.y();
Vec2dVector yCuts;
yCuts.reserve(std::abs(endRow - startRow));
for (int row = startRow; row != endRow; row += incRow)
{
double y = m_internal->_location.bottom() + ((row + 0.5) + (incRow * 0.5)) * m_internal->_elexelSize.y();
double x = cStart.x() + (y - cStart.y()) * m;
yCuts.push_back(QPointF(x, y));
}
// xCuts und yCuts zusammenführen, unter Berücksichtigung der Reihenfolge
Vec2dVector mergeCuts;
Vec2dVector::iterator xCutItr = xCuts.begin();
Vec2dVector::iterator yCutItr = yCuts.begin();
while ((xCutItr != xCuts.end()) && (yCutItr != yCuts.end()))
{
if (incCol > 0)
{
if ((*xCutItr).x() < (*yCutItr).x()) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
else { mergeCuts.push_back(*yCutItr); ++yCutItr; }
}
else
{
if ((*xCutItr).x() > (*yCutItr).x()) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
else { mergeCuts.push_back(*yCutItr); ++yCutItr; }
}
}
while ((xCutItr != xCuts.end())) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
while ((yCutItr != yCuts.end())) { mergeCuts.push_back(*yCutItr); ++yCutItr; }
// mergeCuts enthält sequenziell alle Schnittpunkte des Vektors mit den Hoehengridkanten
// Die 2 Schnittpunkte pro Hoehenwert werden gemittelt zu einem zusammengefasst
if (mergeCuts.size() > 1)
{
for (Vec2dVector::iterator itr = mergeCuts.begin() + 1; itr != mergeCuts.end(); ++itr)
sPnts.push_back(Point3D::fromQPointF((*(itr - 1) + *itr) * 0.5));
}
sPnts.push_back(Point3D::fromQPointF(cEnd));
// DEBUG_LOG(m_logger) << " xCuts.si " << xCuts.size()
// << ", yCuts.si " << yCuts.size()
// << ", mergeCuts.si " << mergeCuts.size();
if (endIsClipped) sPnts.push_back(Point3D::fromQPointF(end));
} // void ElevationMap::getSamplePoints( const QPointF &start, const QPointF &end, Vec3dVector & sPnts)
// start und/oder end liegt ausserhalb !! in Lat/Lon !!
int ElevationMap::clipp2Map(const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd) const
{
Vec2dVector clippVec;
if (end.x() != start.x())
{
double x = m_internal->_location.right();
double m = (end.y() - start.y()) / (end.x() - start.x());
if (isInIntervall(x, start.x(), end.x()))
{
double yc = start.y() + (x - start.x()) * m;
if ((yc <= m_internal->_location.top()) && (yc >= m_internal->_location.bottom()))
clippVec.push_back(QPointF(x, yc));
}
x = m_internal->_location.left();
if (isInIntervall(x, start.x(), end.x()))
{
double yc = start.y() + (x - start.x()) * (end.y() - start.y()) / (end.x() - start.x());
if ((yc <= m_internal->_location.top()) && (yc >= m_internal->_location.bottom()))
clippVec.push_back(QPointF(x, yc));
}
}
if (end.y() != start.y())
{
double y = m_internal->_location.top();
double m = (end.x() - start.x()) / (end.y() - start.y());
if (isInIntervall(y, start.y(), end.y()))
{
double xc = start.x() + (y - start.y()) * m;
if ((xc < m_internal->_location.right()) && (xc > m_internal->_location.left()))
clippVec.push_back(QPointF(xc, y));
}
y = m_internal->_location.bottom();
if (isInIntervall(y, start.y(), end.y()))
{
double xc = start.x() + (y - start.y()) * m;
if ((xc < m_internal->_location.right()) && (xc > m_internal->_location.left()))
clippVec.push_back(QPointF(xc, y));
}
}
int size = (int)clippVec.size();
if (size == 0)
{
return 0; // komlett ausserhalb ( oder komplett innerhalb)
}
if (size == 1)
{
if (m_internal->_location.contains(start))
{
cEnd = clippVec[0]; return 2;
}
else
{
cStart = clippVec[0]; return 1;
}
}
if (size == 2)
{
if (squareLength(clippVec[0] - start) < squareLength(clippVec[1] - start))
{
cStart = clippVec[0];
cEnd = clippVec[1];
}
else
{
cStart = clippVec[1];
cEnd = clippVec[0];
}
return 3;
}
ERROR_LOG(m_logger) << LOGFUNCTION << "Internal Clipp Error!";
return 0;
} // int ElevationMap::clipp2Map( const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd)
// Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid
void ElevationMap::LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile)
{
m_gdalReadWatcher.waitForFinished();
if (!m_internal->_location.contains(start)) return ; // ausserhalb der Höhenkarte !!
bool clipped = false;
double totalLng;
QPointF startG = start;
QPointF endG = end;
if (!m_internal->_location.contains(end)) // Clippen
{
clipped = true;
totalLng = length(endG - startG);
if (endG.x() < m_internal->_location.left())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = m_internal->_location.left();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
else if (endG.x() > m_internal->_location.right())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = m_internal->_location.right();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
if (endG.y() < m_internal->_location.bottom())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = m_internal->_location.bottom();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
else if (endG.y() > m_internal->_location.top())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = m_internal->_location.top();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
}
// Korrektursteigung aufgrund WGS84 Erdellipsoid, Lineare Approximation, Fehler bis 200 km: < 1.0m
double startRadius = radiusWGS84(startG.y());
double corrM = (radiusWGS84(endG.y()) - startRadius) / length(endG - startG);
Vec3dVector elevProfile;
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
{
double dist = length(distance_in_meter_WGS84(start, itr->toPointF()));
double height = elevation(itr->x(), itr->y());
height += corrM * dist; // WGS84 Erdellipsoid Korrektur
height -= (startRadius * (1 - cos(atan(dist / startRadius)))); // Einbeziehen der Erdkruemmung
// alternativ als Naeherung: height -= ( dist * dist) / (startRadius * 2.0);
losProfile.push_back(LOSentry(dist, height));
}
if (clipped)
losProfile.push_back(LOSentry(totalLng, -NAN));
qCritical() << " losProfile.isze() = " << losProfile.size();
} // void ElevationMap::LOSProfile( const QPointF &start, const QPointF &end, LOSVector &losProfile)
} // SituationMap
/* -----------------------------------------------------------------------------------------------
** CVS-Id/Release ...: $Header$
** -----------------------------------------------------------------------------------------------
**
** Historie:
**
** $Log: not supported by cvs2svn $
** Revision 1.17.1.1 2008/06/16 06:05:37 joerg
** Transparenz und Bias einheitlich für alle Objekte implementiert
**
** Revision 1.17 2006/03/24 09:05:01 klaus
** Elevationprofile: Begrenzung auf Kartengröße aufgehoben ( Clipping)
**
** Revision 1.16 2005/11/09 13:44:36 klaus
** Umsetzung auf Linux;
**
** Revision 1.15 2005/04/15 13:05:45 joerg
** Resource Control
**
** Revision 1.14 2005/03/04 14:29:26 klaus
** Elevationmap mit CachedTileList versehen und automatischem Entladen von Tiles
**
** Revision 1.13 2005/03/03 12:37:53 klaus
** dynamischen Laden und Entladen der ElevationTiles für Reliefmap
**
** Revision 1.12 2005/03/01 12:36:59 klaus
** Reliefmap eingeführt, aus Elevationmap entfernt
**
** Revision 1.11 2005/02/25 15:32:56 klaus
** Ergänzung um Reliefkarte
**
** Revision 1.10 2005/02/10 14:15:19 klaus
** Bias in Basisklasse MapObject überführt
**
** Revision 1.9 2005/02/10 13:50:33 klaus
** Bias für SightFans eingeführt
**
** Revision 1.8 2005/02/09 16:45:26 klaus
** 1. Version der SightFans
**
** Revision 1.7 2005/01/26 12:26:23 klaus
** todos bearbeitet, Fehlerrückmeldnugen
**
** Revision 1.6 2004/12/14 10:29:48 klaus
** 7. Erweiterung für Höhenkarten
**
** Revision 1.5 2004/12/07 11:08:28 klaus
** 5. Erweiterung für Höhenkarten
**
** Revision 1.4 2004/12/03 16:25:14 klaus
** 4. Erweiterung für Höhenkarten
**
** Revision 1.3 2004/11/30 13:18:13 klaus
** 3. Erweiterung für Höhenkarten
**
** Revision 1.2 2004/11/23 14:36:40 klaus
** 2. Erweiterung für Höhenkarten
**
**
** ----------------------------------------------------------------------------------------------- */