1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-16 11:54:23 +02:00

Compare commits

..

58 Commits

Author SHA1 Message Date
27401d58b7 Make the DEM data borders look much better 2024-05-21 20:59:06 +02:00
da2b6661f6 Replace the compiler dependent signed right shifts with a universal version 2024-05-21 20:57:21 +02:00
de76eafdfb Missing header change... 2024-05-21 20:56:55 +02:00
19cda6fbd5 Enable inlining of the "hot" paths 2024-05-21 20:55:50 +02:00
6803ee0324 Made the code more standard conforming 2024-05-21 09:14:15 +02:00
43271d9ff8 Fixed rendering glitch 2024-05-21 08:46:29 +02:00
6bd83780cd Enable hillshading by default 2024-05-21 08:34:52 +02:00
c4d07b5f12 Prefer render quality rather than render speed 2024-05-20 21:19:26 +02:00
b28217a026 A much more accurate overlap delta value 2024-05-20 21:13:27 +02:00
1af2c130b0 Adjust hillshading parameters 2024-05-20 20:21:29 +02:00
ca0d859c6d Fixed include guard comment 2024-05-20 20:20:57 +02:00
947d2d62b3 Added DEM filtering (bluring) 2024-05-20 19:17:27 +02:00
11677f5e35 API cleanup 2024-05-20 19:10:56 +02:00
6ef6644260 Fixed DEM display glitches 2024-05-19 22:06:57 +02:00
f6f9e4146d Remove cut&paste remains 2024-05-19 19:03:49 +02:00
d1401bc302 Show the map elevation from the map DEM if available 2024-05-19 18:59:40 +02:00
488e5e1cac Fixed build on older Qt5 versions 2024-05-19 16:39:12 +02:00
ff4f3eea60 Use the map-provided DEM data for hillshading on IMG maps 2024-05-19 16:14:23 +02:00
d46ac8435e Fixed Debug build 2024-05-01 10:27:39 +02:00
15fbd6d35e Added support for TCX course points icons 2024-04-28 00:43:02 +02:00
7de180d580 Cache size limits update
Allow caches up to 4GB (usefull for 0.5' DEM tiles)
Do not allow smaller pixmap cache than 64MB (minimum for async maps to work on
4K displays is ~34MB of tile image data!)
2024-04-27 12:30:07 +02:00
bd37521ca0 Use kB in the chache size arithmetics to prevent integer overflow 2024-04-27 12:15:06 +02:00
4445976cb9 Use a more strict regular expression to match the tiles 2024-04-27 11:46:00 +02:00
76b14c23c6 Do not show local DEM tiles with unsupported file names 2024-04-27 11:42:37 +02:00
3b4376cc03 Use MacOS 12 for Qt5 builds 2024-04-25 12:06:10 +02:00
41b1ec3605 Added missing map root-XML 2024-04-24 08:26:04 +02:00
4476998333 Use the latest map format version 2024-04-24 08:25:30 +02:00
5940a2ced4 Silence clang warnings 2024-04-23 01:04:38 +02:00
33c45f845a Code cleanup 2024-04-21 00:00:24 +02:00
6f12f91cb1 Version++ 2024-04-21 00:00:09 +02:00
55de85579c Move the tables to the function where they are used 2024-04-20 19:57:52 +02:00
86f98aca42 Make it clear the tables are not modified during runtime 2024-04-20 19:50:33 +02:00
56425a3318 Always use the GPXSee icon theme on Mac and Windows 2024-04-20 16:23:23 +02:00
d045fed086 Swtch to Qt 6.7 in Windows CI builds 2024-04-19 20:19:47 +02:00
31cd65f15e Remove the zoom offset hack
Now that we are back to the map data defined tile size, it makes no sense any
more. Additionaly it crashes with world maps as zoom 0 becomes -1...
2024-04-18 20:35:31 +02:00
089ccfde71 Code cleanup 2024-04-18 20:35:05 +02:00
a0ce50e7e4 Use QVectors instead of plain arrays 2024-04-16 10:26:17 +02:00
031d82f689 Code cleanup 2024-04-16 09:17:18 +02:00
99f620f101 Fixed capitalization algorithm 2024-04-16 08:52:54 +02:00
5f3203d638 Switched the OS X Qt6 build to Qt 6.7.0 2024-04-15 00:03:09 +02:00
33cc1df601 Update to OpenSSL 3.2 2024-04-14 23:49:40 +02:00
5ef29bf827 Some more code cleanup 2024-04-14 23:32:44 +02:00
6f029d81a0 Code cleanup 2024-04-14 22:15:30 +02:00
90395a32dd Improve the capitalization algorithm 2024-04-14 17:33:49 +02:00
d4b10b091c Fit the extension placeholder into the template 2024-04-14 15:09:12 +02:00
6210d1b983 Switched back to diagonal pattern for national parks 2024-04-14 14:50:34 +02:00
5e71715f22 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (474 of 474 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/zh_Hans/
2024-04-06 18:02:00 +02:00
648f90f230 Improve FIT locations support 2024-04-03 02:30:26 +02:00
324168340b Do not blur the icons when resizing them 2024-04-03 02:18:00 +02:00
3e1bddbcfd Fixed line style combo box rendering on retina displays 2024-04-01 21:00:56 +02:00
b069d2ac3f Fixed broken error string encoding 2024-04-01 20:22:28 +02:00
9b46845568 Use the same color in the icon "alias" 2024-04-01 13:46:26 +02:00
f814d1f5a3 Use the water temperature as temperature data when no air temperature is present 2024-04-01 13:30:50 +02:00
0f05dd6ba3 Some more missing FIT course icons 2024-04-01 13:18:56 +02:00
8a069f113e Cosmetics 2024-04-01 12:37:06 +02:00
bfe31023e0 FIT course symbols instead of description 2024-04-01 12:28:50 +02:00
779f98a206 Ignore GeoJSON features with null geometries 2024-03-27 09:07:39 +01:00
a660cbd463 Version++ 2024-03-27 09:07:24 +01:00
102 changed files with 1644 additions and 352 deletions

View File

@ -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:

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icons/symbols/Danger.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

BIN
icons/symbols/First Aid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 822 B

BIN
icons/symbols/Food.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icons/symbols/Gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icons/symbols/Info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
icons/symbols/Left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/symbols/Obstacle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

BIN
icons/symbols/Overlook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

BIN
icons/symbols/Rest Area.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

BIN
icons/symbols/Right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

BIN
icons/symbols/Service.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

BIN
icons/symbols/Shelter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

BIN
icons/symbols/Sprint.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
icons/symbols/Store.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/symbols/Straight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icons/symbols/Toilet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

BIN
icons/symbols/Transport.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
icons/symbols/U-Turn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

BIN
icons/symbols/Valley.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1018 B

BIN
icons/symbols/Water.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 824 B

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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 );

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -22,7 +22,7 @@ public:
QString lonStr() const;
QString latStr() const;
QString baseName() const;
QString fileName() const;
bool operator==(const Tile &other) const
{

View File

@ -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)

View File

@ -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;
}

View File

@ -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();

View File

@ -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;

View File

@ -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"))

View File

@ -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();
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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

View File

@ -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;
}

View File

@ -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
View 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
View 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

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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 &rect;
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 &rect;
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 &rect;
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;
};

View File

@ -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);

View File

@ -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;

View File

@ -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)));

View File

@ -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;

View File

@ -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)
{

View File

@ -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
View 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
View 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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;}

View File

@ -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;
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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();}

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More