Compare commits
58 Commits
13.18
...
27401d58b7
Author | SHA1 | Date | |
---|---|---|---|
27401d58b7 | |||
da2b6661f6 | |||
de76eafdfb | |||
19cda6fbd5 | |||
6803ee0324 | |||
43271d9ff8 | |||
6bd83780cd | |||
c4d07b5f12 | |||
b28217a026 | |||
1af2c130b0 | |||
ca0d859c6d | |||
947d2d62b3 | |||
11677f5e35 | |||
6ef6644260 | |||
f6f9e4146d | |||
d1401bc302 | |||
488e5e1cac | |||
ff4f3eea60 | |||
d46ac8435e | |||
15fbd6d35e | |||
7de180d580 | |||
bd37521ca0 | |||
4445976cb9 | |||
76b14c23c6 | |||
3b4376cc03 | |||
41b1ec3605 | |||
4476998333 | |||
5940a2ced4 | |||
33c45f845a | |||
6f12f91cb1 | |||
55de85579c | |||
86f98aca42 | |||
56425a3318 | |||
d045fed086 | |||
31cd65f15e | |||
089ccfde71 | |||
a0ce50e7e4 | |||
031d82f689 | |||
99f620f101 | |||
5f3203d638 | |||
33cc1df601 | |||
5ef29bf827 | |||
6f029d81a0 | |||
90395a32dd | |||
d4b10b091c | |||
6210d1b983 | |||
5e71715f22 | |||
648f90f230 | |||
324168340b | |||
3e1bddbcfd | |||
b069d2ac3f | |||
9b46845568 | |||
f814d1f5a3 | |||
0f05dd6ba3 | |||
8a069f113e | |||
bfe31023e0 | |||
779f98a206 | |||
a660cbd463 |
@ -1,4 +1,4 @@
|
||||
version: 13.18.{build}
|
||||
version: 13.20.{build}
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
@ -12,8 +12,8 @@ environment:
|
||||
- QTDIR: C:\Qt\5.15\msvc2019_64
|
||||
OPENSSLDIR: C:\OpenSSL-v111-Win64\bin
|
||||
NSISDEF: /DOPENSSL /DANGLE
|
||||
- QTDIR: C:\Qt\6.5\msvc2019_64
|
||||
OPENSSLDIR: C:\OpenSSL-v30-Win64\bin
|
||||
- QTDIR: C:\Qt\6.7\msvc2019_64
|
||||
OPENSSLDIR: C:\OpenSSL-v32-Win64\bin
|
||||
NSISDEF: /DQT6 /DOPENSSL
|
||||
|
||||
install:
|
||||
|
4
.github/workflows/osx.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
qt5:
|
||||
name: GPXSee Qt5 build
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: '6.6.1'
|
||||
version: '6.7.0'
|
||||
modules: qtpositioning qtserialport qtimageformats
|
||||
- name: Create localization
|
||||
run: lrelease gpxsee.pro
|
||||
|
11
gpxsee.pro
@ -3,7 +3,7 @@ unix:!macx:!android {
|
||||
} else {
|
||||
TARGET = GPXSee
|
||||
}
|
||||
VERSION = 13.18
|
||||
VERSION = 13.20
|
||||
|
||||
|
||||
QT += core \
|
||||
@ -128,6 +128,10 @@ HEADERS += src/common/config.h \
|
||||
src/map/ENC/objects.h \
|
||||
src/map/ENC/rastertile.h \
|
||||
src/map/ENC/style.h \
|
||||
src/map/IMG/dem.h \
|
||||
src/map/IMG/demfile.h \
|
||||
src/map/IMG/demtile.h \
|
||||
src/map/IMG/jls.h \
|
||||
src/map/IMG/section.h \
|
||||
src/map/IMG/zoom.h \
|
||||
src/map/conversion.h \
|
||||
@ -135,6 +139,7 @@ HEADERS += src/common/config.h \
|
||||
src/map/encjob.h \
|
||||
src/map/encmap.h \
|
||||
src/map/ENC/iso8211.h \
|
||||
src/map/filter.h \
|
||||
src/map/gemfmap.h \
|
||||
src/map/gmifile.h \
|
||||
src/map/oruxmap.h \
|
||||
@ -345,10 +350,14 @@ SOURCES += src/main.cpp \
|
||||
src/map/ENC/mapdata.cpp \
|
||||
src/map/ENC/rastertile.cpp \
|
||||
src/map/ENC/style.cpp \
|
||||
src/map/IMG/dem.cpp \
|
||||
src/map/IMG/demfile.cpp \
|
||||
src/map/IMG/jls.cpp \
|
||||
src/map/conversion.cpp \
|
||||
src/map/encatlas.cpp \
|
||||
src/map/encmap.cpp \
|
||||
src/map/ENC/iso8211.cpp \
|
||||
src/map/filter.cpp \
|
||||
src/map/gemfmap.cpp \
|
||||
src/map/gmifile.cpp \
|
||||
src/map/oruxmap.cpp \
|
||||
|
@ -4,7 +4,7 @@ while read e; do
|
||||
IFS=":"; set $e
|
||||
|
||||
EXT=`echo $1 | tr /a-z/ /A-Z/`
|
||||
sed -e "s/\$EXTENSION/$EXT/" -e "s/\$COLOR/$2/" icon-template.svg > $1.svg
|
||||
sed -e "s/\$EXT/$EXT/" -e "s/\$COLOR/$2/" icon-template.svg > $1.svg
|
||||
|
||||
ICONSET=$1.iconset
|
||||
mkdir $ICONSET
|
||||
|
@ -4,7 +4,7 @@ while read e; do
|
||||
IFS=":"; set $e
|
||||
|
||||
EXT=`echo $1 | tr /a-z/ /A-Z/`
|
||||
sed -e "s/\$EXTENSION/$EXT/" -e "s/\$COLOR/$2/" icon-template.svg > $1.svg
|
||||
sed -e "s/\$EXT/$EXT/" -e "s/\$COLOR/$2/" icon-template.svg > $1.svg
|
||||
convert -density 400 -background none $1.svg -define icon:auto-resize $1.ico
|
||||
rm $1.svg
|
||||
done < extensions
|
||||
|
@ -13,8 +13,8 @@
|
||||
<g transform="translate(16.573463,124.9782)">
|
||||
<rect y="0" x="0" id="textrect" height="50" width="120" style="fill:$COLOR;fill-opacity:1;stroke:none;"/>
|
||||
<!--
|
||||
<text y="28" x="60" dominant-baseline="central" text-anchor="middle" style="fill:#FFFFFF;font-size:39px;font-family:Helvetica;font-weight:bold;">$EXTENSION</text>
|
||||
<text y="28" x="60" dominant-baseline="central" text-anchor="middle" style="fill:#FFFFFF;font-size:39px;font-family:Helvetica;font-weight:bold;">$EXT</text>
|
||||
-->
|
||||
<text y="25" x="60" dy="0.36em" text-anchor="middle" style="fill:#FFFFFF;font-size:39px;font-family:Helvetica;font-weight:bold;">$EXTENSION</text>
|
||||
<text y="25" x="60" dy="0.36em" text-anchor="middle" style="fill:#FFFFFF;font-size:39px;font-family:Helvetica;font-weight:bold;">$EXT</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
icons/symbols/Alert.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 987 B After Width: | Height: | Size: 884 B |
BIN
icons/symbols/Checkpoint.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
icons/symbols/Danger.png
Normal file
After Width: | Height: | Size: 767 B |
BIN
icons/symbols/First Aid.png
Normal file
After Width: | Height: | Size: 606 B |
BIN
icons/symbols/First Category.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 824 B |
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 822 B |
BIN
icons/symbols/Food.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
icons/symbols/Fourth Category.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
icons/symbols/Gear.png
Normal file
After Width: | Height: | Size: 976 B |
BIN
icons/symbols/Hors Category.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
icons/symbols/Info.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
icons/symbols/Left.png
Normal file
After Width: | Height: | Size: 810 B |
BIN
icons/symbols/Meeting Spot.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
icons/symbols/Obstacle.png
Normal file
After Width: | Height: | Size: 863 B |
BIN
icons/symbols/Overlook.png
Normal file
After Width: | Height: | Size: 821 B |
BIN
icons/symbols/Rest Area.png
Normal file
After Width: | Height: | Size: 918 B |
BIN
icons/symbols/Right.png
Normal file
After Width: | Height: | Size: 821 B |
BIN
icons/symbols/Second Category.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
icons/symbols/Segment End.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
icons/symbols/Segment Start.png
Normal file
After Width: | Height: | Size: 724 B |
BIN
icons/symbols/Service.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
icons/symbols/Sharp Curve.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/symbols/Sharp Left.png
Normal file
After Width: | Height: | Size: 942 B |
BIN
icons/symbols/Sharp Right.png
Normal file
After Width: | Height: | Size: 909 B |
BIN
icons/symbols/Shelter.png
Normal file
After Width: | Height: | Size: 764 B |
BIN
icons/symbols/Slight Left.png
Normal file
After Width: | Height: | Size: 826 B |
BIN
icons/symbols/Slight Right.png
Normal file
After Width: | Height: | Size: 825 B |
BIN
icons/symbols/Sprint.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/symbols/Steep Incline.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
icons/symbols/Store.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
icons/symbols/Straight.png
Normal file
After Width: | Height: | Size: 791 B |
BIN
icons/symbols/Third Category.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
icons/symbols/Toilet.png
Normal file
After Width: | Height: | Size: 989 B |
BIN
icons/symbols/Transport.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
icons/symbols/U-Turn.png
Normal file
After Width: | Height: | Size: 989 B |
BIN
icons/symbols/Valley.png
Normal file
After Width: | Height: | Size: 1018 B |
BIN
icons/symbols/Water.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 824 B |
@ -571,12 +571,12 @@
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="457"/>
|
||||
<source>Download data DEM</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>下载 DEM 数据</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="463"/>
|
||||
<source>Download map DEM</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>下载 DEM 地图</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="468"/>
|
||||
@ -586,7 +586,7 @@
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="471"/>
|
||||
<source>Show hillshading</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>显示山体阴影</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="480"/>
|
||||
@ -1034,13 +1034,13 @@
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="1986"/>
|
||||
<source>DEM tiles download limit exceeded. If you really need data for such a huge area, download the files manually.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>已超过 DEM 瓦片下载限额。如您确需大量数据,请手动下载。</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/GUI/gui.cpp" line="1989"/>
|
||||
<source>Download %n DEM tiles?</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<translation>
|
||||
<numerusform>下载 %n DEM 瓦片?</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
@ -269,6 +269,7 @@
|
||||
<root-XML namespaceURI="http://www.gpxsee.org/map/1.2" localName="map"/>
|
||||
<root-XML namespaceURI="http://www.gpxsee.org/map/1.3" localName="map"/>
|
||||
<root-XML namespaceURI="http://www.gpxsee.org/map/1.4" localName="map"/>
|
||||
<root-XML namespaceURI="http://www.gpxsee.org/map/1.5" localName="map"/>
|
||||
<glob pattern="*.xml"/>
|
||||
</mime-type>
|
||||
|
||||
|
@ -1712,7 +1712,7 @@
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.gpxsee.map</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>http://www.gpxsee.org/map/1.4/</string>
|
||||
<string>http://www.gpxsee.org/map/1.5/</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>GPXSee Map Definition File</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
|
@ -37,7 +37,7 @@ Unicode true
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "13.18"
|
||||
!define VERSION "13.20"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}_x64.exe"
|
||||
|
@ -29,9 +29,9 @@ App::App(int &argc, char **argv) : QApplication(argc, argv)
|
||||
{
|
||||
#if defined(Q_OS_WIN32) || defined(Q_OS_MAC)
|
||||
setApplicationName(APP_NAME);
|
||||
#else
|
||||
#else // Q_OS_WIN32 || Q_OS_MAC
|
||||
setApplicationName(QString(APP_NAME).toLower());
|
||||
#endif
|
||||
#endif // Q_OS_WIN32 || Q_OS_MAC
|
||||
setApplicationVersion(APP_VERSION);
|
||||
|
||||
QTranslator *app = new QTranslator(this);
|
||||
@ -65,6 +65,9 @@ App::App(int &argc, char **argv) : QApplication(argc, argv)
|
||||
loadPCSs();
|
||||
Waypoint::loadSymbolIcons(ProgramPaths::symbolsDir());
|
||||
|
||||
#if defined(Q_OS_WIN32) || defined(Q_OS_MAC)
|
||||
QIcon::setThemeName(APP_NAME);
|
||||
#endif // Q_OS_WIN32 || Q_OS_MAC
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||
QIcon::setFallbackThemeName(APP_NAME);
|
||||
#endif // QT 5.12
|
||||
|
@ -471,7 +471,6 @@ void GUI::createActions()
|
||||
_drawHillShadingAction = new QAction(tr("Show hillshading"), this);
|
||||
_drawHillShadingAction->setMenuRole(QAction::NoRole);
|
||||
_drawHillShadingAction->setCheckable(true);
|
||||
_drawHillShadingAction->setEnabled(false);
|
||||
connect(_drawHillShadingAction, &QAction::triggered, _mapView,
|
||||
&MapView::drawHillShading);
|
||||
|
||||
@ -2249,8 +2248,7 @@ void GUI::updateDataDEMDownloadAction()
|
||||
void GUI::updateMapDEMDownloadAction()
|
||||
{
|
||||
_downloadMapDEMAction->setEnabled(!_dem->url().isEmpty()
|
||||
&& _map->usesDEM() && !_dem->checkTiles(_map->llBounds()));
|
||||
_drawHillShadingAction->setEnabled(_map->usesDEM());
|
||||
&& !_dem->checkTiles(_map->llBounds()));
|
||||
}
|
||||
|
||||
void GUI::setTimeType(TimeType type)
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <QClipboard>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QGeoPositionInfoSource>
|
||||
#include "common/dem.h"
|
||||
#include "data/poi.h"
|
||||
#include "data/data.h"
|
||||
#include "map/map.h"
|
||||
@ -1181,9 +1180,7 @@ void MapView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (_cursorCoordinates->isVisible()) {
|
||||
Coordinates c(_map->xy2ll(mapToScene(event->pos())));
|
||||
DEM::lock();
|
||||
_cursorCoordinates->setCoordinates(c, DEM::elevation(c));
|
||||
DEM::unlock();
|
||||
_cursorCoordinates->setCoordinates(c, _map->elevation(c));
|
||||
}
|
||||
|
||||
QGraphicsView::mouseMoveEvent(event);
|
||||
|
@ -727,14 +727,14 @@ QWidget *OptionsDialog::createSystemPage()
|
||||
_enableHTTP2->setChecked(_options.enableHTTP2);
|
||||
|
||||
_pixmapCache = new QSpinBox();
|
||||
_pixmapCache->setMinimum(16);
|
||||
_pixmapCache->setMaximum(2048);
|
||||
_pixmapCache->setMinimum(64);
|
||||
_pixmapCache->setMaximum(4096);
|
||||
_pixmapCache->setSuffix(UNIT_SPACE + tr("MB"));
|
||||
_pixmapCache->setValue(_options.pixmapCache);
|
||||
|
||||
_demCache = new QSpinBox();
|
||||
_demCache->setMinimum(64);
|
||||
_demCache->setMaximum(2048);
|
||||
_demCache->setMaximum(4096);
|
||||
_demCache->setSuffix(UNIT_SPACE + tr("MB"));
|
||||
_demCache->setValue(_options.demCache);
|
||||
|
||||
|
@ -174,7 +174,7 @@ SETTING(markerInfo, "markerInfo", MarkerInfoItem::None );
|
||||
SETTING(useStyles, "styles", true );
|
||||
|
||||
/* DEM */
|
||||
SETTING(drawHillShading, "hillshading", false );
|
||||
SETTING(drawHillShading, "hillshading", true );
|
||||
|
||||
/* Position */
|
||||
SETTING(showPosition, "show", false );
|
||||
|
@ -13,15 +13,18 @@ static Qt::PenStyle styles[] = {Qt::SolidLine, Qt::DashLine, Qt::DotLine,
|
||||
|
||||
QIcon StyleComboBox::icon(Qt::PenStyle style)
|
||||
{
|
||||
QPixmap pm(iconSize());
|
||||
qreal ratio(devicePixelRatioF());
|
||||
QSize size(iconSize());
|
||||
QPixmap pm(size * ratio);
|
||||
pm.setDevicePixelRatio(ratio);
|
||||
pm.fill(Qt::transparent);
|
||||
|
||||
QBrush brush(QPalette().brush(QPalette::Active, QPalette::WindowText));
|
||||
QPen pen(brush, pm.height() / LINE_WIDTH_RATIO, style);
|
||||
QPen pen(brush, size.height() / LINE_WIDTH_RATIO, style);
|
||||
|
||||
QPainter painter(&pm);
|
||||
painter.setPen(pen);
|
||||
painter.drawLine(0, pm.height() / 2, pm.width(), pm.height() / 2);
|
||||
painter.drawLine(0, size.height() / 2, size.width(), size.height() / 2);
|
||||
|
||||
return QIcon(pm);
|
||||
}
|
||||
|
@ -170,8 +170,7 @@ void WaypointItem::paint(QPainter *painter,
|
||||
if (_font.bold())
|
||||
painter->drawPixmap(-_icon->width() * 0.625, icon.isNull()
|
||||
? -_icon->height() * 1.25 : -_icon->height() * 0.625,
|
||||
_icon->scaled(_icon->width() * 1.25, _icon->height() * 1.25,
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
_icon->scaled(_icon->width() * 1.25, _icon->height() * 1.25));
|
||||
else
|
||||
painter->drawPixmap(-_icon->width()/2.0, icon.isNull()
|
||||
? -_icon->height() : -_icon->height()/2, *_icon);
|
||||
|
@ -65,7 +65,7 @@ QString DEM::Tile::lonStr() const
|
||||
return QString("%1%2").arg(ew).arg(qAbs(_lon), 3, 10, QChar('0'));
|
||||
}
|
||||
|
||||
QString DEM::Tile::baseName() const
|
||||
QString DEM::Tile::fileName() const
|
||||
{
|
||||
return QString("%1%2.hgt").arg(latStr(), lonStr());
|
||||
}
|
||||
@ -75,7 +75,7 @@ DEM::TileCache DEM::_data;
|
||||
|
||||
void DEM::setCacheSize(int size)
|
||||
{
|
||||
_data.setMaxCost(size * 1024);
|
||||
_data.setMaxCost(size);
|
||||
}
|
||||
|
||||
void DEM::setDir(const QString &path)
|
||||
@ -108,15 +108,15 @@ double DEM::height(const Coordinates &c, const Entry *e)
|
||||
|
||||
DEM::Entry *DEM::loadTile(const Tile &tile)
|
||||
{
|
||||
QString bn(tile.baseName());
|
||||
QString fn(QDir(_dir).absoluteFilePath(bn));
|
||||
QString zn(fn + ".zip");
|
||||
QString fileName(tile.fileName());
|
||||
QString path(QDir(_dir).absoluteFilePath(fileName));
|
||||
QString zipPath(path + ".zip");
|
||||
|
||||
if (QFileInfo::exists(zn)) {
|
||||
QZipReader zip(zn, QIODevice::ReadOnly);
|
||||
return new Entry(zip.fileData(bn));
|
||||
if (QFileInfo::exists(zipPath)) {
|
||||
QZipReader zip(zipPath, QIODevice::ReadOnly);
|
||||
return new Entry(zip.fileData(fileName));
|
||||
} else {
|
||||
QFile file(fn);
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning("%s: %s", qPrintable(file.fileName()),
|
||||
qPrintable(file.errorString()));
|
||||
@ -138,7 +138,7 @@ double DEM::elevation(const Coordinates &c)
|
||||
if (!e) {
|
||||
e = loadTile(tile);
|
||||
ele = height(c, e);
|
||||
_data.insert(tile, e, e->data().size());
|
||||
_data.insert(tile, e, e->data().size() / 1024);
|
||||
} else
|
||||
ele = height(c, e);
|
||||
|
||||
@ -147,15 +147,15 @@ double DEM::elevation(const Coordinates &c)
|
||||
|
||||
QList<Area> DEM::tiles()
|
||||
{
|
||||
static const QRegularExpression re("([NS])([0-9]{2})([EW])([0-9]{3})");
|
||||
static const QRegularExpression re(
|
||||
"^([NS])([0-9]{2})([EW])([0-9]{3})(\\.hgt|\\.hgt\\.zip)$");
|
||||
QDir dir(_dir);
|
||||
QFileInfoList files(dir.entryInfoList(QDir::Files | QDir::Readable));
|
||||
QLocale l(QLocale::system());
|
||||
QList<Area> list;
|
||||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
QString basename(files.at(i).baseName());
|
||||
QRegularExpressionMatch match(re.match(basename));
|
||||
QRegularExpressionMatch match(re.match(files.at(i).fileName()));
|
||||
if (!match.hasMatch())
|
||||
continue;
|
||||
|
||||
@ -167,7 +167,7 @@ QList<Area> DEM::tiles()
|
||||
lon = -lon;
|
||||
|
||||
Area area(RectC(Coordinates(lon, lat + 1), Coordinates(lon + 1, lat)));
|
||||
area.setName(basename);
|
||||
area.setName(files.at(i).baseName());
|
||||
area.setDescription(files.at(i).suffix().toUpper() + ", "
|
||||
+ l.formattedDataSize(files.at(i).size()));
|
||||
area.setStyle(PolygonStyle(QColor(0xFF, 0, 0, 0x40),
|
||||
@ -182,7 +182,7 @@ QList<Area> DEM::tiles()
|
||||
#ifndef QT_NO_DEBUG
|
||||
QDebug operator<<(QDebug dbg, const DEM::Tile &tile)
|
||||
{
|
||||
dbg.nospace() << "Tile(" << tile.baseName() << ")";
|
||||
dbg.nospace() << "Tile(" << tile.fileName() << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
#endif // QT_NO_DEBUG
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
|
||||
QString lonStr() const;
|
||||
QString latStr() const;
|
||||
QString baseName() const;
|
||||
QString fileName() const;
|
||||
|
||||
bool operator==(const Tile &other) const
|
||||
{
|
||||
|
@ -110,7 +110,7 @@ QUrl DEMLoader::tileUrl(const DEM::Tile &tile) const
|
||||
|
||||
QString DEMLoader::tileFile(const DEM::Tile &tile) const
|
||||
{
|
||||
return _dir.absoluteFilePath(tile.baseName());
|
||||
return _dir.absoluteFilePath(tile.fileName());
|
||||
}
|
||||
|
||||
void DEMLoader::setAuthorization(const Authorization &authorization)
|
||||
|
@ -106,7 +106,7 @@ Data::Data(const QString &fileName, bool tryUnknown)
|
||||
_errorLine = 0;
|
||||
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
_errorString = qPrintable(file.errorString());
|
||||
_errorString = file.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ public:
|
||||
SegmentData segment;
|
||||
};
|
||||
|
||||
static QMap<int, QString> coursePointDescInit()
|
||||
static QMap<int, QString> coursePointSymbolsInit()
|
||||
{
|
||||
QMap<int, QString> map;
|
||||
|
||||
@ -76,28 +76,70 @@ static QMap<int, QString> coursePointDescInit()
|
||||
map.insert(6, "Left");
|
||||
map.insert(7, "Right");
|
||||
map.insert(8, "Straight");
|
||||
map.insert(9, "First aid");
|
||||
map.insert(10, "Fourth category");
|
||||
map.insert(11, "Third category");
|
||||
map.insert(12, "Second category");
|
||||
map.insert(13, "First category");
|
||||
map.insert(14, "Hors category");
|
||||
map.insert(9, "First Aid");
|
||||
map.insert(10, "Fourth Category");
|
||||
map.insert(11, "Third Category");
|
||||
map.insert(12, "Second Category");
|
||||
map.insert(13, "First Category");
|
||||
map.insert(14, "Hors Category");
|
||||
map.insert(15, "Sprint");
|
||||
map.insert(16, "Left fork");
|
||||
map.insert(17, "Right fork");
|
||||
map.insert(18, "Middle fork");
|
||||
map.insert(19, "Slight left");
|
||||
map.insert(20, "Sharp left");
|
||||
map.insert(21, "Slight right");
|
||||
map.insert(22, "Sharp right");
|
||||
map.insert(16, "Left Fork");
|
||||
map.insert(17, "Right Fork");
|
||||
map.insert(18, "Middle Fork");
|
||||
map.insert(19, "Slight Left");
|
||||
map.insert(20, "Sharp Left");
|
||||
map.insert(21, "Slight Right");
|
||||
map.insert(22, "Sharp Right");
|
||||
map.insert(23, "U-Turn");
|
||||
map.insert(24, "Segment start");
|
||||
map.insert(25, "Segment end");
|
||||
map.insert(24, "Segment Start");
|
||||
map.insert(25, "Segment End");
|
||||
map.insert(27, "Campground");
|
||||
map.insert(28, "Aid Station");
|
||||
map.insert(29, "Rest Area");
|
||||
map.insert(30, "General Distance");
|
||||
map.insert(31, "Service");
|
||||
map.insert(32, "Energy Gel");
|
||||
map.insert(33, "Sports Drink");
|
||||
map.insert(34, "Mile Marker");
|
||||
map.insert(35, "Checkpoint");
|
||||
map.insert(36, "Shelter");
|
||||
map.insert(37, "Meeting Spot");
|
||||
map.insert(38, "Overlook");
|
||||
map.insert(39, "Toilet");
|
||||
map.insert(40, "Shower");
|
||||
map.insert(41, "Gear");
|
||||
map.insert(42, "Sharp Curve");
|
||||
map.insert(43, "Steep Incline");
|
||||
map.insert(44, "Tunnel");
|
||||
map.insert(45, "Bridge");
|
||||
map.insert(46, "Obstacle");
|
||||
map.insert(47, "Crossing");
|
||||
map.insert(48, "Store");
|
||||
map.insert(49, "Transition");
|
||||
map.insert(50, "Navaid");
|
||||
map.insert(51, "Transport");
|
||||
map.insert(52, "Alert");
|
||||
map.insert(53, "Info");
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static QMap<int, QString> coursePointDesc = coursePointDescInit();
|
||||
static QMap<int, QString> locationPointSymbolsInit()
|
||||
{
|
||||
QMap<int, QString> map;
|
||||
|
||||
/* The location symbols are a typical GARMIN mess. Every GPS unit
|
||||
has probably its own list, so we only add a few generic icons seen
|
||||
"in the wild" most often. */
|
||||
map.insert(94, "Flag, Blue");
|
||||
map.insert(95, "Flag, Green");
|
||||
map.insert(96, "Flag, Red");
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static QMap<int, QString> coursePointSymbols = coursePointSymbolsInit();
|
||||
static QMap<int, QString> locationPointSymbols = locationPointSymbolsInit();
|
||||
|
||||
|
||||
bool FITParser::readData(QFile *file, char *data, size_t size)
|
||||
@ -244,7 +286,7 @@ bool FITParser::readField(CTX &ctx, Field *field, QVariant &val, bool &valid)
|
||||
ctx.len -= field->size;
|
||||
ret = (ba.size() == field->size);
|
||||
val = ret ? ba : QString();
|
||||
valid = !ba.isEmpty();}
|
||||
valid = (!ba.isEmpty() && ba.at(0) != 0);}
|
||||
break;
|
||||
default:
|
||||
ret = skipValue(ctx, field->size);
|
||||
@ -330,8 +372,8 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
|
||||
} else if (def->globalId == COURSE_POINT) {
|
||||
switch (field->id) {
|
||||
case 1:
|
||||
waypoint.setTimestamp(QDateTime::fromSecsSinceEpoch(val.toUInt()
|
||||
+ 631065600, Qt::UTC));
|
||||
waypoint.setTimestamp(QDateTime::fromSecsSinceEpoch(
|
||||
val.toUInt() + 631065600, Qt::UTC));
|
||||
break;
|
||||
case 2:
|
||||
waypoint.rcoordinates().setLat(
|
||||
@ -342,7 +384,7 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
|
||||
(val.toInt() / (double)0x7fffffff) * 180);
|
||||
break;
|
||||
case 5:
|
||||
waypoint.setDescription(coursePointDesc.value(val.toUInt()));
|
||||
waypoint.setSymbol(coursePointSymbols.value(val.toUInt()));
|
||||
break;
|
||||
case 6:
|
||||
waypoint.setName(val.toString());
|
||||
@ -361,6 +403,9 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
|
||||
waypoint.rcoordinates().setLon(
|
||||
(val.toInt() / (double)0x7fffffff) * 180);
|
||||
break;
|
||||
case 3:
|
||||
waypoint.setSymbol(locationPointSymbols.value(val.toUInt()));
|
||||
break;
|
||||
case 4:
|
||||
waypoint.setElevation((val.toUInt() / 5.0) - 500);
|
||||
break;
|
||||
@ -386,8 +431,8 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
|
||||
}
|
||||
} else if (def->globalId == RECORD_MESSAGE) {
|
||||
if (ctx.trackpoint.coordinates().isValid()) {
|
||||
ctx.trackpoint.setTimestamp(QDateTime::fromSecsSinceEpoch(ctx.timestamp
|
||||
+ 631065600, Qt::UTC));
|
||||
ctx.trackpoint.setTimestamp(QDateTime::fromSecsSinceEpoch(
|
||||
ctx.timestamp + 631065600, Qt::UTC));
|
||||
ctx.trackpoint.setRatio(ctx.ratio);
|
||||
ctx.segment.append(ctx.trackpoint);
|
||||
ctx.trackpoint = Trackpoint();
|
||||
|
@ -603,6 +603,8 @@ bool GeoJSONParser::geometryCollection(const QJsonObject &object,
|
||||
bool GeoJSONParser::feature(const QJsonObject &object, const Projection &parent,
|
||||
QList<TrackData> &tracks, QList<Area> &areas, QVector<Waypoint> &waypoints)
|
||||
{
|
||||
if (object["geometry"].isNull())
|
||||
return true;
|
||||
if (!object["geometry"].isObject()) {
|
||||
_errorString = "Invalid/missing Feature geometry object";
|
||||
return false;
|
||||
|
@ -76,6 +76,9 @@ void GPXParser::tpExtension(Trackpoint &trackpoint)
|
||||
trackpoint.setHeartRate(number());
|
||||
else if (_reader.name() == QLatin1String("atemp"))
|
||||
trackpoint.setTemperature(number());
|
||||
else if (_reader.name() == QLatin1String("wtemp")
|
||||
&& !trackpoint.hasTemperature())
|
||||
trackpoint.setTemperature(number());
|
||||
else if (_reader.name() == QLatin1String("cad"))
|
||||
trackpoint.setCadence(number());
|
||||
else if (_reader.name() == QLatin1String("speed"))
|
||||
|
@ -123,6 +123,8 @@ void TCXParser::waypointData(Waypoint &waypoint)
|
||||
waypoint.setElevation(number());
|
||||
else if (_reader.name() == QLatin1String("Time"))
|
||||
waypoint.setTimestamp(time());
|
||||
else if (_reader.name() == QLatin1String("PointType"))
|
||||
waypoint.setSymbol(_reader.readElementText());
|
||||
else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
|
@ -76,7 +76,6 @@ private:
|
||||
bool readBytesAligned(quint32 bytes, quint32 &val);
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
bool BitStream1::read(quint32 bits, T &val)
|
||||
{
|
||||
|
112
src/map/IMG/dem.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "dem.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
#define DELTA 1e-9 /* ensure col+1/row+1 is in the next tile */
|
||||
|
||||
static double interpolate(double dx, double dy, double p0, double p1, double p2,
|
||||
double p3)
|
||||
{
|
||||
return p0 * (1.0 - dx) * (1.0 - dy) + p1 * dx * (1.0 - dy)
|
||||
+ p2 * dy * (1.0 - dx) + p3 * dx * dy;
|
||||
}
|
||||
|
||||
static double val(const Matrix<qint16> &m, int row, int col)
|
||||
{
|
||||
qint16 v = m.at(row, col);
|
||||
return (v == -32768) ? NAN : (double)v;
|
||||
}
|
||||
|
||||
bool DEM::edgeCb(const MapData::Elevation *e, void *context)
|
||||
{
|
||||
EdgeCTX *ctx = (EdgeCTX*)context;
|
||||
|
||||
double x = (ctx->c.lon() - e->rect.left()) / e->xr;
|
||||
double y = (e->rect.top() - ctx->c.lat()) / e->yr;
|
||||
int row = qMin((int)y, e->m.h() - 1);
|
||||
int col = qMin((int)x, e->m.w() - 1);
|
||||
|
||||
ctx->ele = val(e->m, row, col);
|
||||
|
||||
return std::isnan(ctx->ele);
|
||||
}
|
||||
|
||||
double DEM::edge(const DEMTRee &tree, const Coordinates &c)
|
||||
{
|
||||
double min[2], max[2];
|
||||
double ele = NAN;
|
||||
EdgeCTX ctx(c, ele);
|
||||
|
||||
min[0] = c.lon();
|
||||
min[1] = c.lat();
|
||||
max[0] = c.lon();
|
||||
max[1] = c.lat();
|
||||
|
||||
tree.Search(min, max, edgeCb, &ctx);
|
||||
|
||||
return ele;
|
||||
}
|
||||
|
||||
double DEM::elevation(const DEMTRee &tree, const MapData::Elevation *e,
|
||||
const Coordinates &c)
|
||||
{
|
||||
double x = (c.lon() - e->rect.left()) / e->xr;
|
||||
double y = (e->rect.top() - c.lat()) / e->yr;
|
||||
int row = qMin((int)y, e->m.h() - 1);
|
||||
int col = qMin((int)x, e->m.w() - 1);
|
||||
|
||||
double p0 = val(e->m, row, col);
|
||||
double p1 = (col == e->m.w() - 1)
|
||||
? edge(tree, Coordinates(e->rect.left() + (col + 1) * e->xr + DELTA,
|
||||
e->rect.top() - row * e->yr))
|
||||
: val(e->m, row, col + 1);
|
||||
double p2 = (row == e->m.h() - 1)
|
||||
? edge(tree, Coordinates(e->rect.left() + col * e->xr,
|
||||
e->rect.top() - (row + 1) * e->yr - DELTA))
|
||||
: val(e->m, row + 1, col);
|
||||
double p3 = ((col == e->m.w() - 1) || (row == e->m.h() - 1))
|
||||
? edge(tree, Coordinates(e->rect.left() + (col + 1) * e->xr + DELTA,
|
||||
e->rect.top() - (row + 1) * e->yr - DELTA))
|
||||
: val(e->m, row + 1, col + 1);
|
||||
|
||||
return interpolate(x - col, y - row, p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
void DEM::buildTree(const QList<MapData::Elevation> &tiles, DEMTRee &tree)
|
||||
{
|
||||
double min[2], max[2];
|
||||
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
const MapData::Elevation &e = tiles.at(i);
|
||||
|
||||
min[0] = e.rect.left();
|
||||
min[1] = e.rect.bottom();
|
||||
max[0] = e.rect.right();
|
||||
max[1] = e.rect.top();
|
||||
|
||||
tree.Insert(min, max, &e);
|
||||
}
|
||||
}
|
||||
|
||||
bool DEM::elevationCb(const MapData::Elevation *e, void *context)
|
||||
{
|
||||
ElevationCTX *ctx = (ElevationCTX*)context;
|
||||
|
||||
ctx->ele = elevation(ctx->tree, e, ctx->c);
|
||||
return std::isnan(ctx->ele);
|
||||
}
|
||||
|
||||
void DEM::searchTree(const DEMTRee &tree, const Coordinates &c,
|
||||
double &ele)
|
||||
{
|
||||
double min[2], max[2];
|
||||
ElevationCTX ctx(tree, c, ele);
|
||||
|
||||
min[0] = c.lon();
|
||||
min[1] = c.lat();
|
||||
max[0] = c.lon();
|
||||
max[1] = c.lat();
|
||||
|
||||
ele = NAN;
|
||||
tree.Search(min, max, elevationCb, &ctx);
|
||||
}
|
44
src/map/IMG/dem.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef IMG_DEM_H
|
||||
#define IMG_DEM_H
|
||||
|
||||
#include "common/rtree.h"
|
||||
#include "mapdata.h"
|
||||
|
||||
namespace IMG {
|
||||
|
||||
class DEM {
|
||||
public:
|
||||
|
||||
typedef RTree<const MapData::Elevation*, double, 2> DEMTRee;
|
||||
|
||||
static void buildTree(const QList<MapData::Elevation> &tiles, DEMTRee &tree);
|
||||
static void searchTree(const DEMTRee &tree, const Coordinates &c,
|
||||
double &ele);
|
||||
|
||||
private:
|
||||
struct ElevationCTX {
|
||||
ElevationCTX(const DEMTRee &tree, const Coordinates &c, double &ele)
|
||||
: tree(tree), c(c), ele(ele) {}
|
||||
|
||||
const DEMTRee &tree;
|
||||
const Coordinates &c;
|
||||
double &ele;
|
||||
};
|
||||
|
||||
struct EdgeCTX {
|
||||
EdgeCTX(const Coordinates &c, double &ele) : c(c), ele(ele) {}
|
||||
|
||||
const Coordinates &c;
|
||||
double &ele;
|
||||
};
|
||||
|
||||
static double edge(const DEMTRee &tree, const Coordinates &c);
|
||||
static double elevation(const DEMTRee &tree, const MapData::Elevation *e,
|
||||
const Coordinates &c);
|
||||
static bool elevationCb(const MapData::Elevation *e, void *context);
|
||||
static bool edgeCb(const MapData::Elevation *e, void *context);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // IMG_DEM_H
|
187
src/map/IMG/demfile.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#include "common/garmin.h"
|
||||
#include "jls.h"
|
||||
#include "demfile.h"
|
||||
|
||||
using namespace IMG;
|
||||
using namespace Garmin;
|
||||
|
||||
static qint16 limit(const DEMTile *tile, quint16 factor)
|
||||
{
|
||||
quint8 f1 = (tile->flags() & 1) != 0;
|
||||
qint16 l = f1 ? tile->diff() - factor : tile->diff() + 1;
|
||||
|
||||
if (tile->flags() > 1) {
|
||||
for (int i = 1; i < 8; i++) {
|
||||
if (((tile->flags() >> i) & 1) != 0)
|
||||
l = (l - 1) - (factor << f1);
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void DEMFile::clear()
|
||||
{
|
||||
_levels.clear();
|
||||
}
|
||||
|
||||
bool DEMFile::load(Handle &hdl)
|
||||
{
|
||||
quint32 u32, zoomData;
|
||||
quint16 zooms, zoomDataSize;
|
||||
|
||||
if (!(seek(hdl, _gmpOffset + 0x15) && readUInt32(hdl, _flags)
|
||||
&& readUInt16(hdl, zooms) && readUInt32(hdl, u32)
|
||||
&& readUInt16(hdl, zoomDataSize) && readUInt32(hdl, zoomData)))
|
||||
return false;
|
||||
|
||||
_levels.reserve(zooms);
|
||||
|
||||
for (quint16 i = 0; i < zooms; i++) {
|
||||
quint32 pixelWidth, pixelHeight, pixelWidth2, pixelHeight2, table, cols,
|
||||
rows, xr, yr, data;
|
||||
qint32 lon, lat;
|
||||
quint16 encoding, size, factor;
|
||||
qint16 minHeight, maxHeight;
|
||||
quint8 layer, level;
|
||||
QList<DEMTile> tiles;
|
||||
|
||||
if (!(seek(hdl, zoomData + i * zoomDataSize) && readUInt8(hdl, layer)
|
||||
&& readUInt8(hdl, level) && readUInt32(hdl, pixelHeight)
|
||||
&& readUInt32(hdl, pixelWidth) && readUInt32(hdl, pixelHeight2)
|
||||
&& readUInt32(hdl, pixelWidth2) && readUInt16(hdl, factor)
|
||||
&& readUInt32(hdl, cols) && readUInt32(hdl, rows)
|
||||
&& readUInt16(hdl, encoding) && readUInt16(hdl, size)
|
||||
&& readUInt32(hdl, table) && readUInt32(hdl, data)
|
||||
&& readInt32(hdl, lon) && readInt32(hdl, lat)
|
||||
&& readUInt32(hdl, yr) && readUInt32(hdl, xr)
|
||||
&& readInt16(hdl, minHeight) && readInt16(hdl, maxHeight)))
|
||||
return false;
|
||||
|
||||
if (layer)
|
||||
continue;
|
||||
|
||||
if (!seek(hdl, table))
|
||||
return false;
|
||||
|
||||
tiles.reserve((rows + 1) * (cols + 1));
|
||||
|
||||
for (quint32 i = 0; i < rows + 1; i++) {
|
||||
for (quint32 j = 0; j < cols + 1; j++) {
|
||||
qint32 x = lon + j * pixelWidth * xr;
|
||||
qint32 y = lat - i * pixelHeight * yr;
|
||||
quint32 w = (j == cols) ? (pixelWidth2 + 1) : pixelWidth;
|
||||
quint32 h = (i == rows) ? (pixelHeight2 + 1) : pixelHeight;
|
||||
RectC r(Coordinates(toWGS32(x), toWGS32(y)),
|
||||
Coordinates(toWGS32(x + w * xr), toWGS32(y - h * yr)));
|
||||
|
||||
quint32 offset;
|
||||
qint16 base;
|
||||
quint16 diff;
|
||||
quint8 flags = 0;
|
||||
|
||||
if (!readVUInt32(hdl, (encoding & 0x3) + 1, offset))
|
||||
return false;
|
||||
if (encoding & 0x4) {
|
||||
if (!readInt16(hdl, base))
|
||||
return false;
|
||||
} else {
|
||||
if (!readInt8(hdl, base))
|
||||
return false;
|
||||
}
|
||||
if (encoding & 0x8) {
|
||||
if (!readUInt16(hdl, diff))
|
||||
return false;
|
||||
} else {
|
||||
if (!readUInt8(hdl, diff))
|
||||
return false;
|
||||
}
|
||||
if ((encoding & 0x10) && !readUInt8(hdl, flags))
|
||||
return false;
|
||||
|
||||
tiles.append(DEMTile(r, w, h, offset, base, diff, flags));
|
||||
}
|
||||
}
|
||||
|
||||
_levels.append(Level(RectC(tiles.first().rect().topLeft(),
|
||||
tiles.last().rect().bottomRight()), toWGS32(xr), toWGS32(yr),
|
||||
toWGS32(pixelWidth * xr), toWGS32(pixelHeight* yr),
|
||||
data, rows + 1, cols + 1, factor, level, minHeight, maxHeight, tiles));
|
||||
}
|
||||
|
||||
return !_levels.isEmpty();
|
||||
}
|
||||
|
||||
QList<const DEMTile*> DEMFile::tiles(const RectC &rect, int level) const
|
||||
{
|
||||
const Level &lvl = _levels.at(level);
|
||||
QList<const DEMTile*> ret;
|
||||
|
||||
RectC ir(lvl.rect & rect);
|
||||
double left = (ir.left() - lvl.rect.left()) / lvl.txr;
|
||||
double top = (lvl.rect.top() - ir.top()) / lvl.tyr;
|
||||
double right = (ir.right() - lvl.rect.left()) / lvl.txr;
|
||||
double bottom = (lvl.rect.top() - ir.bottom()) / lvl.tyr;
|
||||
quint32 t = qMin((quint32)top, lvl.rows - 1);
|
||||
quint32 l = qMin((quint32)left, lvl.cols - 1);
|
||||
quint32 b = qMin((quint32)bottom, lvl.rows - 1);
|
||||
quint32 r = qMin((quint32)right, lvl.cols - 1);
|
||||
|
||||
ret.reserve((b - t + 1) * (r - l + 1));
|
||||
|
||||
for (quint32 i = t; i <= b; i++)
|
||||
for (quint32 j = l; j <= r; j++)
|
||||
ret.append(&lvl.tiles.at(lvl.cols * i + j));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int DEMFile::level(const Zoom &zoom) const
|
||||
{
|
||||
for (int i = 0; i < _levels.size(); i++)
|
||||
if (_levels.at(i).level >= zoom.level())
|
||||
return i;
|
||||
|
||||
return _levels.size() - 1;
|
||||
}
|
||||
|
||||
MapData::Elevation *DEMFile::elevations(Handle &hdl, int level,
|
||||
const DEMTile *tile)
|
||||
{
|
||||
const Level &l = _levels.at(level);
|
||||
MapData::Elevation *ele = new MapData::Elevation();
|
||||
ele->rect = tile->rect();
|
||||
ele->xr = l.xr;
|
||||
ele->yr = l.yr;
|
||||
|
||||
if (!tile->diff()) {
|
||||
ele->m = Matrix<qint16>(tile->h(), tile->w(),
|
||||
tile->flags() ? -32768 : meters(tile->base()));
|
||||
return ele;
|
||||
}
|
||||
|
||||
if (!seek(hdl, tile->offset() + l.data))
|
||||
return ele;
|
||||
|
||||
quint16 lim = limit(tile, l.factor);
|
||||
Matrix<qint16> m(tile->h(), tile->w());
|
||||
JLS jls(tile->diff(), l.factor);
|
||||
if (jls.decode(this, hdl, m)) {
|
||||
for (int i = 0; i < m.size(); i++) {
|
||||
if (m.at(i) >= lim)
|
||||
m.at(i) = -32768;
|
||||
else {
|
||||
m.at(i) += tile->base();
|
||||
if (m.at(i) < l.minHeight)
|
||||
m.at(i) = l.minHeight;
|
||||
if (m.at(i) > l.maxHeight)
|
||||
m.at(i) = l.maxHeight;
|
||||
m.at(i) = meters(m.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
ele->m = m;
|
||||
}
|
||||
|
||||
return ele;
|
||||
}
|
58
src/map/IMG/demfile.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef DEMFILE_H
|
||||
#define DEMFILE_H
|
||||
|
||||
#include "common/rtree.h"
|
||||
#include "subfile.h"
|
||||
#include "demtile.h"
|
||||
|
||||
namespace IMG {
|
||||
|
||||
class DEMFile : public SubFile
|
||||
{
|
||||
public:
|
||||
DEMFile(const IMGData *img) : SubFile(img) {}
|
||||
DEMFile(const QString *path) : SubFile(path) {}
|
||||
DEMFile(const SubFile *gmp, quint32 offset) : SubFile(gmp, offset) {}
|
||||
|
||||
bool load(Handle &hdl);
|
||||
void clear();
|
||||
MapData::Elevation *elevations(Handle &hdl, int level, const DEMTile *tile);
|
||||
|
||||
int level(const Zoom &zoom) const;
|
||||
QList<const DEMTile *> tiles(const RectC &rect, int level) const;
|
||||
|
||||
private:
|
||||
struct Level {
|
||||
Level() {}
|
||||
Level(const RectC &rect, double xr, double yr, double txr, double tyr,
|
||||
quint32 data, quint32 rows, quint32 cols, quint16 factor,
|
||||
quint8 level, qint16 minHeight, qint16 maxHeight,
|
||||
const QList<DEMTile> &tiles)
|
||||
: rect(rect), xr(xr), yr(yr), txr(txr), tyr(tyr), data(data),
|
||||
rows(rows), cols(cols), factor(factor), level(level),
|
||||
minHeight(minHeight), maxHeight(maxHeight), tiles(tiles) {}
|
||||
|
||||
RectC rect;
|
||||
double xr, yr;
|
||||
double txr, tyr;
|
||||
quint32 data;
|
||||
quint32 rows, cols;
|
||||
quint16 factor;
|
||||
quint8 level;
|
||||
qint16 minHeight;
|
||||
qint16 maxHeight;
|
||||
QList<DEMTile> tiles;
|
||||
};
|
||||
|
||||
qint16 meters(qint16 val)
|
||||
{
|
||||
return (_flags & 1) ? (qint16)qRound(val * 0.3048) : val;
|
||||
}
|
||||
|
||||
quint32 _flags;
|
||||
QVector<Level> _levels;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DEMFILE_H
|
36
src/map/IMG/demtile.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef DEMTILE_H
|
||||
#define DEMTILE_H
|
||||
|
||||
#include "common/rectc.h"
|
||||
|
||||
namespace IMG {
|
||||
|
||||
class DEMTile {
|
||||
public:
|
||||
DEMTile(const RectC &rect, quint32 w, quint32 h, quint32 offset,
|
||||
qint16 base, quint16 diff, quint8 flags)
|
||||
: _rect(rect), _w(w), _h(h), _offset(offset), _base(base),
|
||||
_diff(diff), _flags(flags) {}
|
||||
|
||||
const RectC &rect() const {return _rect;}
|
||||
quint32 w() const {return _w;}
|
||||
quint32 h() const {return _h;}
|
||||
|
||||
quint32 offset() const {return _offset;}
|
||||
qint16 base() const {return _base;}
|
||||
quint16 diff() const {return _diff;}
|
||||
quint8 flags() const {return _flags;}
|
||||
|
||||
private:
|
||||
RectC _rect;
|
||||
quint32 _w, _h;
|
||||
|
||||
quint32 _offset;
|
||||
qint16 _base;
|
||||
quint16 _diff;
|
||||
quint8 _flags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DEMTILE_H
|
@ -7,6 +7,9 @@ using namespace IMG;
|
||||
|
||||
static SubFile::Type tileType(const QString &suffix)
|
||||
{
|
||||
/* Note: we do not load NOD files from non-NT maps as we have no usage
|
||||
for them */
|
||||
|
||||
if (!suffix.compare("TRE"))
|
||||
return SubFile::TRE;
|
||||
else if (!suffix.compare("RGN"))
|
||||
@ -15,10 +18,12 @@ static SubFile::Type tileType(const QString &suffix)
|
||||
return SubFile::LBL;
|
||||
else if (!suffix.compare("TYP"))
|
||||
return SubFile::TYP;
|
||||
else if (!suffix.compare("GMP"))
|
||||
return SubFile::GMP;
|
||||
else if (!suffix.compare("NET"))
|
||||
return SubFile::NET;
|
||||
else if (!suffix.compare("DEM"))
|
||||
return SubFile::DEM;
|
||||
else if (!suffix.compare("GMP"))
|
||||
return SubFile::GMP;
|
||||
else
|
||||
return SubFile::Unknown;
|
||||
}
|
||||
@ -101,6 +106,7 @@ bool GMAPData::loadTile(const QDir &dir)
|
||||
_tileTree.Insert(min, max, tile);
|
||||
|
||||
_bounds |= tile->bounds();
|
||||
_hasDEM |= tile->hasDem();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ using namespace IMG;
|
||||
|
||||
static SubFile::Type tileType(const char str[3])
|
||||
{
|
||||
/* Note: we do not load NOD files from non-NT maps as we have no usage
|
||||
for them */
|
||||
|
||||
if (!memcmp(str, "TRE", 3))
|
||||
return SubFile::TRE;
|
||||
else if (!memcmp(str, "RGN", 3))
|
||||
@ -16,10 +19,12 @@ static SubFile::Type tileType(const char str[3])
|
||||
return SubFile::LBL;
|
||||
else if (!memcmp(str, "TYP", 3))
|
||||
return SubFile::TYP;
|
||||
else if (!memcmp(str, "GMP", 3))
|
||||
return SubFile::GMP;
|
||||
else if (!memcmp(str, "NET", 3))
|
||||
return SubFile::NET;
|
||||
else if (!memcmp(str, "DEM", 3))
|
||||
return SubFile::DEM;
|
||||
else if (!memcmp(str, "GMP", 3))
|
||||
return SubFile::GMP;
|
||||
else
|
||||
return SubFile::Unknown;
|
||||
}
|
||||
@ -156,6 +161,7 @@ bool IMGData::createTileTree(const TileMap &tileMap)
|
||||
_tileTree.Insert(min, max, tile);
|
||||
|
||||
_bounds |= tile->bounds();
|
||||
_hasDEM |= tile->hasDem();
|
||||
}
|
||||
|
||||
return (_tileTree.Count() > 0);
|
||||
|
332
src/map/IMG/jls.cpp
Normal file
@ -0,0 +1,332 @@
|
||||
#include <cmath>
|
||||
#include "jls.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static const quint8 Z[] = {
|
||||
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
static const int J[] = {
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||
4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||
};
|
||||
|
||||
JLS::JLS(quint16 diff, quint16 factor)
|
||||
{
|
||||
_maxval = diff;
|
||||
_near = factor;
|
||||
|
||||
_range = ((_maxval + _near * 2) / (_near * 2 + 1)) + 1;
|
||||
_qbpp = ceil(log2(_range));
|
||||
quint8 bpp = max(2, ceil(log2(_maxval + 1)));
|
||||
quint8 LIMIT = 2 * (bpp + max(8, bpp));
|
||||
_limit = LIMIT - _qbpp - 1;
|
||||
}
|
||||
|
||||
bool JLS::processRunMode(BitStream &bs, quint16 col, quint16 &samples)
|
||||
{
|
||||
quint8 z;
|
||||
quint16 cnt = 0;
|
||||
|
||||
while (true) {
|
||||
if ((qint32)bs.value() < 0) {
|
||||
z = Z[(bs.value() >> 0x18) ^ 0xff];
|
||||
|
||||
for (quint8 i = 0; i < z; i++) {
|
||||
cnt = cnt + _rg;
|
||||
|
||||
if (cnt <= col && _runIndex < 31) {
|
||||
_runIndex++;
|
||||
_rk = J[_runIndex];
|
||||
_rg = 1U << _rk;
|
||||
}
|
||||
|
||||
if (cnt >= col) {
|
||||
if (!bs.read(i + 1))
|
||||
return 3;
|
||||
|
||||
samples = col;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else
|
||||
z = 0;
|
||||
|
||||
if (z != 8) {
|
||||
if (!bs.read(z + 1))
|
||||
return false;
|
||||
|
||||
if (_rk) {
|
||||
samples = (bs.value() >> (32 - _rk)) + cnt;
|
||||
if (!bs.read(_rk))
|
||||
return false;
|
||||
} else
|
||||
samples = cnt;
|
||||
|
||||
_lrk = _rk + 1;
|
||||
if (_runIndex != 0) {
|
||||
_runIndex--;
|
||||
_rk = J[_runIndex];
|
||||
_rg = 1U << _rk;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!bs.read(8))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool JLS::decodeError(BitStream &bs, quint8 limit, quint8 k, uint &MErrval)
|
||||
{
|
||||
quint8 cnt = 0;
|
||||
MErrval = 0;
|
||||
|
||||
while ((int)bs.value() >= 0) {
|
||||
cnt = Z[bs.value() >> 0x18];
|
||||
MErrval += cnt;
|
||||
if (bs.value() >> 0x18 != 0)
|
||||
break;
|
||||
|
||||
if (!bs.read(8))
|
||||
return false;
|
||||
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
if (!bs.read(cnt + 1))
|
||||
return false;
|
||||
|
||||
if (MErrval < limit) {
|
||||
if (k != 0) {
|
||||
MErrval = (bs.value() >> (0x20 - k)) + (MErrval << k);
|
||||
if (!bs.read(k))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MErrval = (bs.value() >> (0x20 - _qbpp)) + 1;
|
||||
if (!bs.read(_qbpp))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JLS::readLine(BitStream &bs)
|
||||
{
|
||||
quint8 ictx, rctx;
|
||||
quint8 k;
|
||||
uint MErrval;
|
||||
int Errval;
|
||||
int Rx;
|
||||
int Ra = _last[1];
|
||||
int Rb = _last[1];
|
||||
int Rc = _last[0];
|
||||
uint col = 1;
|
||||
|
||||
*_current = _last[1];
|
||||
|
||||
do {
|
||||
if (abs(Rb - Ra) > _near) {
|
||||
int Px = Ra + Rb - Rc;
|
||||
if (Px < 0)
|
||||
Px = 0;
|
||||
else if (Px > _maxval)
|
||||
Px = _maxval;
|
||||
|
||||
for (k = 0; _n[1] << k < _a[1]; k++)
|
||||
;
|
||||
|
||||
if (!decodeError(bs, _limit, k, MErrval))
|
||||
return false;
|
||||
|
||||
int mes, meh;
|
||||
if (MErrval & 1) {
|
||||
meh = (MErrval + 1) >> 1;
|
||||
mes = -meh;
|
||||
} else {
|
||||
meh = MErrval >> 1;
|
||||
mes = meh;
|
||||
}
|
||||
if ((_near == 0) && (k == 0) && (_b[1] * 2 <= -_n[1])) {
|
||||
meh = mes + 1;
|
||||
mes = -mes - 1;
|
||||
if (MErrval & 1)
|
||||
meh = mes;
|
||||
} else
|
||||
mes = mes * (_near * 2 + 1);
|
||||
|
||||
Errval = (Ra < Rb) ? mes : -mes;
|
||||
Rx = Px + Errval;
|
||||
|
||||
if (Rx < -_near)
|
||||
Rx += (_near * 2 + 1) * _range;
|
||||
else if (Rx > _maxval + _near)
|
||||
Rx -= (_near * 2 + 1) * _range;
|
||||
|
||||
if (Rx < 0)
|
||||
Rx = 0;
|
||||
if (Rx > _maxval)
|
||||
Rx = _maxval;
|
||||
|
||||
_a[1] = _a[1] + meh;
|
||||
_b[1] = _b[1] + mes;
|
||||
if (_n[1] == 0x40) {
|
||||
_a[1] = _a[1] >> 1;
|
||||
if (_b[1] >= 0)
|
||||
_b[1] = _b[1] >> 1;
|
||||
else
|
||||
_b[1] = -((1 - _b[1]) >> 1);
|
||||
_n[1] = 0x21;
|
||||
} else {
|
||||
_n[1] = _n[1] + 1;
|
||||
}
|
||||
|
||||
if (_b[1] <= -_n[1]) {
|
||||
_b[1] = _b[1] + _n[1];
|
||||
if (_b[1] <= -_n[1])
|
||||
_b[1] = 1 - _n[1];
|
||||
} else if (_b[1] > 0)
|
||||
_b[1] = ((_b[1] - _n[1]) >> 0xf) & (_b[1] - _n[1]);
|
||||
|
||||
Rc = Rb;
|
||||
Rb = _last[col + 1];
|
||||
} else {
|
||||
quint16 samples;
|
||||
if (!processRunMode(bs, _w - col + 1, samples))
|
||||
return false;
|
||||
|
||||
if (samples != 0) {
|
||||
for (int i = 0; i < samples; i++) {
|
||||
if (col > _w)
|
||||
return false;
|
||||
_current[col] = Ra;
|
||||
col++;
|
||||
}
|
||||
|
||||
if (col > _w)
|
||||
break;
|
||||
|
||||
Rc = _last[col];
|
||||
Rb = _last[col + 1];
|
||||
} else {
|
||||
Rc = Rb;
|
||||
Rb = _last[col + 1];
|
||||
}
|
||||
|
||||
rctx = (abs(Rc - Ra) <= _near);
|
||||
quint16 TEMP = _a[rctx + 2];
|
||||
if (rctx)
|
||||
TEMP += _n[rctx + 2] >> 1;
|
||||
ictx = rctx | 2;
|
||||
|
||||
for (k = 0; _n[rctx + 2] << k < TEMP; k++)
|
||||
;
|
||||
|
||||
if (!decodeError(bs, _limit - _lrk, k, MErrval))
|
||||
return false;
|
||||
|
||||
quint16 s = ((k == 0) && (rctx || MErrval)) ?
|
||||
(_b[ictx] * 2 < _n[ictx]) : 0;
|
||||
|
||||
Errval = MErrval + rctx + s;
|
||||
int evh;
|
||||
if ((Errval & 1) == 0) {
|
||||
Errval = Errval / 2;
|
||||
evh = Errval;
|
||||
} else {
|
||||
Errval = s - ((Errval + 1) >> 1);
|
||||
evh = -Errval;
|
||||
_b[ictx] = _b[ictx] + 1;
|
||||
}
|
||||
|
||||
Errval *= (_near * 2 + 1);
|
||||
if (!rctx) {
|
||||
if (Ra == Rc)
|
||||
return false;
|
||||
|
||||
if (Ra < Rc)
|
||||
Rx = Rc + Errval;
|
||||
else
|
||||
Rx = Rc - Errval;
|
||||
} else
|
||||
Rx = Ra + Errval;
|
||||
|
||||
if (Rx < -_near)
|
||||
Rx += (_near * 2 + 1) * _range;
|
||||
else if (Rx > _maxval + _near)
|
||||
Rx -= (_near * 2 + 1) * _range;
|
||||
|
||||
if (Rx < 0)
|
||||
Rx = 0;
|
||||
if (Rx > _maxval)
|
||||
Rx = _maxval;
|
||||
|
||||
_a[ictx] = _a[ictx] + (evh - rctx);
|
||||
if (_n[ictx] == 0x40) {
|
||||
_a[ictx] = _a[ictx] >> 1;
|
||||
if (_b[ictx] >= 0)
|
||||
_b[ictx] = _b[ictx] >> 1;
|
||||
else
|
||||
_b[ictx] = -((1 - _b[ictx]) >> 1);
|
||||
_n[ictx] = 0x21;
|
||||
} else
|
||||
_n[ictx] = _n[ictx] + 1;
|
||||
}
|
||||
|
||||
_current[col] = Rx;
|
||||
|
||||
Ra = Rx;
|
||||
col = col + 1;
|
||||
} while (col <= _w);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JLS::decode(const SubFile *file, SubFile::Handle &hdl, Matrix<qint16> &img)
|
||||
{
|
||||
BitStream bs(file, hdl);
|
||||
if (!bs.init())
|
||||
return false;
|
||||
|
||||
_w = img.w();
|
||||
_data = QVector<quint16>((_w + 3) * 2);
|
||||
_last = _data.data();
|
||||
_current = _data.data() + (_w + 3);
|
||||
|
||||
_runIndex = 0;
|
||||
_rk = 0;
|
||||
_rg = 1;
|
||||
_lrk = 0;
|
||||
|
||||
quint16 A = max(2, (_range + 32) / 64);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
_a[i] = A;
|
||||
_b[i] = 0;
|
||||
_n[i] = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < img.h(); i++) {
|
||||
if (!readLine(bs))
|
||||
return false;
|
||||
|
||||
memcpy(&img.at(i, 0), _current + 1, _w * sizeof(quint16));
|
||||
|
||||
quint16 *tmp = _last;
|
||||
_last = _current;
|
||||
_current = tmp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
85
src/map/IMG/jls.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef IMG_JLS_H
|
||||
#define IMG_JLS_H
|
||||
|
||||
#include <QVector>
|
||||
#include "bitstream.h"
|
||||
#include "map/matrix.h"
|
||||
|
||||
namespace IMG {
|
||||
|
||||
class JLS
|
||||
{
|
||||
public:
|
||||
JLS(quint16 diff, quint16 factor);
|
||||
|
||||
bool decode(const SubFile *file, SubFile::Handle &hdl, Matrix<qint16> &img);
|
||||
|
||||
private:
|
||||
class BitStream
|
||||
{
|
||||
public:
|
||||
BitStream(const SubFile *file, SubFile::Handle &hdl)
|
||||
: _file(file), _hdl(hdl) {}
|
||||
|
||||
bool init()
|
||||
{
|
||||
if (!_file->readVUInt32SW(_hdl, 4, _value))
|
||||
return false;
|
||||
_shift = (quint8)-8;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(quint8 bits)
|
||||
{
|
||||
quint8 data;
|
||||
|
||||
_value <<= bits;
|
||||
_shift += bits;
|
||||
|
||||
while (-1 < (qint8)_shift) {
|
||||
if (!_file->readByte(_hdl, &data))
|
||||
return false;
|
||||
|
||||
_value |= (quint32)data << _shift;
|
||||
_shift -= 8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
quint32 value() const {return _value;}
|
||||
|
||||
private:
|
||||
const SubFile *_file;
|
||||
SubFile::Handle &_hdl;
|
||||
quint32 _value;
|
||||
quint8 _shift;
|
||||
};
|
||||
|
||||
bool readLine(BitStream &bs);
|
||||
bool processRunMode(BitStream &bs, quint16 col, quint16 &samples);
|
||||
bool decodeError(BitStream &bs, quint8 limit, quint8 k, uint &MErrval);
|
||||
|
||||
quint16 _maxval;
|
||||
quint16 _near;
|
||||
quint16 _range;
|
||||
quint8 _qbpp;
|
||||
quint8 _limit;
|
||||
|
||||
quint8 _runIndex;
|
||||
quint8 _rk;
|
||||
quint16 _rg;
|
||||
quint16 _n[4];
|
||||
quint16 _a[4];
|
||||
qint16 _b[4];
|
||||
quint8 _lrk;
|
||||
|
||||
quint16 _w;
|
||||
QVector<quint16> _data;
|
||||
quint16 *_current;
|
||||
quint16 *_last;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // IMG_JLS_H
|
@ -5,55 +5,36 @@
|
||||
using namespace Garmin;
|
||||
using namespace IMG;
|
||||
|
||||
enum Charset {Normal, Symbol, Special};
|
||||
|
||||
static quint8 NORMAL_CHARS[] = {
|
||||
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', '~', '~', '~', ' ', ' ',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||
};
|
||||
|
||||
static quint8 SYMBOL_CHARS[] = {
|
||||
'@', '!', '"', '#', '$', '%', '&', '\'',
|
||||
'(', ')', '*', '+', ',', '-', '.', '/',
|
||||
'~', '~', '~', '~', '~', '~', '~', '~',
|
||||
'~', '~', ':', ';', '<', '=', '>', '?',
|
||||
'~', '~', '~', '~', '~', '~', '~', '~',
|
||||
'~', '~', '~', '[', '\\', ']', '^', '_'
|
||||
};
|
||||
|
||||
static quint8 SPECIAL_CHARS[] = {
|
||||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
||||
'x', 'y', 'z', '~', '~', '~', '~', '~',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||
};
|
||||
|
||||
static bool isAllUpperCase(const QString &str)
|
||||
{
|
||||
if (str.isEmpty())
|
||||
return false;
|
||||
for (int i = 0; i < str.size(); i++)
|
||||
if (str.at(i).isLetter() && !(str.at(i).isUpper()
|
||||
|| str.at(i) == QChar(0x00DF)))
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
QChar c(str.at(i));
|
||||
|
||||
if (c.isLetter() && !(c.isUpper() || c == QChar(0x00DF)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static QString capitalized(const QString &str)
|
||||
{
|
||||
QString ret(str);
|
||||
for (int i = 0; i < str.size(); i++)
|
||||
if (i && !str.at(i-1).isSpace())
|
||||
ret[i] = str.at(i).toLower();
|
||||
else
|
||||
ret[i] = str.at(i);
|
||||
QString ret;
|
||||
|
||||
ret.resize(str.size());
|
||||
|
||||
if (!str.isEmpty())
|
||||
ret[0] = str.at(0);
|
||||
|
||||
for (int i = 1; i < str.size(); i++) {
|
||||
QChar last(str.at(i-1));
|
||||
QChar c(str.at(i));
|
||||
|
||||
ret[i] = (last.isSpace() || last == '(') ? c : c.toLower();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -65,11 +46,10 @@ static QByteArray ft2m(const QByteArray &str)
|
||||
return ok ? QByteArray::number(qRound(number * 0.3048)) : str;
|
||||
}
|
||||
|
||||
|
||||
LBLFile::~LBLFile()
|
||||
{
|
||||
delete _huffmanText;
|
||||
delete[] _table;
|
||||
delete[] _rasters;
|
||||
}
|
||||
|
||||
bool LBLFile::load(Handle &hdl, const RGNFile *rgn, Handle &rgnHdl)
|
||||
@ -93,10 +73,10 @@ bool LBLFile::load(Handle &hdl, const RGNFile *rgn, Handle &rgnHdl)
|
||||
return false;
|
||||
|
||||
if (size && recordSize) {
|
||||
_table = new quint32[size / recordSize];
|
||||
_table.resize(size / recordSize);
|
||||
if (!seek(hdl, offset))
|
||||
return false;
|
||||
for (quint32 i = 0; i < size / recordSize; i++) {
|
||||
for (int i = 0; i < _table.size(); i++) {
|
||||
if (!readVUInt32(hdl, recordSize, _table[i]))
|
||||
return false;
|
||||
}
|
||||
@ -129,12 +109,10 @@ bool LBLFile::load(Handle &hdl, const RGNFile *rgn, Handle &rgnHdl)
|
||||
|
||||
void LBLFile::clear()
|
||||
{
|
||||
_table = QVector<quint32>();
|
||||
_rasters = QVector<Image>();
|
||||
delete _huffmanText;
|
||||
delete[] _table;
|
||||
delete[] _rasters;
|
||||
_huffmanText = 0;
|
||||
_table = 0;
|
||||
_rasters = 0;
|
||||
}
|
||||
|
||||
Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize,
|
||||
@ -183,10 +161,35 @@ Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize,
|
||||
Label LBLFile::label6b(const SubFile *file, Handle &fileHdl, quint32 size,
|
||||
bool capitalize, bool convert) const
|
||||
{
|
||||
enum Charset {Normal, Symbol, Special};
|
||||
static const quint8 NORMAL_CHARS[] = {
|
||||
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', '~', '~', '~', ' ', ' ',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||
};
|
||||
static const quint8 SYMBOL_CHARS[] = {
|
||||
'@', '!', '"', '#', '$', '%', '&', '\'',
|
||||
'(', ')', '*', '+', ',', '-', '.', '/',
|
||||
'~', '~', '~', '~', '~', '~', '~', '~',
|
||||
'~', '~', ':', ';', '<', '=', '>', '?',
|
||||
'~', '~', '~', '~', '~', '~', '~', '~',
|
||||
'~', '~', '~', '[', '\\', ']', '^', '_'
|
||||
};
|
||||
static const quint8 SPECIAL_CHARS[] = {
|
||||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
||||
'x', 'y', 'z', '~', '~', '~', '~', '~',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||
};
|
||||
Shield::Type shieldType = Shield::None;
|
||||
QByteArray label, shieldLabel;
|
||||
QByteArray *bap = &label;
|
||||
Charset curCharSet = Normal;
|
||||
Charset charset = Normal;
|
||||
quint8 b1, b2, b3;
|
||||
int split = -1;
|
||||
|
||||
@ -198,7 +201,7 @@ Label LBLFile::label6b(const SubFile *file, Handle &fileHdl, quint32 size,
|
||||
int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F};
|
||||
|
||||
for (int cpt = 0; cpt < 4; cpt++) {
|
||||
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
|
||||
if (c[cpt] > 0x2f || (charset == Normal && c[cpt] == 0x1d)) {
|
||||
if (split >= 0)
|
||||
label = label.left(split) + ft2m(label.mid(split));
|
||||
else if (convert)
|
||||
@ -207,12 +210,12 @@ Label LBLFile::label6b(const SubFile *file, Handle &fileHdl, quint32 size,
|
||||
return Label(capitalize && isAllUpperCase(text)
|
||||
? capitalized(text) : text, Shield(shieldType, shieldLabel));
|
||||
}
|
||||
switch (curCharSet) {
|
||||
switch (charset) {
|
||||
case Normal:
|
||||
if (c[cpt] == 0x1c)
|
||||
curCharSet = Symbol;
|
||||
charset = Symbol;
|
||||
else if (c[cpt] == 0x1b)
|
||||
curCharSet = Special;
|
||||
charset = Special;
|
||||
else if (c[cpt] >= 0x1e && c[cpt] <= 0x1f) {
|
||||
if (bap == &shieldLabel)
|
||||
bap = &label;
|
||||
@ -233,11 +236,11 @@ Label LBLFile::label6b(const SubFile *file, Handle &fileHdl, quint32 size,
|
||||
break;
|
||||
case Symbol:
|
||||
bap->append(SYMBOL_CHARS[c[cpt]]);
|
||||
curCharSet = Normal;
|
||||
charset = Normal;
|
||||
break;
|
||||
case Special:
|
||||
bap->append(SPECIAL_CHARS[c[cpt]]);
|
||||
curCharSet = Normal;
|
||||
charset = Normal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -266,42 +269,41 @@ Label LBLFile::label8b(const SubFile *file, Handle &fileHdl, quint32 size,
|
||||
Label LBLFile::labelHuffman(Handle &hdl, const SubFile *file, Handle &fileHdl,
|
||||
quint32 size, bool capitalize, bool convert)
|
||||
{
|
||||
QVector<quint8> tpl;
|
||||
|
||||
if (!_huffmanText->decode(file, fileHdl, size, tpl))
|
||||
return Label();
|
||||
if (!_table.size())
|
||||
return str2label(tpl, capitalize, convert);
|
||||
|
||||
|
||||
QVector<quint8> str;
|
||||
|
||||
if (!_huffmanText->decode(file, fileHdl, size, str))
|
||||
return Label();
|
||||
if (!_table)
|
||||
return str2label(str, capitalize, convert);
|
||||
for (int i = 0; i < tpl.size(); i++) {
|
||||
quint8 c(tpl.at(i));
|
||||
quint32 val = (c < _table.size()) ? _table.at(c) : 0;
|
||||
|
||||
|
||||
QVector<quint8> str2;
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
quint32 val = _table[str.at(i)];
|
||||
if (val) {
|
||||
quint32 off = _base.offset + ((val & 0x7fffff) << _shift);
|
||||
if (!seek(hdl, off))
|
||||
if (str.size() && str.back() == '\0')
|
||||
str.back() = ' ';
|
||||
else if (str.size())
|
||||
str.append(' ');
|
||||
|
||||
quint32 offset = _base.offset + ((val & 0x7fffff) << _shift);
|
||||
quint32 limit = _base.offset + _base.size - offset;
|
||||
if (!seek(hdl, offset))
|
||||
return Label();
|
||||
|
||||
if (str2.size() && str2.back() == '\0')
|
||||
str2[str2.size() - 1] = ' ';
|
||||
else if (str2.size())
|
||||
str2.append(' ');
|
||||
|
||||
if (!_huffmanText->decode(this, hdl, _base.offset + _base.size - off,
|
||||
str2))
|
||||
if (!_huffmanText->decode(this, hdl, limit, str))
|
||||
return Label();
|
||||
} else {
|
||||
if (str.at(i) == 7) {
|
||||
str2.append(0);
|
||||
break;
|
||||
}
|
||||
if (str2.size() && str2.back() == '\0')
|
||||
str2[str2.size() - 1] = ' ';
|
||||
str2.append(str.at(i));
|
||||
if (str.size() && str.back() == '\0')
|
||||
str.back() = ' ';
|
||||
|
||||
str.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return str2label(str2, capitalize, convert);
|
||||
return str2label(str, capitalize, convert);
|
||||
}
|
||||
|
||||
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize,
|
||||
@ -347,15 +349,15 @@ bool LBLFile::loadRasterTable(Handle &hdl, quint32 offset, quint32 size,
|
||||
quint32 recordSize)
|
||||
{
|
||||
quint32 prev, cur;
|
||||
quint32 imgCount = size / recordSize;
|
||||
|
||||
_imgCount = size / recordSize;
|
||||
_imgIdSize = byteSize(_imgCount - 1);
|
||||
_rasters = new Image[_imgCount];
|
||||
_imgIdSize = byteSize(imgCount - 1);
|
||||
_rasters.resize(imgCount);
|
||||
|
||||
if (!(seek(hdl, offset) && readVUInt32(hdl, recordSize, prev)))
|
||||
return false;
|
||||
|
||||
for (quint32 i = 1; i < _imgCount; i++) {
|
||||
for (quint32 i = 1; i < imgCount; i++) {
|
||||
if (!readVUInt32(hdl, recordSize, cur))
|
||||
return false;
|
||||
|
||||
@ -365,8 +367,8 @@ bool LBLFile::loadRasterTable(Handle &hdl, quint32 offset, quint32 size,
|
||||
prev = cur;
|
||||
}
|
||||
|
||||
_rasters[_imgCount-1].offset = prev;
|
||||
_rasters[_imgCount-1].size = _img.size - prev;
|
||||
_rasters[imgCount-1].offset = prev;
|
||||
_rasters[imgCount-1].size = _img.size - prev;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -375,14 +377,14 @@ QPixmap LBLFile::image(Handle &hdl, quint32 id) const
|
||||
{
|
||||
QPixmap pm;
|
||||
|
||||
if (id >= _imgCount)
|
||||
if ((int)id >= _rasters.size())
|
||||
return pm;
|
||||
|
||||
if (!seek(hdl, _img.offset + _rasters[id].offset))
|
||||
if (!seek(hdl, _img.offset + _rasters.at(id).offset))
|
||||
return pm;
|
||||
QByteArray ba;
|
||||
ba.resize(_rasters[id].size);
|
||||
if (!read(hdl, ba.data(), _rasters[id].size))
|
||||
ba.resize(_rasters.at(id).size);
|
||||
if (!read(hdl, ba.data(), ba.size()))
|
||||
return pm;
|
||||
|
||||
pm.loadFromData(ba, "jpeg");
|
||||
|
@ -16,14 +16,14 @@ class LBLFile : public SubFile
|
||||
{
|
||||
public:
|
||||
LBLFile(const IMGData *img)
|
||||
: SubFile(img), _huffmanText(0), _table(0), _rasters(0), _imgIdSize(0),
|
||||
_poiShift(0), _shift(0), _encoding(0) {}
|
||||
: SubFile(img), _huffmanText(0), _imgIdSize(0), _poiShift(0), _shift(0),
|
||||
_encoding(0) {}
|
||||
LBLFile(const QString *path)
|
||||
: SubFile(path), _huffmanText(0), _table(0), _rasters(0), _imgIdSize(0),
|
||||
_poiShift(0), _shift(0), _encoding(0) {}
|
||||
: SubFile(path), _huffmanText(0), _imgIdSize(0), _poiShift(0), _shift(0),
|
||||
_encoding(0) {}
|
||||
LBLFile(const SubFile *gmp, quint32 offset)
|
||||
: SubFile(gmp, offset), _huffmanText(0), _table(0), _rasters(0),
|
||||
_imgIdSize(0), _poiShift(0), _shift(0), _encoding(0) {}
|
||||
: SubFile(gmp, offset), _huffmanText(0), _imgIdSize(0), _poiShift(0),
|
||||
_shift(0), _encoding(0) {}
|
||||
~LBLFile();
|
||||
|
||||
bool load(Handle &hdl, const RGNFile *rgn, Handle &rgnHdl);
|
||||
@ -55,11 +55,10 @@ private:
|
||||
quint32 recordSize);
|
||||
|
||||
HuffmanText *_huffmanText;
|
||||
quint32 *_table;
|
||||
Image *_rasters;
|
||||
QVector<Image> _rasters;
|
||||
QVector<quint32> _table;
|
||||
TextCodec _codec;
|
||||
Section _base, _poi, _img;
|
||||
quint32 _imgCount;
|
||||
quint8 _imgIdSize;
|
||||
quint8 _poiShift;
|
||||
quint8 _shift;
|
||||
|
@ -7,29 +7,37 @@
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
#define CACHED_SUBDIVS_COUNT 2048 // ~32MB for both caches together
|
||||
#define CACHED_SUBDIVS_COUNT 2048 // ~32MB for both caches together
|
||||
#define CACHED_DEMTILES_COUNT 1024 // ~32MB
|
||||
|
||||
bool MapData::polyCb(VectorTile *tile, void *context)
|
||||
{
|
||||
PolyCTX *ctx = (PolyCTX*)context;
|
||||
tile->polys(ctx->rect, ctx->zoom, ctx->polygons, ctx->lines,
|
||||
ctx->polyCache, ctx->lock);
|
||||
ctx->cache, ctx->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapData::pointCb(VectorTile *tile, void *context)
|
||||
{
|
||||
PointCTX *ctx = (PointCTX*)context;
|
||||
tile->points(ctx->rect, ctx->zoom, ctx->points, ctx->pointCache, ctx->lock);
|
||||
tile->points(ctx->rect, ctx->zoom, ctx->points, ctx->cache, ctx->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapData::elevationCb(VectorTile *tile, void *context)
|
||||
{
|
||||
ElevationCTX *ctx = (ElevationCTX*)context;
|
||||
tile->elevations(ctx->rect, ctx->zoom, ctx->elevations, ctx->cache, ctx->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
MapData::MapData(const QString &fileName)
|
||||
: _fileName(fileName), _typ(0), _style(0), _valid(false)
|
||||
: _fileName(fileName), _typ(0), _style(0), _hasDEM(false), _valid(false)
|
||||
{
|
||||
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);
|
||||
_pointCache.setMaxCost(CACHED_SUBDIVS_COUNT);
|
||||
_demCache.setMaxCost(CACHED_DEMTILES_COUNT);
|
||||
}
|
||||
|
||||
MapData::~MapData()
|
||||
@ -69,6 +77,19 @@ void MapData::points(const RectC &rect, int bits, QList<Point> *points)
|
||||
_tileTree.Search(min, max, pointCb, &ctx);
|
||||
}
|
||||
|
||||
void MapData::elevations(const RectC &rect, int bits, QList<Elevation> *elevations)
|
||||
{
|
||||
ElevationCTX ctx(rect, zoom(bits), elevations, &_demCache, &_demLock);
|
||||
double min[2], max[2];
|
||||
|
||||
min[0] = rect.left();
|
||||
min[1] = rect.bottom();
|
||||
max[0] = rect.right();
|
||||
max[1] = rect.top();
|
||||
|
||||
_tileTree.Search(min, max, elevationCb, &ctx);
|
||||
}
|
||||
|
||||
void MapData::load(qreal ratio)
|
||||
{
|
||||
Q_ASSERT(!_style);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "common/rtree.h"
|
||||
#include "common/range.h"
|
||||
#include "common/hash.h"
|
||||
#include "map/matrix.h"
|
||||
#include "label.h"
|
||||
#include "raster.h"
|
||||
#include "zoom.h"
|
||||
@ -20,6 +21,7 @@ class Style;
|
||||
class SubDiv;
|
||||
class SubFile;
|
||||
class VectorTile;
|
||||
class DEMTile;
|
||||
|
||||
class MapData
|
||||
{
|
||||
@ -55,6 +57,13 @@ public:
|
||||
{return id < other.id;}
|
||||
};
|
||||
|
||||
struct Elevation {
|
||||
Matrix<qint16> m;
|
||||
RectC rect;
|
||||
double xr;
|
||||
double yr;
|
||||
};
|
||||
|
||||
MapData(const QString &fileName);
|
||||
virtual ~MapData();
|
||||
|
||||
@ -65,10 +74,13 @@ public:
|
||||
void polys(const RectC &rect, int bits, QList<Poly> *polygons,
|
||||
QList<Poly> *lines);
|
||||
void points(const RectC &rect, int bits, QList<Point> *points);
|
||||
void elevations(const RectC &rect, int bits, QList<Elevation> *elevations);
|
||||
|
||||
void load(qreal ratio);
|
||||
void clear();
|
||||
|
||||
bool hasDEM() const {return _hasDEM;}
|
||||
|
||||
const QString &fileName() const {return _fileName;}
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
@ -87,6 +99,7 @@ protected:
|
||||
TileTree _tileTree;
|
||||
QList<Zoom> _zooms;
|
||||
Range _zoomLevels;
|
||||
bool _hasDEM;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
@ -103,34 +116,48 @@ private:
|
||||
|
||||
typedef QCache<const SubDiv*, Polys> PolyCache;
|
||||
typedef QCache<const SubDiv*, QList<Point> > PointCache;
|
||||
typedef QCache<const DEMTile*, Elevation> ElevationCache;
|
||||
|
||||
struct PolyCTX
|
||||
{
|
||||
PolyCTX(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
|
||||
PolyCache *polyCache, QMutex *lock)
|
||||
PolyCache *cache, QMutex *lock)
|
||||
: rect(rect), zoom(zoom), polygons(polygons), lines(lines),
|
||||
polyCache(polyCache), lock(lock) {}
|
||||
cache(cache), lock(lock) {}
|
||||
|
||||
const RectC ▭
|
||||
const Zoom &zoom;
|
||||
QList<MapData::Poly> *polygons;
|
||||
QList<MapData::Poly> *lines;
|
||||
PolyCache *polyCache;
|
||||
PolyCache *cache;
|
||||
QMutex *lock;
|
||||
};
|
||||
|
||||
struct PointCTX
|
||||
{
|
||||
PointCTX(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Point> *points, PointCache *pointCache, QMutex *lock)
|
||||
: rect(rect), zoom(zoom), points(points), pointCache(pointCache),
|
||||
lock(lock) {}
|
||||
QList<MapData::Point> *points, PointCache *cache, QMutex *lock)
|
||||
: rect(rect), zoom(zoom), points(points), cache(cache), lock(lock) {}
|
||||
|
||||
const RectC ▭
|
||||
const Zoom &zoom;
|
||||
QList<MapData::Point> *points;
|
||||
PointCache *pointCache;
|
||||
PointCache *cache;
|
||||
QMutex *lock;
|
||||
};
|
||||
|
||||
struct ElevationCTX
|
||||
{
|
||||
ElevationCTX(const RectC &rect, const Zoom &zoom,
|
||||
QList<Elevation> *elevations, ElevationCache *cache, QMutex *lock)
|
||||
: rect(rect), zoom(zoom), elevations(elevations), cache(cache),
|
||||
lock(lock) {}
|
||||
|
||||
const RectC ▭
|
||||
const Zoom &zoom;
|
||||
QList<Elevation> *elevations;
|
||||
ElevationCache *cache;
|
||||
QMutex *lock;
|
||||
};
|
||||
|
||||
@ -138,10 +165,12 @@ private:
|
||||
|
||||
static bool polyCb(VectorTile *tile, void *context);
|
||||
static bool pointCb(VectorTile *tile, void *context);
|
||||
static bool elevationCb(VectorTile *tile, void *context);
|
||||
|
||||
PolyCache _polyCache;
|
||||
PointCache _pointCache;
|
||||
QMutex _lock;
|
||||
ElevationCache _demCache;
|
||||
QMutex _lock, _demLock;
|
||||
|
||||
friend class VectorTile;
|
||||
};
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include "map/bitmapline.h"
|
||||
#include "map/rectd.h"
|
||||
#include "map/hillshading.h"
|
||||
#include "map/filter.h"
|
||||
#include "style.h"
|
||||
#include "lblfile.h"
|
||||
#include "dem.h"
|
||||
#include "rastertile.h"
|
||||
|
||||
using namespace IMG;
|
||||
@ -27,6 +29,8 @@ using namespace IMG;
|
||||
#define ROAD 0
|
||||
#define WATER 1
|
||||
|
||||
#define BLUR_RADIUS 3
|
||||
|
||||
static const QColor textColor(Qt::black);
|
||||
static const QColor haloColor(Qt::white);
|
||||
static const QColor shieldColor(Qt::white);
|
||||
@ -107,7 +111,8 @@ static bool rectNearPolygon(const QPolygonF &polygon, const QRectF &rect)
|
||||
|| polygon.containsPoint(rect.bottomRight(), Qt::OddEvenFill)));
|
||||
}
|
||||
|
||||
const QFont *RasterTile::poiFont(Style::FontSize size, int zoom, bool extended)
|
||||
const QFont *RasterTile::poiFont(Style::FontSize size, int zoom,
|
||||
bool extended) const
|
||||
{
|
||||
if (zoom > 25)
|
||||
size = Style::Normal;
|
||||
@ -122,7 +127,7 @@ const QFont *RasterTile::poiFont(Style::FontSize size, int zoom, bool extended)
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::ll2xy(QList<MapData::Poly> &polys)
|
||||
void RasterTile::ll2xy(QList<MapData::Poly> &polys) const
|
||||
{
|
||||
for (int i = 0; i < polys.size(); i++) {
|
||||
MapData::Poly &poly = polys[i];
|
||||
@ -133,7 +138,7 @@ void RasterTile::ll2xy(QList<MapData::Poly> &polys)
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::ll2xy(QList<MapData::Point> &points)
|
||||
void RasterTile::ll2xy(QList<MapData::Point> &points) const
|
||||
{
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
QPointF p(ll2xy(points.at(i).coordinates));
|
||||
@ -142,7 +147,7 @@ void RasterTile::ll2xy(QList<MapData::Point> &points)
|
||||
}
|
||||
|
||||
void RasterTile::drawPolygons(QPainter *painter,
|
||||
const QList<MapData::Poly> &polygons)
|
||||
const QList<MapData::Poly> &polygons) const
|
||||
{
|
||||
QCache<const LBLFile *, SubFile::Handle> hc(16);
|
||||
|
||||
@ -177,7 +182,9 @@ void RasterTile::drawPolygons(QPainter *painter,
|
||||
|
||||
//painter->setPen(Qt::blue);
|
||||
//painter->setBrush(Qt::NoBrush);
|
||||
//painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
//painter->drawRect(QRectF(tl, br));
|
||||
//painter->setRenderHint(QPainter::Antialiasing);
|
||||
} else {
|
||||
const Style::Polygon &style = _data->style()->polygon(poly.type);
|
||||
|
||||
@ -189,7 +196,8 @@ void RasterTile::drawPolygons(QPainter *painter,
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::drawLines(QPainter *painter, const QList<MapData::Poly> &lines)
|
||||
void RasterTile::drawLines(QPainter *painter,
|
||||
const QList<MapData::Poly> &lines) const
|
||||
{
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
@ -218,7 +226,7 @@ void RasterTile::drawLines(QPainter *painter, const QList<MapData::Poly> &lines)
|
||||
}
|
||||
|
||||
void RasterTile::drawTextItems(QPainter *painter,
|
||||
const QList<TextItem*> &textItems)
|
||||
const QList<TextItem*> &textItems) const
|
||||
{
|
||||
for (int i = 0; i < textItems.size(); i++)
|
||||
textItems.at(i)->paint(painter);
|
||||
@ -446,30 +454,58 @@ void RasterTile::fetchData(QList<MapData::Poly> &polygons,
|
||||
_data->points(pointRectD.toRectC(_proj, 20), _zoom, &points);
|
||||
}
|
||||
|
||||
Matrix RasterTile::elevation() const
|
||||
MatrixD RasterTile::elevation(int extend) const
|
||||
{
|
||||
Matrix m(_rect.height() + 2, _rect.width() + 2);
|
||||
|
||||
int left = _rect.left() - 1;
|
||||
int right = _rect.right() + 1;
|
||||
int top = _rect.top() - 1;
|
||||
int bottom = _rect.bottom() + 1;
|
||||
|
||||
MatrixD m(_rect.height() + 2 * extend, _rect.width() + 2 * extend);
|
||||
QVector<Coordinates> ll;
|
||||
|
||||
int left = _rect.left() - extend;
|
||||
int right = _rect.right() + extend;
|
||||
int top = _rect.top() - extend;
|
||||
int bottom = _rect.bottom() + extend;
|
||||
|
||||
ll.reserve(m.w() * m.h());
|
||||
for (int y = top; y <= bottom; y++) {
|
||||
for (int y = top; y <= bottom; y++)
|
||||
for (int x = left; x <= right; x++)
|
||||
ll.append(xy2ll(QPointF(x, y)));
|
||||
|
||||
if (_data->hasDEM()) {
|
||||
RectC rect;
|
||||
QList<MapData::Elevation> tiles;
|
||||
DEMTRee tree;
|
||||
|
||||
for (int i = 0; i < ll.size(); i++)
|
||||
rect = rect.united(ll.at(i));
|
||||
// Extra margin for always including the next DEM tile on the map tile
|
||||
// edges (the DEM tile resolution is usally < 5% of the map tile)
|
||||
double delta = rect.width() / 16;
|
||||
rect = rect.united(Coordinates(rect.right() + delta,
|
||||
rect.bottom() - delta));
|
||||
|
||||
_data->elevations(rect, _zoom, &tiles);
|
||||
|
||||
DEM::buildTree(tiles, tree);
|
||||
for (int i = 0; i < ll.size(); i++)
|
||||
DEM::searchTree(tree, ll.at(i), m.at(i));
|
||||
} else {
|
||||
::DEM::lock();
|
||||
for (int i = 0; i < ll.size(); i++)
|
||||
m.at(i) = ::DEM::elevation(ll.at(i));
|
||||
::DEM::unlock();
|
||||
}
|
||||
|
||||
DEM::lock();
|
||||
for (int i = 0; i < ll.size(); i++)
|
||||
m.m(i) = DEM::elevation(ll.at(i));
|
||||
DEM::unlock();
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void RasterTile::drawHillShading(QPainter *painter) const
|
||||
{
|
||||
if (_hillShading && _zoom >= 18 && _zoom <= 24) {
|
||||
MatrixD dem(Filter::blur(elevation(BLUR_RADIUS + 1), BLUR_RADIUS));
|
||||
QImage img(HillShading::render(dem, BLUR_RADIUS + 1));
|
||||
painter->drawImage(_rect.x(), _rect.y(), img);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::render()
|
||||
{
|
||||
QImage img(_rect.width() * _ratio, _rect.height() * _ratio,
|
||||
@ -501,14 +537,14 @@ void RasterTile::render()
|
||||
painter.translate(-_rect.x(), -_rect.y());
|
||||
|
||||
drawPolygons(&painter, polygons);
|
||||
if (_hillShading && _zoom >= 18 && _zoom <= 24)
|
||||
painter.drawImage(_rect.x(), _rect.y(), HillShading::render(elevation()));
|
||||
drawHillShading(&painter);
|
||||
drawLines(&painter, lines);
|
||||
drawTextItems(&painter, textItems);
|
||||
|
||||
qDeleteAll(textItems);
|
||||
|
||||
//painter.setPen(Qt::red);
|
||||
//painter.setBrush(Qt::NoBrush);
|
||||
//painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
//painter.drawRect(_rect);
|
||||
|
||||
|
@ -30,18 +30,36 @@ public:
|
||||
void render();
|
||||
|
||||
private:
|
||||
typedef RTree<const MapData::Elevation*, double, 2> DEMTRee;
|
||||
|
||||
struct ElevationCTX {
|
||||
ElevationCTX(const DEMTRee &tree, const Coordinates &c, double &ele)
|
||||
: tree(tree), c(c), ele(ele) {}
|
||||
|
||||
const DEMTRee &tree;
|
||||
const Coordinates &c;
|
||||
double &ele;
|
||||
};
|
||||
struct EdgeCTX {
|
||||
EdgeCTX(const Coordinates &c, double &ele) : c(c), ele(ele) {}
|
||||
|
||||
const Coordinates &c;
|
||||
double &ele;
|
||||
};
|
||||
|
||||
void fetchData(QList<MapData::Poly> &polygons, QList<MapData::Poly> &lines,
|
||||
QList<MapData::Point> &points);
|
||||
QPointF ll2xy(const Coordinates &c) const
|
||||
{return _transform.proj2img(_proj.ll2xy(c));}
|
||||
Coordinates xy2ll(const QPointF &p) const
|
||||
{return _proj.xy2ll(_transform.img2proj(p));}
|
||||
void ll2xy(QList<MapData::Poly> &polys);
|
||||
void ll2xy(QList<MapData::Point> &points);
|
||||
void ll2xy(QList<MapData::Poly> &polys) const;
|
||||
void ll2xy(QList<MapData::Point> &points) const;
|
||||
|
||||
void drawPolygons(QPainter *painter, const QList<MapData::Poly> &polygons);
|
||||
void drawLines(QPainter *painter, const QList<MapData::Poly> &lines);
|
||||
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
|
||||
void drawPolygons(QPainter *painter, const QList<MapData::Poly> &polygons) const;
|
||||
void drawLines(QPainter *painter, const QList<MapData::Poly> &lines) const;
|
||||
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems) const;
|
||||
void drawHillShading(QPainter *painter) const;
|
||||
|
||||
void processPolygons(const QList<MapData::Poly> &polygons,
|
||||
QList<TextItem *> &textItems);
|
||||
@ -55,9 +73,9 @@ private:
|
||||
QList<TextItem*> &textItems, const QImage (&arrows)[2]);
|
||||
|
||||
const QFont *poiFont(Style::FontSize size = Style::Normal,
|
||||
int zoom = -1, bool extended = false);
|
||||
int zoom = -1, bool extended = false) const;
|
||||
|
||||
Matrix elevation() const;
|
||||
MatrixD elevation(int extend) const;
|
||||
|
||||
Projection _proj;
|
||||
Transform _transform;
|
||||
|
@ -187,7 +187,7 @@ void Style::defaultPolygonStyle()
|
||||
_polygons[TYPE(0x14)] = Polygon(QBrush(QColor(0xca, 0xdf, 0xaf)));
|
||||
_polygons[TYPE(0x15)] = Polygon(QBrush(QColor(0xca, 0xdf, 0xaf)));
|
||||
_polygons[TYPE(0x16)] = Polygon(QBrush(QColor(0x9a, 0xc2, 0x69),
|
||||
Qt::Dense5Pattern));
|
||||
Qt::BDiagPattern));
|
||||
_polygons[TYPE(0x17)] = Polygon(QBrush(QColor(0xe4, 0xef, 0xcf)));
|
||||
_polygons[TYPE(0x18)] = Polygon(QBrush(QColor(0xe3, 0xed, 0xc6)));
|
||||
_polygons[TYPE(0x19)] = Polygon(QBrush(QColor(0xe3, 0xed, 0xc6)),
|
||||
@ -195,9 +195,9 @@ void Style::defaultPolygonStyle()
|
||||
_polygons[TYPE(0x1a)] = Polygon(QBrush(QColor(0, 0, 0), Qt::Dense6Pattern),
|
||||
QPen(QColor(0xcd, 0xcc, 0xc4), 1));
|
||||
_polygons[TYPE(0x1e)] = Polygon(QBrush(QColor(0x9a, 0xc2, 0x69),
|
||||
Qt::Dense5Pattern));
|
||||
Qt::BDiagPattern));
|
||||
_polygons[TYPE(0x1f)] = Polygon(QBrush(QColor(0x9a, 0xc2, 0x69),
|
||||
Qt::Dense5Pattern));
|
||||
Qt::BDiagPattern));
|
||||
_polygons[TYPE(0x28)] = Polygon(QBrush(QColor(0x9f, 0xc4, 0xe1)));
|
||||
_polygons[TYPE(0x32)] = Polygon(QBrush(QColor(0x9f, 0xc4, 0xe1)));
|
||||
_polygons[TYPE(0x3c)] = Polygon(QBrush(QColor(0x9f, 0xc4, 0xe1)));
|
||||
|
@ -13,7 +13,7 @@ namespace IMG {
|
||||
class SubFile
|
||||
{
|
||||
public:
|
||||
enum Type {Unknown, TRE, RGN, LBL, NET, NOD, TYP, GMP};
|
||||
enum Type {Unknown, TRE, RGN, LBL, NET, NOD, DEM, TYP, GMP};
|
||||
|
||||
class Handle
|
||||
{
|
||||
@ -76,6 +76,16 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool readInt8(Handle &handle, T &val) const
|
||||
{
|
||||
quint8 b;
|
||||
if (!readByte(handle, &b))
|
||||
return false;
|
||||
val = (qint8)b;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool readUInt16(Handle &handle, T &val) const
|
||||
{
|
||||
@ -90,7 +100,7 @@ public:
|
||||
{
|
||||
if (!readUInt16(handle, (quint16&)val))
|
||||
return false;
|
||||
if((quint16)val > 0x7FFF)
|
||||
if ((quint16)val > 0x7FFF)
|
||||
val = (val & 0x7FFF) - 0x8000;
|
||||
return true;
|
||||
}
|
||||
@ -125,6 +135,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readInt32(Handle &handle, qint32 &val) const
|
||||
{
|
||||
return readUInt32(handle, (quint32&)val);
|
||||
}
|
||||
|
||||
bool readVUInt32SW(Handle &hdl, quint32 bytes, quint32 &val) const
|
||||
{
|
||||
quint8 b;
|
||||
|
@ -18,7 +18,6 @@ static void copyPoints(const RectC &rect, QList<MapData::Point> *src,
|
||||
dst->append(src->at(j));
|
||||
}
|
||||
|
||||
|
||||
SubFile *VectorTile::file(SubFile::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
@ -32,6 +31,8 @@ SubFile *VectorTile::file(SubFile::Type type)
|
||||
return _net;
|
||||
case SubFile::NOD:
|
||||
return _nod;
|
||||
case SubFile::DEM:
|
||||
return _dem;
|
||||
case SubFile::GMP:
|
||||
return _gmp;
|
||||
default:
|
||||
@ -53,11 +54,12 @@ bool VectorTile::init()
|
||||
bool VectorTile::initGMP()
|
||||
{
|
||||
SubFile::Handle hdl(_gmp);
|
||||
quint32 tre, rgn, lbl, net, nod;
|
||||
quint32 tre, rgn, lbl, net, nod, dem;
|
||||
|
||||
if (!(_gmp->seek(hdl, 0x19) && _gmp->readUInt32(hdl, tre)
|
||||
&& _gmp->readUInt32(hdl, rgn) && _gmp->readUInt32(hdl, lbl)
|
||||
&& _gmp->readUInt32(hdl, net) && _gmp->readUInt32(hdl, nod)))
|
||||
&& _gmp->readUInt32(hdl, net) && _gmp->readUInt32(hdl, nod)
|
||||
&& _gmp->readUInt32(hdl, dem)))
|
||||
return false;
|
||||
|
||||
_tre = tre ? new TREFile(_gmp, tre) : 0;
|
||||
@ -65,6 +67,7 @@ bool VectorTile::initGMP()
|
||||
_lbl = lbl ? new LBLFile(_gmp, lbl) : 0;
|
||||
_net = net ? new NETFile(_gmp, net) : 0;
|
||||
_nod = nod ? new NODFile(_gmp, nod) : 0;
|
||||
_dem = dem ? new DEMFile(_gmp, dem) : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -88,6 +91,18 @@ bool VectorTile::load(SubFile::Handle &rgnHdl, SubFile::Handle &lblHdl,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VectorTile::loadDem(SubFile::Handle &hdl)
|
||||
{
|
||||
_demLoaded = -1;
|
||||
|
||||
if (!_dem || !_dem->load(hdl))
|
||||
return false;
|
||||
|
||||
_demLoaded = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VectorTile::clear()
|
||||
{
|
||||
_tre->clear();
|
||||
@ -96,13 +111,16 @@ void VectorTile::clear()
|
||||
_lbl->clear();
|
||||
if (_net)
|
||||
_net->clear();
|
||||
if (_dem)
|
||||
_dem->clear();
|
||||
|
||||
_loaded = 0;
|
||||
_demLoaded = 0;
|
||||
}
|
||||
|
||||
void VectorTile::polys(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
|
||||
MapData::PolyCache *polyCache, QMutex *lock)
|
||||
MapData::PolyCache *cache, QMutex *lock)
|
||||
{
|
||||
SubFile::Handle *rgnHdl = 0, *lblHdl = 0, *netHdl = 0, *nodHdl = 0,
|
||||
*nodHdl2 = 0;
|
||||
@ -131,7 +149,7 @@ void VectorTile::polys(const RectC &rect, const Zoom &zoom,
|
||||
for (int i = 0; i < subdivs.size(); i++) {
|
||||
SubDiv *subdiv = subdivs.at(i);
|
||||
|
||||
MapData::Polys *polys = polyCache->object(subdiv);
|
||||
MapData::Polys *polys = cache->object(subdiv);
|
||||
if (!polys) {
|
||||
quint32 shift = _tre->shift(subdiv->bits());
|
||||
QList<MapData::Poly> p, l;
|
||||
@ -165,7 +183,7 @@ void VectorTile::polys(const RectC &rect, const Zoom &zoom,
|
||||
|
||||
copyPolys(rect, &p, polygons);
|
||||
copyPolys(rect, &l, lines);
|
||||
polyCache->insert(subdiv, new MapData::Polys(p, l));
|
||||
cache->insert(subdiv, new MapData::Polys(p, l));
|
||||
} else {
|
||||
copyPolys(rect, &(polys->polygons), polygons);
|
||||
copyPolys(rect, &(polys->lines), lines);
|
||||
@ -178,8 +196,7 @@ void VectorTile::polys(const RectC &rect, const Zoom &zoom,
|
||||
}
|
||||
|
||||
void VectorTile::points(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Point> *points, QCache<const SubDiv *,
|
||||
QList<MapData::Point> > *pointCache, QMutex *lock)
|
||||
QList<MapData::Point> *points, MapData::PointCache *cache, QMutex *lock)
|
||||
{
|
||||
SubFile::Handle *rgnHdl = 0, *lblHdl = 0;
|
||||
|
||||
@ -207,7 +224,7 @@ void VectorTile::points(const RectC &rect, const Zoom &zoom,
|
||||
for (int i = 0; i < subdivs.size(); i++) {
|
||||
SubDiv *subdiv = subdivs.at(i);
|
||||
|
||||
QList<MapData::Point> *pl = pointCache->object(subdiv);
|
||||
QList<MapData::Point> *pl = cache->object(subdiv);
|
||||
if (!pl) {
|
||||
QList<MapData::Point> p;
|
||||
|
||||
@ -226,7 +243,7 @@ void VectorTile::points(const RectC &rect, const Zoom &zoom,
|
||||
_rgn->extPointObjects(*rgnHdl, subdiv, _lbl, *lblHdl, &p);
|
||||
|
||||
copyPoints(rect, &p, points);
|
||||
pointCache->insert(subdiv, new QList<MapData::Point>(p));
|
||||
cache->insert(subdiv, new QList<MapData::Point>(p));
|
||||
} else
|
||||
copyPoints(rect, pl, points);
|
||||
}
|
||||
@ -236,6 +253,57 @@ void VectorTile::points(const RectC &rect, const Zoom &zoom,
|
||||
delete rgnHdl; delete lblHdl;
|
||||
}
|
||||
|
||||
void VectorTile::elevations(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Elevation> *elevations, MapData::ElevationCache *cache,
|
||||
QMutex *lock)
|
||||
{
|
||||
SubFile::Handle *hdl = 0;
|
||||
|
||||
lock->lock();
|
||||
|
||||
if (_demLoaded < 0) {
|
||||
lock->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_demLoaded) {
|
||||
hdl = new SubFile::Handle(_dem);
|
||||
|
||||
if (!loadDem(*hdl)) {
|
||||
lock->unlock();
|
||||
delete hdl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift the DEM level to get better data then what the map defines for
|
||||
// the given zoom (we prefer rendering quality rather than speed). For
|
||||
// maps with a single level this has no effect.
|
||||
int level = _dem->level(zoom) / 2;
|
||||
QList<const DEMTile*> tiles(_dem->tiles(rect, level));
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
const DEMTile *tile = tiles.at(i);
|
||||
MapData::Elevation *el = cache->object(tile);
|
||||
|
||||
if (!el) {
|
||||
if (!hdl)
|
||||
hdl = new SubFile::Handle(_dem);
|
||||
|
||||
el = _dem->elevations(*hdl, level, tile);
|
||||
if (!el->m.isNull())
|
||||
elevations->append(*el);
|
||||
cache->insert(tile, el);
|
||||
} else {
|
||||
if (!el->m.isNull())
|
||||
elevations->append(*el);
|
||||
}
|
||||
}
|
||||
|
||||
lock->unlock();
|
||||
|
||||
delete hdl;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
QDebug operator<<(QDebug dbg, const VectorTile &tile)
|
||||
{
|
||||
|
@ -6,17 +6,19 @@
|
||||
#include "lblfile.h"
|
||||
#include "netfile.h"
|
||||
#include "nodfile.h"
|
||||
#include "demfile.h"
|
||||
|
||||
namespace IMG {
|
||||
|
||||
class VectorTile {
|
||||
public:
|
||||
VectorTile()
|
||||
: _tre(0), _rgn(0), _lbl(0), _net(0), _nod(0), _gmp(0), _loaded(0) {}
|
||||
: _tre(0), _rgn(0), _lbl(0), _net(0), _nod(0), _dem(0), _gmp(0),
|
||||
_loaded(0), _demLoaded(0) {}
|
||||
~VectorTile()
|
||||
{
|
||||
delete _tre; delete _rgn; delete _lbl; delete _net; delete _nod;
|
||||
delete _gmp;
|
||||
delete _dem; delete _gmp;
|
||||
}
|
||||
|
||||
bool init();
|
||||
@ -24,21 +26,25 @@ public:
|
||||
|
||||
const RectC &bounds() const {return _tre->bounds();}
|
||||
QVector<Zoom> zooms() const {return _tre->zooms();}
|
||||
bool hasDem() const {return _dem != 0;}
|
||||
|
||||
SubFile *file(SubFile::Type type);
|
||||
|
||||
void polys(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
|
||||
MapData::PolyCache *polyCache, QMutex *lock);
|
||||
MapData::PolyCache *cache, QMutex *lock);
|
||||
void points(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Point> *points, QCache<const SubDiv*,
|
||||
QList<MapData::Point> > *pointCache, QMutex *lock);
|
||||
QList<MapData::Point> *points, MapData::PointCache *cache, QMutex *lock);
|
||||
void elevations(const RectC &rect, const Zoom &zoom,
|
||||
QList<MapData::Elevation> *elevations, MapData::ElevationCache *cache,
|
||||
QMutex *lock);
|
||||
|
||||
static bool isTileFile(SubFile::Type type)
|
||||
{
|
||||
return (type == SubFile::TRE || type == SubFile::LBL
|
||||
|| type == SubFile::RGN || type == SubFile::NET
|
||||
|| type == SubFile::NOD || type == SubFile::GMP);
|
||||
|| type == SubFile::NOD || type == SubFile::DEM
|
||||
|| type == SubFile::GMP);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -60,6 +66,9 @@ public:
|
||||
case SubFile::NOD:
|
||||
_nod = new NODFile(container);
|
||||
return _nod;
|
||||
case SubFile::DEM:
|
||||
_dem = new DEMFile(container);
|
||||
return _dem;
|
||||
case SubFile::GMP:
|
||||
_gmp = new SubFile(container);
|
||||
return _gmp;
|
||||
@ -72,15 +81,17 @@ private:
|
||||
bool initGMP();
|
||||
bool load(SubFile::Handle &rgnHdl, SubFile::Handle &lblHdl,
|
||||
SubFile::Handle &netHdl, SubFile::Handle &nodHdl);
|
||||
bool loadDem(SubFile::Handle &demHdl);
|
||||
|
||||
TREFile *_tre;
|
||||
RGNFile *_rgn;
|
||||
LBLFile *_lbl;
|
||||
NETFile *_net;
|
||||
NODFile *_nod;
|
||||
DEMFile *_dem;
|
||||
SubFile *_gmp;
|
||||
|
||||
int _loaded;
|
||||
int _loaded, _demLoaded;
|
||||
};
|
||||
|
||||
}
|
||||
|
129
src/map/filter.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include <cmath>
|
||||
#include "filter.h"
|
||||
|
||||
static QVector<int> boxesForGauss(double sigma, int n)
|
||||
{
|
||||
double wIdeal = sqrt((12 * sigma * sigma / n) + 1);
|
||||
int wl = floor(wIdeal);
|
||||
if (wl % 2 == 0)
|
||||
wl--;
|
||||
int wu = wl + 2;
|
||||
|
||||
double mIdeal = (12 * sigma*sigma - n * wl * wl - 4 * n * wl - 3 * n)
|
||||
/ (-4 * wl - 4);
|
||||
int m = round(mIdeal);
|
||||
|
||||
QVector<int> sizes(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
sizes[i] = i < m ? wl : wu;
|
||||
|
||||
return sizes;
|
||||
}
|
||||
|
||||
static void boxBlurH4(const MatrixD &src, MatrixD &dst, int r)
|
||||
{
|
||||
double iarr = 1.0 / (r + r + 1);
|
||||
|
||||
for (int i = 0; i < src.h(); i++) {
|
||||
int ti = i * src.w(), li = ti, ri = ti + r;
|
||||
double fv = src.at(ti);
|
||||
double lv = src.at(ti + src.w() - 1);
|
||||
double val = (r + 1) * fv;
|
||||
|
||||
for (int j = 0; j < r; j++)
|
||||
val += src.at(ti + j);
|
||||
for (int j = 0; j <= r; j++) {
|
||||
val += src.at(ri++) - fv;
|
||||
dst.at(ti++) = val * iarr;
|
||||
}
|
||||
for (int j = r + 1; j < src.w() - r; j++) {
|
||||
val += src.at(ri++) - src.at(li++);
|
||||
dst.at(ti++) = val * iarr;
|
||||
}
|
||||
for (int j = src.w() - r; j < src.w(); j++) {
|
||||
val += lv - src.at(li++);
|
||||
dst.at(ti++) = val * iarr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void boxBlurT4(const MatrixD &src, MatrixD &dst, int r)
|
||||
{
|
||||
double iarr = 1.0 / (r + r + 1);
|
||||
|
||||
for (int i = 0; i < src.w(); i++) {
|
||||
int ti = i, li = ti, ri = ti + r * src.w();
|
||||
double fv = src.at(ti);
|
||||
double lv = src.at(ti + src.w() * (src.h() - 1));
|
||||
double val = (r + 1) * fv;
|
||||
|
||||
for (int j = 0; j < r; j++)
|
||||
val += src.at(ti + j * src.w());
|
||||
for (int j = 0; j <= r; j++) {
|
||||
val += src.at(ri) - fv;
|
||||
dst.at(ti) = val * iarr;
|
||||
ri += src.w(); ti += src.w();
|
||||
}
|
||||
for (int j = r + 1; j < src.h() - r; j++) {
|
||||
val += src.at(ri) - src.at(li);
|
||||
dst.at(ti) = val * iarr;
|
||||
li += src.w(); ri += src.w(); ti += src.w();
|
||||
}
|
||||
for (int j = src.h() - r; j < src.h(); j++) {
|
||||
val += lv - src.at(li);
|
||||
dst.at(ti) = val * iarr;
|
||||
li += src.w(); ti += src.w();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void boxBlur4(MatrixD &src, MatrixD &dst, int r)
|
||||
{
|
||||
for (int i = 0; i < src.size(); i++)
|
||||
dst.at(i) = src.at(i);
|
||||
|
||||
boxBlurH4(dst, src, r);
|
||||
boxBlurT4(src, dst, r);
|
||||
}
|
||||
|
||||
static void gaussBlur4(MatrixD &src, MatrixD &dst, int r)
|
||||
{
|
||||
QVector<int> bxs(boxesForGauss(r, 3));
|
||||
|
||||
boxBlur4(src, dst, (bxs.at(0) - 1) / 2);
|
||||
boxBlur4(dst, src, (bxs.at(1) - 1) / 2);
|
||||
boxBlur4(src, dst, (bxs.at(2) - 1) / 2);
|
||||
}
|
||||
|
||||
MatrixD Filter::blur(const MatrixD &m, int radius)
|
||||
{
|
||||
MatrixD src(m);
|
||||
MatrixD dst(m.h(), m.w());
|
||||
double sum = 0;
|
||||
int cnt = 0;
|
||||
|
||||
for (int i = 0; i < m.size(); i++) {
|
||||
if (!std::isnan(m.at(i))) {
|
||||
sum += m.at(i);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt != m.size()) {
|
||||
double avg = sum / cnt;
|
||||
|
||||
for (int i = 0; i < m.size(); i++)
|
||||
if (std::isnan(m.at(i)))
|
||||
src.at(i) = avg;
|
||||
}
|
||||
|
||||
gaussBlur4(src, dst, radius);
|
||||
|
||||
if (cnt != m.size()) {
|
||||
for (int i = 0; i < dst.size(); i++)
|
||||
if (std::isnan(m.at(i)))
|
||||
dst.at(i) = NAN;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
11
src/map/filter.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef FILTER_H
|
||||
#define FILTER_H
|
||||
|
||||
#include "matrix.h"
|
||||
|
||||
namespace Filter
|
||||
{
|
||||
MatrixD blur(const MatrixD &m, int radius);
|
||||
}
|
||||
|
||||
#endif // FILTER_H
|
@ -223,18 +223,6 @@ bool GCS::loadList(const QString &path)
|
||||
return true;
|
||||
}
|
||||
|
||||
Coordinates GCS::toWGS84(const Coordinates &c) const
|
||||
{
|
||||
return datum().toWGS84(Coordinates(_primeMeridian.toGreenwich(c.lon()),
|
||||
c.lat()));
|
||||
}
|
||||
|
||||
Coordinates GCS::fromWGS84(const Coordinates &c) const
|
||||
{
|
||||
Coordinates ds(datum().fromWGS84(c));
|
||||
return Coordinates(_primeMeridian.fromGreenwich(ds.lon()), ds.lat());
|
||||
}
|
||||
|
||||
QList<KV<int, QString> > GCS::list()
|
||||
{
|
||||
QList<KV<int, QString> > list;
|
||||
|
@ -29,8 +29,16 @@ public:
|
||||
bool isValid() const {return _datum.isValid() && _angularUnits.isValid()
|
||||
&& _primeMeridian.isValid();}
|
||||
|
||||
Coordinates toWGS84(const Coordinates &c) const;
|
||||
Coordinates fromWGS84(const Coordinates &c) const;
|
||||
Coordinates toWGS84(const Coordinates &c) const
|
||||
{
|
||||
return datum().toWGS84(Coordinates(_primeMeridian.toGreenwich(c.lon()),
|
||||
c.lat()));
|
||||
}
|
||||
Coordinates fromWGS84(const Coordinates &c) const
|
||||
{
|
||||
Coordinates ds(datum().fromWGS84(c));
|
||||
return Coordinates(_primeMeridian.fromGreenwich(ds.lon()), ds.lat());
|
||||
}
|
||||
|
||||
static GCS gcs(int id);
|
||||
static GCS gcs(int geodeticDatum, int primeMeridian, int angularUnits);
|
||||
|
@ -42,27 +42,28 @@ static void getDerivativesHorn(const SubMatrix &sm, double z, Derivatives &d)
|
||||
d.dzdy = (z * (sm.z1 + 2 * sm.z2 + sm.z3 - sm.z7 - 2 * sm.z8 - sm.z9)) / 8;
|
||||
}
|
||||
|
||||
static void getSubmatrix(int x, int y, const Matrix &m, SubMatrix &sm)
|
||||
static void getSubmatrix(int x, int y, const MatrixD &m, SubMatrix &sm)
|
||||
{
|
||||
int left = x - 1;
|
||||
int right = x + 1;
|
||||
int top = y - 1;
|
||||
int bottom = y + 1;
|
||||
|
||||
sm.z1 = m.m(top, left);
|
||||
sm.z2 = m.m(top, x);
|
||||
sm.z3 = m.m(top, right);
|
||||
sm.z4 = m.m(y, left);
|
||||
sm.z6 = m.m(y, right);
|
||||
sm.z7 = m.m(bottom, left);
|
||||
sm.z8 = m.m(bottom, x);
|
||||
sm.z9 = m.m(bottom, right);
|
||||
sm.z1 = m.at(top, left);
|
||||
sm.z2 = m.at(top, x);
|
||||
sm.z3 = m.at(top, right);
|
||||
sm.z4 = m.at(y, left);
|
||||
sm.z6 = m.at(y, right);
|
||||
sm.z7 = m.at(bottom, left);
|
||||
sm.z8 = m.at(bottom, x);
|
||||
sm.z9 = m.at(bottom, right);
|
||||
}
|
||||
|
||||
QImage HillShading::render(const Matrix &m, quint8 alpha, double z,
|
||||
QImage HillShading::render(const MatrixD &m, int extend, quint8 alpha, double z,
|
||||
double azimuth, double elevation)
|
||||
{
|
||||
QImage img(m.w() - 2, m.h() - 2, QImage::Format_ARGB32_Premultiplied);
|
||||
QImage img(m.w() - 2 * extend, m.h() - 2 * extend,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
uchar *bits = img.bits();
|
||||
int bpl = img.bytesPerLine();
|
||||
|
||||
@ -72,8 +73,8 @@ QImage HillShading::render(const Matrix &m, quint8 alpha, double z,
|
||||
|
||||
getConstants(azimuth, elevation, c);
|
||||
|
||||
for (int y = 1; y < m.h() - 1; y++) {
|
||||
for (int x = 1; x < m.w() - 1; x++) {
|
||||
for (int y = extend; y < m.h() - extend; y++) {
|
||||
for (int x = extend; x < m.w() - extend; x++) {
|
||||
getSubmatrix(x, y, m, sm);
|
||||
getDerivativesHorn(sm, z, d);
|
||||
|
||||
@ -89,7 +90,7 @@ QImage HillShading::render(const Matrix &m, quint8 alpha, double z,
|
||||
pixel = (alpha - val)<<24;
|
||||
}
|
||||
|
||||
*(quint32*)(bits + (y - 1) * bpl + (x - 1) * 4) = pixel;
|
||||
*(quint32*)(bits + (y - extend) * bpl + (x - extend) * 4) = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
||||
class HillShading
|
||||
{
|
||||
public:
|
||||
static QImage render(const Matrix &m, quint8 alpha = 96, double z = 0.3,
|
||||
double azimuth = 315, double elevation = 25);
|
||||
static QImage render(const MatrixD &m, int extend, quint8 alpha = 96,
|
||||
double z = 0.6, double azimuth = 315, double elevation = 45);
|
||||
};
|
||||
|
||||
#endif // HILLSHADING_H
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "IMG/imgdata.h"
|
||||
#include "IMG/gmapdata.h"
|
||||
#include "IMG/rastertile.h"
|
||||
#include "IMG/dem.h"
|
||||
#include "osm.h"
|
||||
#include "pcs.h"
|
||||
#include "rectd.h"
|
||||
@ -266,6 +267,22 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
}
|
||||
}
|
||||
|
||||
double IMGMap::elevation(const Coordinates &c)
|
||||
{
|
||||
QList<MapData::Elevation> tiles;
|
||||
DEM::DEMTRee tree;
|
||||
MapData *d = _data.first();
|
||||
double ele = NAN;
|
||||
|
||||
d->elevations(RectC(Coordinates(c), Coordinates(c)), d->zooms().max(),
|
||||
&tiles);
|
||||
|
||||
DEM::buildTree(tiles, tree);
|
||||
DEM::searchTree(tree, c, ele);
|
||||
|
||||
return ele;
|
||||
}
|
||||
|
||||
Map* IMGMap::createIMG(const QString &path, const Projection &proj, bool *isDir)
|
||||
{
|
||||
Q_UNUSED(proj);
|
||||
|
@ -74,7 +74,7 @@ public:
|
||||
bool hidpi);
|
||||
void unload();
|
||||
|
||||
bool usesDEM() const {return true;}
|
||||
double elevation(const Coordinates &c);
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
QString errorString() const {return _errorString;}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <cmath>
|
||||
#include <QLineF>
|
||||
#include "common/dem.h"
|
||||
#include "map.h"
|
||||
|
||||
|
||||
@ -79,3 +80,14 @@ qreal Map::resolution(const QRectF &rect)
|
||||
|
||||
return ds/ps;
|
||||
}
|
||||
|
||||
double Map::elevation(const Coordinates &c)
|
||||
{
|
||||
double ele;
|
||||
|
||||
DEM::lock();
|
||||
ele = DEM::elevation(c);
|
||||
DEM::unlock();
|
||||
|
||||
return ele;
|
||||
}
|
||||
|
@ -56,8 +56,9 @@ public:
|
||||
|
||||
virtual void draw(QPainter *painter, const QRectF &rect, Flags flags) = 0;
|
||||
|
||||
virtual double elevation(const Coordinates &c);
|
||||
|
||||
virtual void clearCache() {}
|
||||
virtual bool usesDEM() const {return false;}
|
||||
|
||||
signals:
|
||||
void tilesLoaded();
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "common/dem.h"
|
||||
#include "map/rectd.h"
|
||||
#include "map/hillshading.h"
|
||||
#include "map/filter.h"
|
||||
#include "rastertile.h"
|
||||
|
||||
using namespace Mapsforge;
|
||||
@ -12,6 +13,8 @@ using namespace Mapsforge;
|
||||
#define PATHS_EXTENT 20
|
||||
#define SEARCH_EXTENT -0.5
|
||||
|
||||
#define BLUR_RADIUS 3
|
||||
|
||||
static double LIMIT = cos(deg2rad(170));
|
||||
|
||||
static qreal area(const QPainterPath &polygon)
|
||||
@ -438,9 +441,12 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
|
||||
painter->setBrush(ri->brush());
|
||||
painter->drawEllipse(ll2xy(point->coordinates), radius, radius);
|
||||
} else {
|
||||
if (_hillShading)
|
||||
painter->drawImage(_rect.x(), _rect.y(),
|
||||
HillShading::render(elevation()));
|
||||
if (_hillShading) {
|
||||
MatrixD dem(Filter::blur(elevation(BLUR_RADIUS + 1),
|
||||
BLUR_RADIUS));
|
||||
QImage img(HillShading::render(dem, BLUR_RADIUS + 1));
|
||||
painter->drawImage(_rect.x(), _rect.y(), img);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,14 +477,14 @@ void RasterTile::fetchData(QList<MapData::Path> &paths,
|
||||
_data->points(pointRectD.toRectC(_proj, 20), _zoom, &points);
|
||||
}
|
||||
|
||||
Matrix RasterTile::elevation() const
|
||||
MatrixD RasterTile::elevation(int extend) const
|
||||
{
|
||||
Matrix m(_rect.height() + 2, _rect.width() + 2);
|
||||
MatrixD m(_rect.height() + 2 * extend, _rect.width() + 2 * extend);
|
||||
|
||||
int left = _rect.left() - 1;
|
||||
int right = _rect.right() + 1;
|
||||
int top = _rect.top() - 1;
|
||||
int bottom = _rect.bottom() + 1;
|
||||
int left = _rect.left() - extend;
|
||||
int right = _rect.right() + extend;
|
||||
int top = _rect.top() - extend;
|
||||
int bottom = _rect.bottom() + extend;
|
||||
|
||||
QVector<Coordinates> ll;
|
||||
ll.reserve(m.w() * m.h());
|
||||
@ -489,7 +495,7 @@ Matrix RasterTile::elevation() const
|
||||
|
||||
DEM::lock();
|
||||
for (int i = 0; i < ll.size(); i++)
|
||||
m.m(i) = DEM::elevation(ll.at(i));
|
||||
m.at(i) = DEM::elevation(ll.at(i));
|
||||
DEM::unlock();
|
||||
|
||||
return m;
|
||||
|
@ -23,17 +23,13 @@ namespace Mapsforge {
|
||||
class RasterTile
|
||||
{
|
||||
public:
|
||||
/* Most Mapsforge themes expect the zoom levels to be offset by one from
|
||||
the standard OSM zoom levels! We decrease the zoom level internaly
|
||||
here when initializing _zoom and return the propper (increased) value
|
||||
back in zoom() */
|
||||
RasterTile(const Projection &proj, const Transform &transform,
|
||||
const Style *style, MapData *data, int zoom, const QRect &rect,
|
||||
qreal ratio, bool hillShading)
|
||||
: _proj(proj), _transform(transform), _style(style), _data(data),
|
||||
_zoom(zoom - 1), _rect(rect), _ratio(ratio), _hillShading(hillShading) {}
|
||||
_zoom(zoom), _rect(rect), _ratio(ratio), _hillShading(hillShading) {}
|
||||
|
||||
int zoom() const {return _zoom + 1;}
|
||||
int zoom() const {return _zoom;}
|
||||
QPoint xy() const {return _rect.topLeft();}
|
||||
const QPixmap &pixmap() const {return _pixmap;}
|
||||
|
||||
@ -219,7 +215,7 @@ private:
|
||||
void drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
|
||||
const QList<MapData::Point> &points, QVector<PainterPath> &painterPaths);
|
||||
|
||||
Matrix elevation() const;
|
||||
MatrixD elevation(int extend) const;
|
||||
|
||||
Projection _proj;
|
||||
Transform _transform;
|
||||
|
@ -71,8 +71,6 @@ public:
|
||||
|
||||
void draw(QPainter *painter, const QRectF &rect, Flags flags);
|
||||
|
||||
bool usesDEM() const {return true;}
|
||||
|
||||
bool isValid() const {return _data.isValid();}
|
||||
QString errorString() const {return _data.errorString();}
|
||||
|
||||
|
@ -1,79 +1,56 @@
|
||||
#include "matrix.h"
|
||||
|
||||
|
||||
#define abs(x) ((x)<0 ? -(x) : (x))
|
||||
|
||||
Matrix::Matrix(int h, int w)
|
||||
{
|
||||
_h = h;
|
||||
_w = w;
|
||||
_m.resize(_h * _w);
|
||||
}
|
||||
|
||||
bool Matrix::eliminate(double epsilon)
|
||||
bool MatrixD::eliminate(double epsilon)
|
||||
{
|
||||
double temp;
|
||||
|
||||
for (int i = 0; i < _h; i++) {
|
||||
int maxrow = i;
|
||||
for (int j = i+1; j < _h; j++)
|
||||
if (abs(m(j, i)) > abs(m(maxrow, i)))
|
||||
if (abs(at(j, i)) > abs(at(maxrow, i)))
|
||||
maxrow = j;
|
||||
for (int j = 0; j < _w; j++) {
|
||||
temp = m(i, j);
|
||||
m(i, j) = m(maxrow, j);
|
||||
m(maxrow, j) = temp;
|
||||
temp = at(i, j);
|
||||
at(i, j) = at(maxrow, j);
|
||||
at(maxrow, j) = temp;
|
||||
}
|
||||
if (abs(m(i, i)) <= epsilon)
|
||||
if (abs(at(i, i)) <= epsilon)
|
||||
return false;
|
||||
for (int j = i+1; j<_h; j++) {
|
||||
temp = m(j, i) / m(i, i);
|
||||
temp = at(j, i) / at(i, i);
|
||||
for (int k = i; k < _w; k++)
|
||||
m(j, k) -= m(i, k) * temp;
|
||||
at(j, k) -= at(i, k) * temp;
|
||||
}
|
||||
}
|
||||
for (int i = _h-1; i >= 0; i--) {
|
||||
temp = m(i, i);
|
||||
temp = at(i, i);
|
||||
for (int j = 0; j < i; j++)
|
||||
for (int k = _w-1; k >= i; k--)
|
||||
m(j, k) -= m(i, k) * m(j, i) / temp;
|
||||
m(i, i) /= temp;
|
||||
at(j, k) -= at(i, k) * at(j, i) / temp;
|
||||
at(i, i) /= temp;
|
||||
for (int j = _h; j < _w; j++)
|
||||
m(i, j) /= temp;
|
||||
at(i, j) /= temp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Matrix Matrix::augemented(const Matrix &M) const
|
||||
MatrixD MatrixD::augemented(const MatrixD &M) const
|
||||
{
|
||||
if (_h != M._h)
|
||||
return Matrix();
|
||||
return MatrixD();
|
||||
|
||||
Matrix A(_h, _w + M._w);
|
||||
MatrixD A(_h, _w + M._w);
|
||||
|
||||
for (int i = 0; i < _h; i++)
|
||||
for (int j = 0; j < _w; j++)
|
||||
A.m(i, j) = m(i, j);
|
||||
A.at(i, j) = at(i, j);
|
||||
|
||||
for (int i = 0; i < _h; i++)
|
||||
for (int j = _w; j < A._w; j++)
|
||||
A.m(i, j) = M.m(i, j-_w);
|
||||
A.at(i, j) = M.at(i, j-_w);
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
QDebug operator<<(QDebug dbg, const Matrix &matrix)
|
||||
{
|
||||
dbg.nospace() << "Matrix(" << "\n";
|
||||
for (int i = 0; i < matrix.h(); i++) {
|
||||
for (int j = 0; j < matrix.w(); j++)
|
||||
dbg << "\t" << matrix.m(i, j);
|
||||
dbg << "\n";
|
||||
}
|
||||
dbg << ")";
|
||||
|
||||
return dbg.space();
|
||||
}
|
||||
#endif // QT_NO_DEBUG
|
||||
|