Compare commits
104 Commits
Author | SHA1 | Date | |
---|---|---|---|
d579ce3482 | |||
67ce176b74 | |||
1a88527c60 | |||
9d6a2cce45 | |||
7a5f67790e | |||
704c66449f | |||
42e1331678 | |||
ad3b666a19 | |||
3dd253828e | |||
bb5e50b009 | |||
69384ca315 | |||
676c82b7a4 | |||
5a2be6ff07 | |||
454e725587 | |||
b9d9ab85b2 | |||
15a2df12bd | |||
db7a75088a | |||
378da395fb | |||
da7d0fe32d | |||
789f314ae8 | |||
0986864c6c | |||
83ac0b5ed7 | |||
a1be73fbba | |||
7761935c29 | |||
4da0e8a1c7 | |||
066736b3d2 | |||
fdcbc4c6c2 | |||
b894df26d3 | |||
5a5c0ef68a | |||
56b7014eaf | |||
1f52dad1c6 | |||
0f8859dd20 | |||
398eb152f6 | |||
38ab835898 | |||
927740a196 | |||
4d8b7aa8ae | |||
49c94d34b3 | |||
5c2ac54bb4 | |||
003947263f | |||
6a70e5ea00 | |||
e83be4d553 | |||
ce38077281 | |||
e75a2882a5 | |||
8add7b428f | |||
846f864bd4 | |||
8683254155 | |||
ef2ffd9fc4 | |||
a9c86fd580 | |||
fe360a2578 | |||
a09a58eece | |||
abd1817d83 | |||
7c90174751 | |||
b24f27cf79 | |||
98f88db3cf | |||
f2ddfa6fb7 | |||
54ed0ca9f6 | |||
477b32f444 | |||
1fb6aad50f | |||
4fa9aac917 | |||
03db87535a | |||
92145c8445 | |||
5e8479707b | |||
2039105ba5 | |||
2605e1abeb | |||
8fd17badda | |||
7d62ef038c | |||
138e0e9505 | |||
5dffb2714b | |||
5d8330a68a | |||
ff30163175 | |||
743fb20a95 | |||
50f483663c | |||
96997ffa35 | |||
d738ad7b5a | |||
01d69a4f2a | |||
0e026d6a96 | |||
07825e5701 | |||
03e7d092c4 | |||
0b5d01a1f6 | |||
08aa087f61 | |||
6604f85f4a | |||
5343a1a922 | |||
d6e0757364 | |||
51becc4bf1 | |||
29a821f8b2 | |||
14f4dead76 | |||
96bb3bbdbb | |||
22d18b6d4e | |||
c1b79217a9 | |||
e67a14b072 | |||
473d03cf1f | |||
a339706293 | |||
39c414ca73 | |||
c59d60faed | |||
32d3eab10e | |||
e7729e8745 | |||
bf145c9eb5 | |||
de0a6b0397 | |||
8cf89a580f | |||
152e2a8a09 | |||
6b860fe18c | |||
95f138f5f0 | |||
1fc4dbbb73 | |||
0999cdcba2 |
@ -1,4 +1,4 @@
|
||||
version: 7.30.{build}
|
||||
version: 7.34.{build}
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
35
gpxsee.pro
@ -3,7 +3,7 @@ unix:!macx {
|
||||
} else {
|
||||
TARGET = GPXSee
|
||||
}
|
||||
VERSION = 7.30
|
||||
VERSION = 7.34
|
||||
|
||||
QT += core \
|
||||
gui \
|
||||
@ -14,12 +14,14 @@ greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
QT += widgets
|
||||
QT += printsupport
|
||||
}
|
||||
lessThan(QT_VERSION, 5.4.0) {QT += opengl}
|
||||
lessThan(QT_MAJOR_VERSION, 5) {QT += opengl}
|
||||
equals(QT_MAJOR_VERSION, 5) : lessThan(QT_MINOR_VERSION, 4) {QT += opengl}
|
||||
|
||||
INCLUDEPATH += ./src
|
||||
HEADERS += src/common/config.h \
|
||||
src/GUI/graphicsscene.h \
|
||||
src/GUI/mapaction.h \
|
||||
src/GUI/marginswidget.h \
|
||||
src/GUI/popup.h \
|
||||
src/common/garmin.h \
|
||||
src/common/staticassert.h \
|
||||
@ -52,7 +54,6 @@ HEADERS += src/common/config.h \
|
||||
src/GUI/palette.h \
|
||||
src/GUI/heartrategraph.h \
|
||||
src/GUI/trackinfo.h \
|
||||
src/GUI/exportdialog.h \
|
||||
src/GUI/fileselectwidget.h \
|
||||
src/GUI/margins.h \
|
||||
src/GUI/temperaturegraph.h \
|
||||
@ -94,6 +95,7 @@ HEADERS += src/common/config.h \
|
||||
src/map/IMG/gmap.h \
|
||||
src/map/IMG/huffmanstream.h \
|
||||
src/map/IMG/huffmantable.h \
|
||||
src/map/IMG/nodfile.h \
|
||||
src/map/IMG/mapdata.h \
|
||||
src/map/IMG/rastertile.h \
|
||||
src/map/IMG/textpathitem.h \
|
||||
@ -196,8 +198,11 @@ HEADERS += src/common/config.h \
|
||||
src/data/cupparser.h \
|
||||
src/data/gpiparser.h \
|
||||
src/data/address.h \
|
||||
src/data/smlparser.h
|
||||
src/data/smlparser.h \
|
||||
src/GUI/pdfexportdialog.h \
|
||||
src/GUI/pngexportdialog.h
|
||||
SOURCES += src/main.cpp \
|
||||
src/GUI/marginswidget.cpp \
|
||||
src/GUI/popup.cpp \
|
||||
src/common/coordinates.cpp \
|
||||
src/common/rectc.cpp \
|
||||
@ -222,7 +227,6 @@ SOURCES += src/main.cpp \
|
||||
src/GUI/palette.cpp \
|
||||
src/GUI/heartrategraph.cpp \
|
||||
src/GUI/trackinfo.cpp \
|
||||
src/GUI/exportdialog.cpp \
|
||||
src/GUI/fileselectwidget.cpp \
|
||||
src/GUI/temperaturegraph.cpp \
|
||||
src/GUI/trackitem.cpp \
|
||||
@ -256,6 +260,7 @@ SOURCES += src/main.cpp \
|
||||
src/map/IMG/gmap.cpp \
|
||||
src/map/IMG/huffmanstream.cpp \
|
||||
src/map/IMG/huffmantable.cpp \
|
||||
src/map/IMG/nodfile.cpp \
|
||||
src/map/IMG/mapdata.cpp \
|
||||
src/map/IMG/rastertile.cpp \
|
||||
src/map/IMG/textpathitem.cpp \
|
||||
@ -341,13 +346,15 @@ SOURCES += src/main.cpp \
|
||||
src/data/cupparser.cpp \
|
||||
src/GUI/graphicsscene.cpp \
|
||||
src/data/gpiparser.cpp \
|
||||
src/data/smlparser.cpp
|
||||
src/data/smlparser.cpp \
|
||||
src/GUI/pdfexportdialog.cpp \
|
||||
src/GUI/pngexportdialog.cpp
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
HEADERS += src/data/geojsonparser.h
|
||||
SOURCES += src/data/geojsonparser.cpp
|
||||
}
|
||||
greaterThan(QT_VERSION, 5.1.0) {
|
||||
equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 4) {
|
||||
HEADERS += src/GUI/timezoneinfo.h
|
||||
}
|
||||
|
||||
@ -370,10 +377,11 @@ TRANSLATIONS = lang/gpxsee_en.ts \
|
||||
lang/gpxsee_es.ts \
|
||||
lang/gpxsee_pt_BR.ts \
|
||||
lang/gpxsee_uk.ts \
|
||||
lang/gpxsee_hu.ts
|
||||
lang/gpxsee_hu.ts \
|
||||
lang/gpxsee_it.ts
|
||||
|
||||
macx {
|
||||
ICON = icons/gpxsee.icns
|
||||
ICON = icons/app/gpxsee.icns
|
||||
QMAKE_INFO_PLIST = pkg/Info.plist
|
||||
locale.path = Contents/Resources/translations
|
||||
locale.files = lang/gpxsee_en.qm \
|
||||
@ -390,7 +398,8 @@ macx {
|
||||
lang/gpxsee_es.qm \
|
||||
lang/gpxsee_pt_BR.qm \
|
||||
lang/gpxsee_uk.qm \
|
||||
lang/gpxsee_hu.qm
|
||||
lang/gpxsee_hu.qm \
|
||||
lang/gpxsee_it.qm
|
||||
csv.path = Contents/Resources
|
||||
csv.files = pkg/csv
|
||||
maps.path = Contents/Resources
|
||||
@ -415,7 +424,7 @@ macx {
|
||||
}
|
||||
|
||||
win32 {
|
||||
RC_ICONS = icons/gpxsee.ico \
|
||||
RC_ICONS = icons/app/gpxsee.ico \
|
||||
icons/formats/gpx.ico \
|
||||
icons/formats/tcx.ico \
|
||||
icons/formats/kml.ico \
|
||||
@ -445,8 +454,8 @@ unix:!macx {
|
||||
csv.path = $$PREFIX/share/gpxsee/csv
|
||||
locale.files = lang/*.qm
|
||||
locale.path = $$PREFIX/share/gpxsee/translations
|
||||
icon.files = icons/gpxsee.png
|
||||
icon.path = $$PREFIX/share/pixmaps
|
||||
icon.files = icons/app/hicolor/*
|
||||
icon.path = $$PREFIX/share/icons/hicolor
|
||||
desktop.files = pkg/gpxsee.desktop
|
||||
desktop.path = $$PREFIX/share/applications
|
||||
mime.files = pkg/gpxsee.xml
|
||||
|
@ -1,8 +1,8 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<!-- GUI -->
|
||||
<file alias="gpxsee.png">icons/gpxsee.png</file>
|
||||
<file alias="gpxsee@2x.png">icons/gpxsee@2x.png</file>
|
||||
<file alias="gpxsee.png">icons/app/gpxsee.png</file>
|
||||
<file alias="gpxsee@2x.png">icons/app/gpxsee@2x.png</file>
|
||||
<file alias="dialog-close.png">icons/GUI/dialog-close.png</file>
|
||||
<file alias="dialog-close@2x.png">icons/GUI/dialog-close@2x.png</file>
|
||||
<file alias="document-open.png">icons/GUI/document-open.png</file>
|
||||
|
BIN
icons/app/gpxsee.dia
Normal file
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
icons/app/hicolor/128x128/apps/gpxsee.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
icons/app/hicolor/16x16/apps/gpxsee.png
Normal file
After Width: | Height: | Size: 571 B |
BIN
icons/app/hicolor/32x32/apps/gpxsee.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
icons/app/hicolor/48x48/apps/gpxsee.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
icons/app/hicolor/64x64/apps/gpxsee.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
33
icons/app/hicolor/scalable/apps/gpxsee.svg
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||
<svg width="6cm" height="6cm" viewBox="47 79 119 119" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g>
|
||||
<rect style="fill: #ffffff" x="47.729" y="79.875" width="118.071" height="118.071" rx="10" ry="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke-linejoin: round; stroke: #ffffff" x="47.729" y="79.875" width="118.071" height="118.071" rx="10" ry="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<ellipse style="fill: #000000" cx="113" cy="90.875" rx="7.021" ry="7.021"/>
|
||||
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke-linejoin: round; stroke: #000000" cx="113" cy="90.875" rx="7.021" ry="7.021"/>
|
||||
</g>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #000000" points="61.5289,182.479 73.5,125.854 96,151.875 113,90.875 136.5,172.375 151.658,157.199 "/>
|
||||
<g>
|
||||
<ellipse style="fill: #000000" cx="73.5" cy="125.854" rx="7.021" ry="7.021"/>
|
||||
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="73.5" cy="125.854" rx="7.021" ry="7.021"/>
|
||||
</g>
|
||||
<g>
|
||||
<ellipse style="fill: #000000" cx="136.5" cy="172.375" rx="7.021" ry="7.021"/>
|
||||
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="136.5" cy="172.375" rx="7.021" ry="7.021"/>
|
||||
</g>
|
||||
<g>
|
||||
<ellipse style="fill: #000000" cx="60.7" cy="186.4" rx="7.021" ry="7.021"/>
|
||||
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="60.7" cy="186.4" rx="7.021" ry="7.021"/>
|
||||
</g>
|
||||
<g>
|
||||
<ellipse style="fill: #000000" cx="154.5" cy="154.354" rx="7.021" ry="7.021"/>
|
||||
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="154.5" cy="154.354" rx="7.021" ry="7.021"/>
|
||||
</g>
|
||||
<g>
|
||||
<ellipse style="fill: #000000" cx="96" cy="151.875" rx="7.021" ry="7.021"/>
|
||||
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="96" cy="151.875" rx="7.021" ry="7.021"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
icons/gpxsee.dia
@ -1,4 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="en_US">
|
||||
<context>
|
||||
<name>GUI</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/GUI/gui.cpp" line="1392"/>
|
||||
<source>%n files</source>
|
||||
<translation>
|
||||
<numerusform>%n file</numerusform>
|
||||
<numerusform>%n files</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -7,7 +7,7 @@
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "7.30"
|
||||
!define VERSION "7.34"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}.exe"
|
||||
@ -177,6 +177,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
|
||||
!insertmacro LOCALIZATION "French" "fr"
|
||||
!insertmacro LOCALIZATION "German" "de"
|
||||
!insertmacro LOCALIZATION "Hungarian" "hu"
|
||||
!insertmacro LOCALIZATION "Italian" "it"
|
||||
!insertmacro LOCALIZATION "Norwegian" "nb"
|
||||
!insertmacro LOCALIZATION "Polish" "pl"
|
||||
!insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR"
|
||||
|
@ -7,7 +7,7 @@
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "7.30"
|
||||
!define VERSION "7.34"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}_x64.exe"
|
||||
@ -184,6 +184,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
|
||||
!insertmacro LOCALIZATION "French" "fr"
|
||||
!insertmacro LOCALIZATION "German" "de"
|
||||
!insertmacro LOCALIZATION "Hungarian" "hu"
|
||||
!insertmacro LOCALIZATION "Italian" "it"
|
||||
!insertmacro LOCALIZATION "Norwegian" "nb"
|
||||
!insertmacro LOCALIZATION "Polish" "pl"
|
||||
!insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR"
|
||||
|
@ -12,7 +12,8 @@ FileSelectWidget::FileSelectWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
QFontMetrics fm(QApplication::font());
|
||||
_edit = new QLineEdit();
|
||||
_edit->setMinimumWidth(fm.boundingRect(QDir::homePath()).width());
|
||||
_edit->setMinimumWidth(fm.averageCharWidth() * (QDir::homePath().length()
|
||||
+ 12));
|
||||
#ifdef Q_OS_WIN32
|
||||
_button = new QPushButton("...");
|
||||
_button->setMaximumWidth(_button->sizeHint().width() / 2);
|
||||
@ -41,3 +42,33 @@ void FileSelectWidget::browse()
|
||||
if (!fileName.isEmpty())
|
||||
_edit->setText(fileName);
|
||||
}
|
||||
|
||||
bool FileSelectWidget::checkFile(QString &error) const
|
||||
{
|
||||
if (_edit->text().isEmpty()) {
|
||||
error = tr("No output file selected.");
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(_edit->text());
|
||||
QFileInfo fi(file);
|
||||
bool exists = fi.exists();
|
||||
bool opened = false;
|
||||
|
||||
if (exists && fi.isDir()) {
|
||||
error = tr("%1 is a directory.").arg(file.fileName());
|
||||
return false;
|
||||
} else if ((exists && !fi.isWritable())
|
||||
|| !(opened = file.open(QFile::Append))) {
|
||||
error = tr("%1 is not writable.").arg(file.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opened) {
|
||||
file.close();
|
||||
if (!exists)
|
||||
file.remove();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -14,9 +14,10 @@ class FileSelectWidget : public QWidget
|
||||
public:
|
||||
FileSelectWidget(QWidget *parent = 0);
|
||||
|
||||
QString file() {return _edit->text();}
|
||||
QString file() const {return _edit->text();}
|
||||
void setFile(const QString &file) {_edit->setText(file);}
|
||||
void setFilter(const QString &filter) {_filter = filter;}
|
||||
bool checkFile(QString &error) const;
|
||||
|
||||
private slots:
|
||||
void browse();
|
||||
|
291
src/GUI/gui.cpp
@ -230,13 +230,20 @@ void GUI::createActions()
|
||||
_printFileAction->setActionGroup(_fileActionGroup);
|
||||
connect(_printFileAction, SIGNAL(triggered()), this, SLOT(printFile()));
|
||||
addAction(_printFileAction);
|
||||
_exportFileAction = new QAction(QIcon(EXPORT_FILE_ICON),
|
||||
_exportPDFFileAction = new QAction(QIcon(EXPORT_FILE_ICON),
|
||||
tr("Export to PDF..."), this);
|
||||
_exportFileAction->setMenuRole(QAction::NoRole);
|
||||
_exportFileAction->setShortcut(EXPORT_SHORTCUT);
|
||||
_exportFileAction->setActionGroup(_fileActionGroup);
|
||||
connect(_exportFileAction, SIGNAL(triggered()), this, SLOT(exportFile()));
|
||||
addAction(_exportFileAction);
|
||||
_exportPDFFileAction->setMenuRole(QAction::NoRole);
|
||||
_exportPDFFileAction->setShortcut(PDF_EXPORT_SHORTCUT);
|
||||
_exportPDFFileAction->setActionGroup(_fileActionGroup);
|
||||
connect(_exportPDFFileAction, SIGNAL(triggered()), this, SLOT(exportPDFFile()));
|
||||
addAction(_exportPDFFileAction);
|
||||
_exportPNGFileAction = new QAction(QIcon(EXPORT_FILE_ICON),
|
||||
tr("Export to PNG..."), this);
|
||||
_exportPNGFileAction->setMenuRole(QAction::NoRole);
|
||||
_exportPNGFileAction->setShortcut(PNG_EXPORT_SHORTCUT);
|
||||
_exportPNGFileAction->setActionGroup(_fileActionGroup);
|
||||
connect(_exportPNGFileAction, SIGNAL(triggered()), this, SLOT(exportPNGFile()));
|
||||
addAction(_exportPNGFileAction);
|
||||
_closeFileAction = new QAction(QIcon(CLOSE_FILE_ICON), tr("Close"), this);
|
||||
_closeFileAction->setMenuRole(QAction::NoRole);
|
||||
_closeFileAction->setShortcut(CLOSE_SHORTCUT);
|
||||
@ -497,7 +504,8 @@ void GUI::createMenus()
|
||||
fileMenu->addAction(_openFileAction);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(_printFileAction);
|
||||
fileMenu->addAction(_exportFileAction);
|
||||
fileMenu->addAction(_exportPDFFileAction);
|
||||
fileMenu->addAction(_exportPNGFileAction);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(_statisticsAction);
|
||||
fileMenu->addSeparator();
|
||||
@ -928,7 +936,7 @@ void GUI::openOptions()
|
||||
Options options(_options);
|
||||
bool reload = false;
|
||||
|
||||
OptionsDialog dialog(&options, this);
|
||||
OptionsDialog dialog(options, _units, this);
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
@ -970,6 +978,7 @@ void GUI::openOptions()
|
||||
SET_TRACK_OPTION(dataUseDEM, useDEM);
|
||||
SET_TRACK_OPTION(showSecondaryElevation, showSecondaryElevation);
|
||||
SET_TRACK_OPTION(showSecondarySpeed, showSecondarySpeed);
|
||||
SET_TRACK_OPTION(useSegments, useSegments);
|
||||
|
||||
SET_ROUTE_OPTION(dataUseDEM, useDEM);
|
||||
SET_ROUTE_OPTION(showSecondaryElevation, showSecondaryElevation);
|
||||
@ -1018,9 +1027,9 @@ void GUI::printFile()
|
||||
plot(&printer);
|
||||
}
|
||||
|
||||
void GUI::exportFile()
|
||||
void GUI::exportPDFFile()
|
||||
{
|
||||
ExportDialog dialog(&_export, this);
|
||||
PDFExportDialog dialog(_pdfExport, _units, this);
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
@ -1028,16 +1037,53 @@ void GUI::exportFile()
|
||||
printer.setOutputFormat(QPrinter::PdfFormat);
|
||||
printer.setCreator(QString(APP_NAME) + QString(" ")
|
||||
+ QString(APP_VERSION));
|
||||
printer.setResolution(_export.resolution);
|
||||
printer.setOrientation(_export.orientation);
|
||||
printer.setOutputFileName(_export.fileName);
|
||||
printer.setPaperSize(_export.paperSize);
|
||||
printer.setPageMargins(_export.margins.left(), _export.margins.top(),
|
||||
_export.margins.right(), _export.margins.bottom(), QPrinter::Millimeter);
|
||||
printer.setResolution(_pdfExport.resolution);
|
||||
printer.setOrientation(_pdfExport.orientation);
|
||||
printer.setOutputFileName(_pdfExport.fileName);
|
||||
printer.setPaperSize(_pdfExport.paperSize);
|
||||
printer.setPageMargins(_pdfExport.margins.left(), _pdfExport.margins.top(),
|
||||
_pdfExport.margins.right(), _pdfExport.margins.bottom(),
|
||||
QPrinter::Millimeter);
|
||||
|
||||
plot(&printer);
|
||||
}
|
||||
|
||||
void GUI::exportPNGFile()
|
||||
{
|
||||
PNGExportDialog dialog(_pngExport, this);
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
QImage img(_pngExport.size, QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter p(&img);
|
||||
QRectF rect(0, 0, img.width(), img.height());
|
||||
QRectF contentRect(rect.adjusted(_pngExport.margins.left(),
|
||||
_pngExport.margins.top(), -_pngExport.margins.right(),
|
||||
-_pngExport.margins.bottom()));
|
||||
|
||||
if (_pngExport.antialiasing)
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.fillRect(rect, Qt::white);
|
||||
plotMainPage(&p, contentRect, 1.0, true);
|
||||
img.save(_pngExport.fileName);
|
||||
|
||||
if (!_tabs.isEmpty() && _options.separateGraphPage) {
|
||||
QImage img2(_pngExport.size.width(), (int)graphPlotHeight(rect, 1)
|
||||
+ _pngExport.margins.bottom(), QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter p2(&img2);
|
||||
QRectF rect2(0, 0, img2.width(), img2.height());
|
||||
|
||||
if (_pngExport.antialiasing)
|
||||
p2.setRenderHint(QPainter::Antialiasing);
|
||||
p2.fillRect(rect2, Qt::white);
|
||||
plotGraphsPage(&p2, contentRect, 1);
|
||||
|
||||
QFileInfo fi(_pngExport.fileName);
|
||||
img2.save(fi.absolutePath() + "/" + fi.baseName() + "-graphs."
|
||||
+ fi.suffix());
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::statistics()
|
||||
{
|
||||
QLocale l(QLocale::system());
|
||||
@ -1109,12 +1155,13 @@ void GUI::statistics()
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
void GUI::plot(QPrinter *printer)
|
||||
void GUI::plotMainPage(QPainter *painter, const QRectF &rect, qreal ratio,
|
||||
bool expand)
|
||||
{
|
||||
QLocale l(QLocale::system());
|
||||
QPainter p(printer);
|
||||
TrackInfo info;
|
||||
qreal ih, gh, mh, ratio;
|
||||
qreal ih, gh, mh;
|
||||
int sc;
|
||||
|
||||
|
||||
if (!_pathName.isNull() && _options.printName)
|
||||
@ -1150,53 +1197,91 @@ void GUI::plot(QPrinter *printer)
|
||||
if (movingTime() > 0 && _options.printMovingTime)
|
||||
info.insert(tr("Moving time"), Format::timeSpan(movingTime()));
|
||||
|
||||
qreal fsr = 1085.0 / (qMax(printer->width(), printer->height())
|
||||
/ (qreal)printer->resolution());
|
||||
ratio = p.paintEngine()->paintDevice()->logicalDpiX() / fsr;
|
||||
if (info.isEmpty()) {
|
||||
ih = 0;
|
||||
mh = 0;
|
||||
} else {
|
||||
ih = info.contentSize().height() * ratio;
|
||||
mh = ih / 2;
|
||||
info.plot(&p, QRectF(0, 0, printer->width(), ih), ratio);
|
||||
info.plot(painter, QRectF(rect.x(), rect.y(), rect.width(), ih), ratio);
|
||||
}
|
||||
if (_graphTabWidget->isVisible() && !_options.separateGraphPage) {
|
||||
qreal r = (((qreal)(printer)->width()) / (qreal)(printer->height()));
|
||||
gh = (printer->width() > printer->height())
|
||||
? 0.15 * r * (printer->height() - ih - 2*mh)
|
||||
: 0.15 * (printer->height() - ih - 2*mh);
|
||||
qreal r = rect.width() / rect.height();
|
||||
gh = (rect.width() > rect.height())
|
||||
? 0.15 * r * (rect.height() - ih - 2*mh)
|
||||
: 0.15 * (rect.height() - ih - 2*mh);
|
||||
if (gh < 150)
|
||||
gh = 150;
|
||||
sc = 2;
|
||||
GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->currentWidget());
|
||||
gt->plot(&p, QRectF(0, printer->height() - gh, printer->width(), gh),
|
||||
ratio);
|
||||
} else
|
||||
gt->plot(painter, QRectF(rect.x(), rect.y() + rect.height() - gh,
|
||||
rect.width(), gh), ratio);
|
||||
} else {
|
||||
gh = 0;
|
||||
_mapView->plot(&p, QRectF(0, ih + mh, printer->width(), printer->height()
|
||||
- (ih + 2*mh + gh)), ratio, _options.hiresPrint);
|
||||
sc = 1;
|
||||
}
|
||||
|
||||
if (_graphTabWidget->isVisible() && _options.separateGraphPage) {
|
||||
printer->newPage();
|
||||
MapView::PlotFlags flags = MapView::NoFlags;
|
||||
if (_options.hiresPrint)
|
||||
flags |= MapView::HiRes;
|
||||
if (expand)
|
||||
flags |= MapView::Expand;
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < _tabs.size(); i++)
|
||||
if (!_tabs.at(i)->isEmpty())
|
||||
cnt++;
|
||||
_mapView->plot(painter, QRectF(rect.x(), rect.y() + ih + mh, rect.width(),
|
||||
rect.height() - (ih + sc*mh + gh)), ratio, flags);
|
||||
}
|
||||
|
||||
qreal sp = ratio * 20;
|
||||
gh = qMin((printer->height() - ((cnt - 1) * sp))/(qreal)cnt,
|
||||
0.20 * printer->height());
|
||||
void GUI::plotGraphsPage(QPainter *painter, const QRectF &rect, qreal ratio)
|
||||
{
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < _tabs.size(); i++)
|
||||
if (!_tabs.at(i)->isEmpty())
|
||||
cnt++;
|
||||
|
||||
qreal y = 0;
|
||||
for (int i = 0; i < _tabs.size(); i++) {
|
||||
if (!_tabs.at(i)->isEmpty()) {
|
||||
_tabs.at(i)->plot(&p, QRectF(0, y, printer->width(), gh),
|
||||
ratio);
|
||||
y += gh + sp;
|
||||
}
|
||||
qreal sp = ratio * 20;
|
||||
qreal gh = qMin((rect.height() - ((cnt - 1) * sp))/(qreal)cnt,
|
||||
0.20 * rect.height());
|
||||
|
||||
qreal y = 0;
|
||||
for (int i = 0; i < _tabs.size(); i++) {
|
||||
if (!_tabs.at(i)->isEmpty()) {
|
||||
_tabs.at(i)->plot(painter, QRectF(rect.x(), rect.y() + y,
|
||||
rect.width(), gh), ratio);
|
||||
y += gh + sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qreal GUI::graphPlotHeight(const QRectF &rect, qreal ratio)
|
||||
{
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < _tabs.size(); i++)
|
||||
if (!_tabs.at(i)->isEmpty())
|
||||
cnt++;
|
||||
|
||||
qreal sp = ratio * 20;
|
||||
qreal gh = qMin((rect.height() - ((cnt - 1) * sp))/(qreal)cnt,
|
||||
0.20 * rect.height());
|
||||
|
||||
return cnt * gh + (cnt - 1) * sp;
|
||||
}
|
||||
|
||||
void GUI::plot(QPrinter *printer)
|
||||
{
|
||||
QPainter p(printer);
|
||||
qreal fsr = 1085.0 / (qMax(printer->width(), printer->height())
|
||||
/ (qreal)printer->resolution());
|
||||
qreal ratio = p.paintEngine()->paintDevice()->logicalDpiX() / fsr;
|
||||
QRectF rect(0, 0, printer->width(), printer->height());
|
||||
|
||||
plotMainPage(&p, rect, ratio);
|
||||
|
||||
if (!_tabs.isEmpty() && _options.separateGraphPage) {
|
||||
printer->newPage();
|
||||
plotGraphsPage(&p, rect, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::reloadFiles()
|
||||
{
|
||||
_trackCount = 0;
|
||||
@ -1551,8 +1636,7 @@ void GUI::setTimeType(TimeType type)
|
||||
|
||||
void GUI::setUnits(Units units)
|
||||
{
|
||||
_export.units = units;
|
||||
_options.units = units;
|
||||
_units = units;
|
||||
|
||||
_mapView->setUnits(units);
|
||||
for (int i = 0; i <_tabs.count(); i++)
|
||||
@ -1795,23 +1879,42 @@ void GUI::writeSettings()
|
||||
_showTicksAction->isChecked());
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(EXPORT_SETTINGS_GROUP);
|
||||
if (_export.orientation != PAPER_ORIENTATION_DEFAULT)
|
||||
settings.setValue(PAPER_ORIENTATION_SETTING, _export.orientation);
|
||||
if (_export.resolution != RESOLUTION_DEFAULT)
|
||||
settings.setValue(RESOLUTION_SETTING, _export.resolution);
|
||||
if (_export.paperSize != PAPER_SIZE_DEFAULT)
|
||||
settings.setValue(PAPER_SIZE_SETTING, _export.paperSize);
|
||||
if (_export.margins.left() != MARGIN_LEFT_DEFAULT)
|
||||
settings.setValue(MARGIN_LEFT_SETTING, _export.margins.left());
|
||||
if (_export.margins.top() != MARGIN_TOP_DEFAULT)
|
||||
settings.setValue(MARGIN_TOP_SETTING, _export.margins.top());
|
||||
if (_export.margins.right() != MARGIN_RIGHT_DEFAULT)
|
||||
settings.setValue(MARGIN_RIGHT_SETTING, _export.margins.right());
|
||||
if (_export.margins.bottom() != MARGIN_BOTTOM_DEFAULT)
|
||||
settings.setValue(MARGIN_BOTTOM_SETTING, _export.margins.bottom());
|
||||
if (_export.fileName != EXPORT_FILENAME_DEFAULT)
|
||||
settings.setValue(EXPORT_FILENAME_SETTING, _export.fileName);
|
||||
settings.beginGroup(PDF_EXPORT_SETTINGS_GROUP);
|
||||
if (_pdfExport.orientation != PAPER_ORIENTATION_DEFAULT)
|
||||
settings.setValue(PAPER_ORIENTATION_SETTING, _pdfExport.orientation);
|
||||
if (_pdfExport.resolution != RESOLUTION_DEFAULT)
|
||||
settings.setValue(RESOLUTION_SETTING, _pdfExport.resolution);
|
||||
if (_pdfExport.paperSize != PAPER_SIZE_DEFAULT)
|
||||
settings.setValue(PAPER_SIZE_SETTING, _pdfExport.paperSize);
|
||||
if (_pdfExport.margins.left() != PDF_MARGIN_LEFT_DEFAULT)
|
||||
settings.setValue(PDF_MARGIN_LEFT_SETTING, _pdfExport.margins.left());
|
||||
if (_pdfExport.margins.top() != PDF_MARGIN_TOP_DEFAULT)
|
||||
settings.setValue(PDF_MARGIN_TOP_SETTING, _pdfExport.margins.top());
|
||||
if (_pdfExport.margins.right() != PDF_MARGIN_RIGHT_DEFAULT)
|
||||
settings.setValue(PDF_MARGIN_RIGHT_SETTING, _pdfExport.margins.right());
|
||||
if (_pdfExport.margins.bottom() != PDF_MARGIN_BOTTOM_DEFAULT)
|
||||
settings.setValue(PDF_MARGIN_BOTTOM_SETTING, _pdfExport.margins.bottom());
|
||||
if (_pdfExport.fileName != PDF_FILENAME_DEFAULT)
|
||||
settings.setValue(PDF_FILENAME_SETTING, _pdfExport.fileName);
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(PNG_EXPORT_SETTINGS_GROUP);
|
||||
if (_pngExport.size.width() != PNG_WIDTH_DEFAULT)
|
||||
settings.setValue(PNG_WIDTH_SETTING, _pngExport.size.width());
|
||||
if (_pngExport.size.height() != PNG_HEIGHT_DEFAULT)
|
||||
settings.setValue(PNG_HEIGHT_SETTING, _pngExport.size.height());
|
||||
if (_pngExport.margins.left() != PNG_MARGIN_LEFT_DEFAULT)
|
||||
settings.setValue(PNG_MARGIN_LEFT_SETTING, _pngExport.margins.left());
|
||||
if (_pngExport.margins.top() != PNG_MARGIN_TOP_DEFAULT)
|
||||
settings.setValue(PNG_MARGIN_TOP_SETTING, _pngExport.margins.top());
|
||||
if (_pngExport.margins.right() != PNG_MARGIN_RIGHT_DEFAULT)
|
||||
settings.setValue(PNG_MARGIN_RIGHT_SETTING, _pngExport.margins.right());
|
||||
if (_pngExport.margins.bottom() != PNG_MARGIN_BOTTOM_DEFAULT)
|
||||
settings.setValue(PNG_MARGIN_BOTTOM_SETTING, _pngExport.margins.bottom());
|
||||
if (_pngExport.antialiasing != PNG_ANTIALIASING_DEFAULT)
|
||||
settings.setValue(PNG_ANTIALIASING_SETTING, _pngExport.antialiasing);
|
||||
if (_pngExport.fileName != PNG_FILENAME_DEFAULT)
|
||||
settings.setValue(PNG_FILENAME_SETTING, _pngExport.fileName);
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(OPTIONS_SETTINGS_GROUP);
|
||||
@ -1884,6 +1987,8 @@ void GUI::writeSettings()
|
||||
settings.setValue(TIME_ZONE_SETTING, QVariant::fromValue(
|
||||
_options.timeZone));
|
||||
#endif // ENABLE_TIMEZONES
|
||||
if (_options.useSegments != USE_SEGMENTS_DEFAULT)
|
||||
settings.setValue(USE_SEGMENTS_SETTING, _options.useSegments);
|
||||
if (_options.poiRadius != POI_RADIUS_DEFAULT)
|
||||
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
|
||||
if (_options.useOpenGL != USE_OPENGL_DEFAULT)
|
||||
@ -2075,23 +2180,42 @@ void GUI::readSettings()
|
||||
}
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(EXPORT_SETTINGS_GROUP);
|
||||
_export.orientation = (QPrinter::Orientation) settings.value(
|
||||
settings.beginGroup(PDF_EXPORT_SETTINGS_GROUP);
|
||||
_pdfExport.orientation = (QPrinter::Orientation) settings.value(
|
||||
PAPER_ORIENTATION_SETTING, PAPER_ORIENTATION_DEFAULT).toInt();
|
||||
_export.resolution = settings.value(RESOLUTION_SETTING, RESOLUTION_DEFAULT)
|
||||
_pdfExport.resolution = settings.value(RESOLUTION_SETTING,
|
||||
RESOLUTION_DEFAULT).toInt();
|
||||
_pdfExport.paperSize = (QPrinter::PaperSize) settings.value(
|
||||
PAPER_SIZE_SETTING, PAPER_SIZE_DEFAULT).toInt();
|
||||
qreal ml = settings.value(PDF_MARGIN_LEFT_SETTING, PDF_MARGIN_LEFT_DEFAULT)
|
||||
.toReal();
|
||||
qreal mt = settings.value(PDF_MARGIN_TOP_SETTING, PDF_MARGIN_TOP_DEFAULT)
|
||||
.toReal();
|
||||
qreal mr = settings.value(PDF_MARGIN_RIGHT_SETTING,
|
||||
PDF_MARGIN_RIGHT_DEFAULT).toReal();
|
||||
qreal mb = settings.value(PDF_MARGIN_BOTTOM_SETTING,
|
||||
PDF_MARGIN_BOTTOM_DEFAULT).toReal();
|
||||
_pdfExport.margins = MarginsF(ml, mt, mr, mb);
|
||||
_pdfExport.fileName = settings.value(PDF_FILENAME_SETTING,
|
||||
PDF_FILENAME_DEFAULT).toString();
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(PNG_EXPORT_SETTINGS_GROUP);
|
||||
_pngExport.size = QSize(settings.value(PNG_WIDTH_SETTING, PNG_WIDTH_DEFAULT)
|
||||
.toInt(), settings.value(PNG_HEIGHT_SETTING, PNG_HEIGHT_DEFAULT).toInt());
|
||||
int mli = settings.value(PNG_MARGIN_LEFT_SETTING, PNG_MARGIN_LEFT_DEFAULT)
|
||||
.toInt();
|
||||
_export.paperSize = (QPrinter::PaperSize) settings.value(PAPER_SIZE_SETTING,
|
||||
PAPER_SIZE_DEFAULT).toInt();
|
||||
qreal ml = settings.value(MARGIN_LEFT_SETTING, MARGIN_LEFT_DEFAULT)
|
||||
.toReal();
|
||||
qreal mt = settings.value(MARGIN_TOP_SETTING, MARGIN_TOP_DEFAULT).toReal();
|
||||
qreal mr = settings.value(MARGIN_RIGHT_SETTING, MARGIN_RIGHT_DEFAULT)
|
||||
.toReal();
|
||||
qreal mb = settings.value(MARGIN_BOTTOM_SETTING, MARGIN_BOTTOM_DEFAULT)
|
||||
.toReal();
|
||||
_export.margins = MarginsF(ml, mt, mr, mb);
|
||||
_export.fileName = settings.value(EXPORT_FILENAME_SETTING,
|
||||
EXPORT_FILENAME_DEFAULT).toString();
|
||||
int mti = settings.value(PNG_MARGIN_TOP_SETTING, PNG_MARGIN_TOP_DEFAULT)
|
||||
.toInt();
|
||||
int mri = settings.value(PNG_MARGIN_RIGHT_SETTING, PNG_MARGIN_RIGHT_DEFAULT)
|
||||
.toInt();
|
||||
int mbi = settings.value(PNG_MARGIN_BOTTOM_SETTING, PNG_MARGIN_BOTTOM_DEFAULT)
|
||||
.toInt();
|
||||
_pngExport.margins = QMargins(mli, mti, mri, mbi);
|
||||
_pngExport.antialiasing = settings.value(PNG_ANTIALIASING_SETTING,
|
||||
PNG_ANTIALIASING_DEFAULT).toBool();
|
||||
_pngExport.fileName = settings.value(PNG_FILENAME_SETTING,
|
||||
PNG_FILENAME_DEFAULT).toString();
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(OPTIONS_SETTINGS_GROUP);
|
||||
@ -2159,6 +2283,8 @@ void GUI::readSettings()
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
_options.timeZone = settings.value(TIME_ZONE_SETTING).value<TimeZoneInfo>();
|
||||
#endif // ENABLE_TIMEZONES
|
||||
_options.useSegments = settings.value(USE_SEGMENTS_SETTING,
|
||||
USE_SEGMENTS_DEFAULT).toBool();
|
||||
_options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING,
|
||||
AUTOMATIC_PAUSE_DEFAULT).toBool();
|
||||
_options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING,
|
||||
@ -2250,6 +2376,7 @@ void GUI::readSettings()
|
||||
Track::useDEM(_options.dataUseDEM);
|
||||
Track::showSecondaryElevation(_options.showSecondaryElevation);
|
||||
Track::showSecondarySpeed(_options.showSecondarySpeed);
|
||||
Track::useSegments(_options.useSegments);
|
||||
Route::useDEM(_options.dataUseDEM);
|
||||
Route::showSecondaryElevation(_options.showSecondaryElevation);
|
||||
Waypoint::useDEM(_options.dataUseDEM);
|
||||
|
@ -10,7 +10,8 @@
|
||||
#include "units.h"
|
||||
#include "timetype.h"
|
||||
#include "format.h"
|
||||
#include "exportdialog.h"
|
||||
#include "pdfexportdialog.h"
|
||||
#include "pngexportdialog.h"
|
||||
#include "optionsdialog.h"
|
||||
|
||||
class QMenu;
|
||||
@ -45,7 +46,8 @@ private slots:
|
||||
void keys();
|
||||
void paths();
|
||||
void printFile();
|
||||
void exportFile();
|
||||
void exportPDFFile();
|
||||
void exportPNGFile();
|
||||
void openFile();
|
||||
void closeAll();
|
||||
void reloadFiles();
|
||||
@ -97,6 +99,10 @@ private:
|
||||
void loadPOIs();
|
||||
void closeFiles();
|
||||
void plot(QPrinter *printer);
|
||||
void plotMainPage(QPainter *painter, const QRectF &rect, qreal ratio,
|
||||
bool expand = false);
|
||||
void plotGraphsPage(QPainter *painter, const QRectF &rect, qreal ratio);
|
||||
qreal graphPlotHeight(const QRectF &rect, qreal ratio);
|
||||
|
||||
QAction *createPOIFileAction(const QString &fileName);
|
||||
MapAction *createMapAction(Map *map);
|
||||
@ -113,7 +119,6 @@ private:
|
||||
bool openPOIFile(const QString &fileName);
|
||||
bool loadFile(const QString &fileName);
|
||||
bool loadMap(const QString &fileName);
|
||||
void exportFile(const QString &fileName);
|
||||
void updateStatusBarInfo();
|
||||
void updateWindowTitle();
|
||||
void updateNavigationActions();
|
||||
@ -153,7 +158,8 @@ private:
|
||||
QAction *_aboutAction;
|
||||
QAction *_aboutQtAction;
|
||||
QAction *_printFileAction;
|
||||
QAction *_exportFileAction;
|
||||
QAction *_exportPDFFileAction;
|
||||
QAction *_exportPNGFileAction;
|
||||
QAction *_openFileAction;
|
||||
QAction *_closeFileAction;
|
||||
QAction *_reloadFileAction;
|
||||
@ -228,10 +234,13 @@ private:
|
||||
QList<QByteArray> _windowStates;
|
||||
int _frameStyle;
|
||||
|
||||
Export _export;
|
||||
PDFExport _pdfExport;
|
||||
PNGExport _pngExport;
|
||||
Options _options;
|
||||
|
||||
QString _dataDir, _mapDir, _poiDir;
|
||||
|
||||
Units _units;
|
||||
};
|
||||
|
||||
#endif // GUI_H
|
||||
|
@ -18,7 +18,8 @@
|
||||
#define OPEN_SHORTCUT QKeySequence(QKeySequence::Open)
|
||||
#define CLOSE_SHORTCUT QKeySequence(QKeySequence::Close)
|
||||
#define RELOAD_SHORTCUT QKeySequence(QKeySequence::Refresh)
|
||||
#define EXPORT_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_E)
|
||||
#define PDF_EXPORT_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_E)
|
||||
#define PNG_EXPORT_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_X)
|
||||
#define SHOW_POI_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_P)
|
||||
#define SHOW_MAP_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_M)
|
||||
#define NEXT_MAP_SHORTCUT QKeySequence(QKeySequence::Forward)
|
||||
|
@ -531,7 +531,7 @@ void MapView::keyPressEvent(QKeyEvent *event)
|
||||
}
|
||||
|
||||
void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
|
||||
bool hires)
|
||||
PlotFlags flags)
|
||||
{
|
||||
QRect orig, adj;
|
||||
qreal ratio, diff, q;
|
||||
@ -559,10 +559,18 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
|
||||
diff = (orig.height() * ratio) - orig.width();
|
||||
adj = orig.adjusted(-diff/2, 0, diff/2, 0);
|
||||
}
|
||||
q = (target.width() / scale) / adj.width();
|
||||
|
||||
// Expand the view if plotting into a bitmap
|
||||
if (flags & Expand) {
|
||||
qreal xdiff = (target.width() - adj.width()) / 2.0;
|
||||
qreal ydiff = (target.height() - adj.height()) / 2.0;
|
||||
adj.adjust(-xdiff, -ydiff, xdiff, ydiff);
|
||||
q = 1.0;
|
||||
} else
|
||||
q = (target.width() / scale) / adj.width();
|
||||
|
||||
// Adjust the view for printing
|
||||
if (hires) {
|
||||
if (flags & HiRes) {
|
||||
zoom = _map->zoom();
|
||||
QRectF vr(mapToScene(orig).boundingRect());
|
||||
origScene = vr.center();
|
||||
@ -594,7 +602,7 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
|
||||
render(painter, target, adj);
|
||||
|
||||
// Revert view changes to display mode
|
||||
if (hires) {
|
||||
if (flags & HiRes) {
|
||||
_map->setZoom(zoom);
|
||||
rescale();
|
||||
centerOn(origScene);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QVector>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QFlags>
|
||||
#include "common/rectc.h"
|
||||
#include "common/config.h"
|
||||
#include "data/waypoint.h"
|
||||
@ -38,6 +39,13 @@ class MapView : public QGraphicsView
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Flag {
|
||||
NoFlags = 0,
|
||||
HiRes = 1,
|
||||
Expand = 2
|
||||
};
|
||||
Q_DECLARE_FLAGS(PlotFlags, Flag)
|
||||
|
||||
MapView(Map *map, POI *poi, QWidget *parent = 0);
|
||||
|
||||
QList<PathItem *> loadData(const Data &data);
|
||||
@ -46,7 +54,8 @@ public:
|
||||
void setPOI(POI *poi);
|
||||
void setMap(Map *map);
|
||||
|
||||
void plot(QPainter *painter, const QRectF &target, qreal scale, bool hires);
|
||||
void plot(QPainter *painter, const QRectF &target, qreal scale,
|
||||
PlotFlags flags);
|
||||
|
||||
void clear();
|
||||
|
||||
|
@ -16,15 +16,22 @@ public:
|
||||
qreal right() const {return _right;}
|
||||
qreal bottom() const {return _bottom;}
|
||||
|
||||
qreal &rleft() {return _left;}
|
||||
qreal &rtop() {return _top;}
|
||||
qreal &rright() {return _right;}
|
||||
qreal &rbottom() {return _bottom;}
|
||||
|
||||
private:
|
||||
qreal _left, _top, _right, _bottom;
|
||||
};
|
||||
|
||||
inline MarginsF operator*(const MarginsF &margins, qreal factor)
|
||||
{
|
||||
return MarginsF(margins.left() * factor, margins.top() * factor,
|
||||
margins.right() * factor, margins.bottom() * factor);
|
||||
}
|
||||
|
||||
inline MarginsF operator/(const MarginsF &margins, qreal factor)
|
||||
{
|
||||
return MarginsF(margins.left() / factor, margins.top() / factor,
|
||||
margins.right() / factor, margins.bottom() / factor);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
inline QDebug operator<<(QDebug dbg, const MarginsF &margins)
|
||||
{
|
||||
|
111
src/GUI/marginswidget.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include <QSpinBox>
|
||||
#include <QGridLayout>
|
||||
#include "units.h"
|
||||
#include "marginswidget.h"
|
||||
|
||||
MarginsWidget::MarginsWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
_top = new QSpinBox();
|
||||
_bottom = new QSpinBox();
|
||||
_left = new QSpinBox();
|
||||
_right = new QSpinBox();
|
||||
|
||||
_top->setMaximumWidth(_top->sizeHint().width());
|
||||
_bottom->setMaximumWidth(_bottom->sizeHint().width());
|
||||
_left->setMaximumWidth(_left->sizeHint().width());
|
||||
_right->setMaximumWidth(_right->sizeHint().width());
|
||||
|
||||
QGridLayout *layout = new QGridLayout();
|
||||
layout->addWidget(_top, 0, 0, 1, 2, Qt::AlignCenter);
|
||||
layout->addWidget(_left, 1, 0, 1, 1, Qt::AlignRight);
|
||||
layout->addWidget(_right, 1, 1, 1, 1, Qt::AlignLeft);
|
||||
layout->addWidget(_bottom, 2, 0, 1, 2, Qt::AlignCenter);
|
||||
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MarginsWidget::setValue(const QMargins &value)
|
||||
{
|
||||
_top->setValue(value.top());
|
||||
_bottom->setValue(value.bottom());
|
||||
_left->setValue(value.left());
|
||||
_right->setValue(value.right());
|
||||
}
|
||||
|
||||
void MarginsWidget::setUnits(const QString &units)
|
||||
{
|
||||
_top->setSuffix(UNIT_SPACE + units);
|
||||
_bottom->setSuffix(UNIT_SPACE + units);
|
||||
_left->setSuffix(UNIT_SPACE + units);
|
||||
_right->setSuffix(UNIT_SPACE + units);
|
||||
|
||||
_top->setMaximumWidth(_top->sizeHint().width());
|
||||
_bottom->setMaximumWidth(_bottom->sizeHint().width());
|
||||
_left->setMaximumWidth(_left->sizeHint().width());
|
||||
_right->setMaximumWidth(_right->sizeHint().width());
|
||||
}
|
||||
|
||||
QMargins MarginsWidget::value() const
|
||||
{
|
||||
return QMargins(_left->value(), _top->value(), _right->value(),
|
||||
_bottom->value());
|
||||
}
|
||||
|
||||
|
||||
MarginsFWidget::MarginsFWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
_top = new QDoubleSpinBox();
|
||||
_bottom = new QDoubleSpinBox();
|
||||
_left = new QDoubleSpinBox();
|
||||
_right = new QDoubleSpinBox();
|
||||
|
||||
_top->setMaximumWidth(_top->sizeHint().width());
|
||||
_bottom->setMaximumWidth(_bottom->sizeHint().width());
|
||||
_left->setMaximumWidth(_left->sizeHint().width());
|
||||
_right->setMaximumWidth(_right->sizeHint().width());
|
||||
|
||||
QGridLayout *layout = new QGridLayout();
|
||||
layout->addWidget(_top, 0, 0, 1, 2, Qt::AlignCenter);
|
||||
layout->addWidget(_left, 1, 0, 1, 1, Qt::AlignRight);
|
||||
layout->addWidget(_right, 1, 1, 1, 1, Qt::AlignLeft);
|
||||
layout->addWidget(_bottom, 2, 0, 1, 2, Qt::AlignCenter);
|
||||
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MarginsFWidget::setValue(const MarginsF &value)
|
||||
{
|
||||
_top->setValue(value.top());
|
||||
_bottom->setValue(value.bottom());
|
||||
_left->setValue(value.left());
|
||||
_right->setValue(value.right());
|
||||
}
|
||||
|
||||
void MarginsFWidget::setUnits(const QString &units)
|
||||
{
|
||||
_top->setSuffix(UNIT_SPACE + units);
|
||||
_bottom->setSuffix(UNIT_SPACE + units);
|
||||
_left->setSuffix(UNIT_SPACE + units);
|
||||
_right->setSuffix(UNIT_SPACE + units);
|
||||
|
||||
_top->setMaximumWidth(_top->sizeHint().width());
|
||||
_bottom->setMaximumWidth(_bottom->sizeHint().width());
|
||||
_left->setMaximumWidth(_left->sizeHint().width());
|
||||
_right->setMaximumWidth(_right->sizeHint().width());
|
||||
}
|
||||
|
||||
void MarginsFWidget::setSingleStep(qreal step)
|
||||
{
|
||||
_top->setSingleStep(step);
|
||||
_bottom->setSingleStep(step);
|
||||
_left->setSingleStep(step);
|
||||
_right->setSingleStep(step);
|
||||
}
|
||||
|
||||
MarginsF MarginsFWidget::value() const
|
||||
{
|
||||
return MarginsF(_left->value(), _top->value(), _right->value(),
|
||||
_bottom->value());
|
||||
}
|
48
src/GUI/marginswidget.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef MARGINSWIDGET_H
|
||||
#define MARGINSWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMargins>
|
||||
#include "margins.h"
|
||||
|
||||
class QSpinBox;
|
||||
class QDoubleSpinBox;
|
||||
|
||||
class MarginsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MarginsWidget(QWidget *parent = 0);
|
||||
|
||||
QMargins value() const;
|
||||
void setValue(const QMargins &value);
|
||||
void setUnits(const QString &units);
|
||||
|
||||
private:
|
||||
QSpinBox *_top;
|
||||
QSpinBox *_bottom;
|
||||
QSpinBox *_left;
|
||||
QSpinBox *_right;
|
||||
};
|
||||
|
||||
class MarginsFWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MarginsFWidget(QWidget *parent = 0);
|
||||
|
||||
MarginsF value() const;
|
||||
void setValue(const MarginsF &value);
|
||||
void setUnits(const QString &units);
|
||||
void setSingleStep(qreal step);
|
||||
|
||||
private:
|
||||
QDoubleSpinBox *_top;
|
||||
QDoubleSpinBox *_bottom;
|
||||
QDoubleSpinBox *_left;
|
||||
QDoubleSpinBox *_right;
|
||||
};
|
||||
|
||||
#endif // MARGINSWIDGET_H
|
@ -56,12 +56,12 @@ QWidget *OptionsDialog::createMapPage()
|
||||
+ projections.at(i).value();
|
||||
_projection->addItem(text, QVariant(projections.at(i).key()));
|
||||
}
|
||||
_projection->setCurrentIndex(_projection->findData(_options->projection));
|
||||
_projection->setCurrentIndex(_projection->findData(_options.projection));
|
||||
|
||||
#ifdef ENABLE_HIDPI
|
||||
_hidpi = new QRadioButton(tr("High-resolution"));
|
||||
_lodpi = new QRadioButton(tr("Standard"));
|
||||
if (_options->hidpiMap)
|
||||
if (_options.hidpiMap)
|
||||
_hidpi->setChecked(true);
|
||||
else
|
||||
_lodpi->setChecked(true);
|
||||
@ -112,10 +112,10 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
{
|
||||
// Tracks
|
||||
_trackWidth = new QSpinBox();
|
||||
_trackWidth->setValue(_options->trackWidth);
|
||||
_trackWidth->setValue(_options.trackWidth);
|
||||
_trackWidth->setMinimum(1);
|
||||
_trackStyle = new StyleComboBox();
|
||||
_trackStyle->setValue(_options->trackStyle);
|
||||
_trackStyle->setValue(_options.trackStyle);
|
||||
QFormLayout *trackLayout = new QFormLayout();
|
||||
#ifdef Q_OS_MAC
|
||||
trackLayout->addRow(tr("Track width:"), _trackWidth);
|
||||
@ -129,10 +129,10 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
|
||||
// Routes
|
||||
_routeWidth = new QSpinBox();
|
||||
_routeWidth->setValue(_options->routeWidth);
|
||||
_routeWidth->setValue(_options.routeWidth);
|
||||
_routeWidth->setMinimum(1);
|
||||
_routeStyle = new StyleComboBox();
|
||||
_routeStyle->setValue(_options->routeStyle);
|
||||
_routeStyle->setValue(_options.routeStyle);
|
||||
QFormLayout *routeLayout = new QFormLayout();
|
||||
#ifdef Q_OS_MAC
|
||||
routeLayout->addRow(tr("Route width:"), _routeWidth);
|
||||
@ -146,11 +146,11 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
|
||||
// Areas
|
||||
_areaWidth = new QSpinBox();
|
||||
_areaWidth->setValue(_options->areaWidth);
|
||||
_areaWidth->setValue(_options.areaWidth);
|
||||
_areaStyle = new StyleComboBox();
|
||||
_areaStyle->setValue(_options->areaStyle);
|
||||
_areaStyle->setValue(_options.areaStyle);
|
||||
_areaOpacity = new PercentSlider();
|
||||
_areaOpacity->setValue(_options->areaOpacity);
|
||||
_areaOpacity->setValue(_options.areaOpacity);
|
||||
QFormLayout *areaLayout = new QFormLayout();
|
||||
#ifdef Q_OS_MAC
|
||||
areaLayout->addRow(tr("Area border width:"), _areaWidth);
|
||||
@ -166,15 +166,15 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
|
||||
// Palette & antialiasing
|
||||
_baseColor = new ColorBox();
|
||||
_baseColor->setColor(_options->palette.color());
|
||||
_baseColor->setColor(_options.palette.color());
|
||||
_colorOffset = new PercentSlider();
|
||||
_colorOffset->setValue(_options->palette.shift() * 100);
|
||||
_colorOffset->setValue(_options.palette.shift() * 100);
|
||||
QFormLayout *paletteLayout = new QFormLayout();
|
||||
paletteLayout->addRow(tr("Base color:"), _baseColor);
|
||||
paletteLayout->addRow(tr("Palette shift:"), _colorOffset);
|
||||
|
||||
_pathAA = new QCheckBox(tr("Use anti-aliasing"));
|
||||
_pathAA->setChecked(_options->pathAntiAliasing);
|
||||
_pathAA->setChecked(_options.pathAntiAliasing);
|
||||
QFormLayout *pathAALayout = new QFormLayout();
|
||||
pathAALayout->addWidget(_pathAA);
|
||||
|
||||
@ -201,9 +201,9 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
// Waypoints
|
||||
_waypointSize = new QSpinBox();
|
||||
_waypointSize->setMinimum(1);
|
||||
_waypointSize->setValue(_options->waypointSize);
|
||||
_waypointSize->setValue(_options.waypointSize);
|
||||
_waypointColor = new ColorBox();
|
||||
_waypointColor->setColor(_options->waypointColor);
|
||||
_waypointColor->setColor(_options.waypointColor);
|
||||
QFormLayout *waypointLayout = new QFormLayout();
|
||||
#ifdef Q_OS_MAC
|
||||
waypointLayout->addRow(tr("Waypoint color:"), _waypointColor);
|
||||
@ -217,9 +217,9 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
|
||||
_poiSize = new QSpinBox();
|
||||
_poiSize->setMinimum(1);
|
||||
_poiSize->setValue(_options->poiSize);
|
||||
_poiSize->setValue(_options.poiSize);
|
||||
_poiColor = new ColorBox();
|
||||
_poiColor->setColor(_options->poiColor);
|
||||
_poiColor->setColor(_options.poiColor);
|
||||
QFormLayout *poiLayout = new QFormLayout();
|
||||
#ifdef Q_OS_MAC
|
||||
poiLayout->addRow(tr("POI color:"), _poiColor);
|
||||
@ -247,9 +247,9 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
|
||||
// Graphs
|
||||
_sliderColor = new ColorBox();
|
||||
_sliderColor->setColor(_options->sliderColor);
|
||||
_sliderColor->setColor(_options.sliderColor);
|
||||
_graphWidth = new QSpinBox();
|
||||
_graphWidth->setValue(_options->graphWidth);
|
||||
_graphWidth->setValue(_options.graphWidth);
|
||||
_graphWidth->setMinimum(1);
|
||||
|
||||
QFormLayout *graphLayout = new QFormLayout();
|
||||
@ -257,7 +257,7 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
graphLayout->addRow(tr("Slider color:"), _sliderColor);
|
||||
|
||||
_graphAA = new QCheckBox(tr("Use anti-aliasing"));
|
||||
_graphAA->setChecked(_options->graphAntiAliasing);
|
||||
_graphAA->setChecked(_options.graphAntiAliasing);
|
||||
QFormLayout *graphAALayout = new QFormLayout();
|
||||
graphAALayout->addWidget(_graphAA);
|
||||
|
||||
@ -271,9 +271,9 @@ QWidget *OptionsDialog::createAppearancePage()
|
||||
|
||||
// Map
|
||||
_mapOpacity = new PercentSlider();
|
||||
_mapOpacity->setValue(_options->mapOpacity);
|
||||
_mapOpacity->setValue(_options.mapOpacity);
|
||||
_backgroundColor = new ColorBox();
|
||||
_backgroundColor->setColor(_options->backgroundColor);
|
||||
_backgroundColor->setColor(_options.backgroundColor);
|
||||
_backgroundColor->enableAlphaChannel(false);
|
||||
|
||||
QFormLayout *mapLayout = new QFormLayout();
|
||||
@ -301,19 +301,19 @@ QWidget *OptionsDialog::createDataPage()
|
||||
QString filterToolTip = tr("Moving average window size");
|
||||
|
||||
_elevationFilter = new OddSpinBox();
|
||||
_elevationFilter->setValue(_options->elevationFilter);
|
||||
_elevationFilter->setValue(_options.elevationFilter);
|
||||
_elevationFilter->setToolTip(filterToolTip);
|
||||
_speedFilter = new OddSpinBox();
|
||||
_speedFilter->setValue(_options->speedFilter);
|
||||
_speedFilter->setValue(_options.speedFilter);
|
||||
_speedFilter->setToolTip(filterToolTip);
|
||||
_heartRateFilter = new OddSpinBox();
|
||||
_heartRateFilter->setValue(_options->heartRateFilter);
|
||||
_heartRateFilter->setValue(_options.heartRateFilter);
|
||||
_heartRateFilter->setToolTip(filterToolTip);
|
||||
_cadenceFilter = new OddSpinBox();
|
||||
_cadenceFilter->setValue(_options->cadenceFilter);
|
||||
_cadenceFilter->setValue(_options.cadenceFilter);
|
||||
_cadenceFilter->setToolTip(filterToolTip);
|
||||
_powerFilter = new OddSpinBox();
|
||||
_powerFilter->setValue(_options->powerFilter);
|
||||
_powerFilter->setValue(_options.powerFilter);
|
||||
_powerFilter->setToolTip(filterToolTip);
|
||||
|
||||
QFormLayout *smoothLayout = new QFormLayout();
|
||||
@ -328,7 +328,7 @@ QWidget *OptionsDialog::createDataPage()
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
_outlierEliminate = new QCheckBox(tr("Eliminate GPS outliers"));
|
||||
_outlierEliminate->setChecked(_options->outlierEliminate);
|
||||
_outlierEliminate->setChecked(_options.outlierEliminate);
|
||||
|
||||
QFormLayout *outlierLayout = new QFormLayout();
|
||||
outlierLayout->addWidget(_outlierEliminate);
|
||||
@ -349,7 +349,7 @@ QWidget *OptionsDialog::createDataPage()
|
||||
|
||||
_automaticPause = new QRadioButton(tr("Automatic"));
|
||||
_manualPause = new QRadioButton(tr("Custom"));
|
||||
if (_options->automaticPause)
|
||||
if (_options.automaticPause)
|
||||
_automaticPause->setChecked(true);
|
||||
else
|
||||
_manualPause->setChecked(true);
|
||||
@ -359,20 +359,20 @@ QWidget *OptionsDialog::createDataPage()
|
||||
_pauseSpeed->setSingleStep(0.1);
|
||||
_pauseSpeed->setMinimum(0.1);
|
||||
_pauseSpeed->setEnabled(_manualPause->isChecked());
|
||||
if (_options->units == Imperial) {
|
||||
_pauseSpeed->setValue(_options->pauseSpeed * MS2MIH);
|
||||
if (_units == Imperial) {
|
||||
_pauseSpeed->setValue(_options.pauseSpeed * MS2MIH);
|
||||
_pauseSpeed->setSuffix(UNIT_SPACE + tr("mi/h"));
|
||||
} else if (_options->units == Nautical) {
|
||||
_pauseSpeed->setValue(_options->pauseSpeed * MS2KN);
|
||||
} else if (_units == Nautical) {
|
||||
_pauseSpeed->setValue(_options.pauseSpeed * MS2KN);
|
||||
_pauseSpeed->setSuffix(UNIT_SPACE + tr("kn"));
|
||||
} else {
|
||||
_pauseSpeed->setValue(_options->pauseSpeed * MS2KMH);
|
||||
_pauseSpeed->setValue(_options.pauseSpeed * MS2KMH);
|
||||
_pauseSpeed->setSuffix(UNIT_SPACE + tr("km/h"));
|
||||
}
|
||||
_pauseInterval = new QSpinBox();
|
||||
_pauseInterval->setMinimum(1);
|
||||
_pauseInterval->setSuffix(UNIT_SPACE + tr("s"));
|
||||
_pauseInterval->setValue(_options->pauseInterval);
|
||||
_pauseInterval->setValue(_options.pauseInterval);
|
||||
_pauseInterval->setEnabled(_manualPause->isChecked());
|
||||
|
||||
connect(_automaticPause, SIGNAL(toggled(bool)), this,
|
||||
@ -400,29 +400,29 @@ QWidget *OptionsDialog::createDataPage()
|
||||
|
||||
_computedSpeed = new QRadioButton(tr("Computed from distance/time"));
|
||||
_reportedSpeed = new QRadioButton(tr("Recorded by device"));
|
||||
if (_options->useReportedSpeed)
|
||||
if (_options.useReportedSpeed)
|
||||
_reportedSpeed->setChecked(true);
|
||||
else
|
||||
_computedSpeed->setChecked(true);
|
||||
_showSecondarySpeed = new QCheckBox(tr("Show secondary speed"));
|
||||
_showSecondarySpeed->setChecked(_options->showSecondarySpeed);
|
||||
_showSecondarySpeed->setChecked(_options.showSecondarySpeed);
|
||||
|
||||
_dataGPSElevation = new QRadioButton(tr("GPS data"));
|
||||
_dataDEMElevation = new QRadioButton(tr("DEM data"));
|
||||
if (_options->dataUseDEM)
|
||||
if (_options.dataUseDEM)
|
||||
_dataDEMElevation->setChecked(true);
|
||||
else
|
||||
_dataGPSElevation->setChecked(true);
|
||||
_showSecondaryElevation = new QCheckBox(tr("Show secondary elevation"));
|
||||
_showSecondaryElevation->setChecked(_options->showSecondaryElevation);
|
||||
_showSecondaryElevation->setChecked(_options.showSecondaryElevation);
|
||||
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
_utcZone = new QRadioButton(tr("UTC"));
|
||||
_systemZone = new QRadioButton(tr("System"));
|
||||
_customZone = new QRadioButton(tr("Custom"));
|
||||
if (_options->timeZone.type() == TimeZoneInfo::UTC)
|
||||
if (_options.timeZone.type() == TimeZoneInfo::UTC)
|
||||
_utcZone->setChecked(true);
|
||||
else if (_options->timeZone.type() == TimeZoneInfo::System)
|
||||
else if (_options.timeZone.type() == TimeZoneInfo::System)
|
||||
_systemZone->setChecked(true);
|
||||
else
|
||||
_customZone->setChecked(true);
|
||||
@ -431,7 +431,7 @@ QWidget *OptionsDialog::createDataPage()
|
||||
QList<QByteArray> zones = QTimeZone::availableTimeZoneIds();
|
||||
for (int i = 0; i < zones.size(); i++)
|
||||
_timeZone->addItem(zones.at(i));
|
||||
_timeZone->setCurrentText(_options->timeZone.customZone().id());
|
||||
_timeZone->setCurrentText(_options.timeZone.customZone().id());
|
||||
connect(_customZone, SIGNAL(toggled(bool)), _timeZone,
|
||||
SLOT(setEnabled(bool)));
|
||||
QHBoxLayout *customZoneLayout = new QHBoxLayout();
|
||||
@ -439,6 +439,9 @@ QWidget *OptionsDialog::createDataPage()
|
||||
customZoneLayout->addWidget(_timeZone);
|
||||
#endif // ENABLE_TIMEZONES
|
||||
|
||||
_useSegments = new QCheckBox(tr("Use segments"));
|
||||
_useSegments->setChecked(_options.useSegments);
|
||||
|
||||
QWidget *sourceTab = new QWidget();
|
||||
QVBoxLayout *sourceTabLayout = new QVBoxLayout();
|
||||
|
||||
@ -477,6 +480,7 @@ QWidget *OptionsDialog::createDataPage()
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
formLayout->addRow(tr("Time zone:"), zoneOptions);
|
||||
#endif // ENABLE_TIMEZONES
|
||||
formLayout->addRow(_useSegments);
|
||||
|
||||
sourceTabLayout->addLayout(formLayout);
|
||||
#else // Q_OS_MAC
|
||||
@ -485,6 +489,7 @@ QWidget *OptionsDialog::createDataPage()
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
QFormLayout *timeZoneLayout = new QFormLayout();
|
||||
#endif // ENABLE_TIMEZONES
|
||||
QFormLayout *segmentsLayout = new QFormLayout();
|
||||
|
||||
speedLayout->addWidget(_computedSpeed);
|
||||
speedLayout->addWidget(_reportedSpeed);
|
||||
@ -510,11 +515,14 @@ QWidget *OptionsDialog::createDataPage()
|
||||
timeZoneBox->setLayout(timeZoneLayout);
|
||||
#endif // ENABLE_TIMEZONES
|
||||
|
||||
segmentsLayout->addWidget(_useSegments);
|
||||
|
||||
sourceTabLayout->addWidget(speedBox);
|
||||
sourceTabLayout->addWidget(elevationBox);
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
sourceTabLayout->addWidget(timeZoneBox);
|
||||
#endif // ENABLE_TIMEZONES
|
||||
sourceTabLayout->addLayout(segmentsLayout);
|
||||
#endif // Q_OS_MAC
|
||||
sourceTabLayout->addStretch();
|
||||
sourceTab->setLayout(sourceTabLayout);
|
||||
@ -533,14 +541,14 @@ QWidget *OptionsDialog::createPOIPage()
|
||||
_poiRadius = new QDoubleSpinBox();
|
||||
_poiRadius->setSingleStep(1);
|
||||
_poiRadius->setDecimals(1);
|
||||
if (_options->units == Imperial) {
|
||||
_poiRadius->setValue(_options->poiRadius / MIINM);
|
||||
if (_units == Imperial) {
|
||||
_poiRadius->setValue(_options.poiRadius / MIINM);
|
||||
_poiRadius->setSuffix(UNIT_SPACE + tr("mi"));
|
||||
} else if (_options->units == Nautical) {
|
||||
_poiRadius->setValue(_options->poiRadius / NMIINM);
|
||||
} else if (_units == Nautical) {
|
||||
_poiRadius->setValue(_options.poiRadius / NMIINM);
|
||||
_poiRadius->setSuffix(UNIT_SPACE + tr("nmi"));
|
||||
} else {
|
||||
_poiRadius->setValue(_options->poiRadius / KMINM);
|
||||
_poiRadius->setValue(_options.poiRadius / KMINM);
|
||||
_poiRadius->setSuffix(UNIT_SPACE + tr("km"));
|
||||
}
|
||||
|
||||
@ -560,7 +568,7 @@ QWidget *OptionsDialog::createExportPage()
|
||||
{
|
||||
_wysiwyg = new QRadioButton(tr("WYSIWYG"));
|
||||
_hires = new QRadioButton(tr("High-Resolution"));
|
||||
if (_options->hiresPrint)
|
||||
if (_options.hiresPrint)
|
||||
_hires->setChecked(true);
|
||||
else
|
||||
_wysiwyg->setChecked(true);
|
||||
@ -589,17 +597,17 @@ QWidget *OptionsDialog::createExportPage()
|
||||
|
||||
|
||||
_name = new QCheckBox(tr("Name"));
|
||||
_name->setChecked(_options->printName);
|
||||
_name->setChecked(_options.printName);
|
||||
_date = new QCheckBox(tr("Date"));
|
||||
_date->setChecked(_options->printDate);
|
||||
_date->setChecked(_options.printDate);
|
||||
_distance = new QCheckBox(tr("Distance"));
|
||||
_distance->setChecked(_options->printDistance);
|
||||
_distance->setChecked(_options.printDistance);
|
||||
_time = new QCheckBox(tr("Time"));
|
||||
_time->setChecked(_options->printTime);
|
||||
_time->setChecked(_options.printTime);
|
||||
_movingTime = new QCheckBox(tr("Moving time"));
|
||||
_movingTime->setChecked(_options->printMovingTime);
|
||||
_movingTime->setChecked(_options.printMovingTime);
|
||||
_itemCount = new QCheckBox(tr("Item count (>1)"));
|
||||
_itemCount->setChecked(_options->printItemCount);
|
||||
_itemCount->setChecked(_options.printItemCount);
|
||||
|
||||
QFormLayout *headerTabLayout = new QFormLayout();
|
||||
headerTabLayout->addWidget(_name);
|
||||
@ -614,7 +622,7 @@ QWidget *OptionsDialog::createExportPage()
|
||||
|
||||
|
||||
_separateGraphPage = new QCheckBox(tr("Separate graph page"));
|
||||
_separateGraphPage->setChecked(_options->separateGraphPage);
|
||||
_separateGraphPage->setChecked(_options.separateGraphPage);
|
||||
|
||||
QFormLayout *graphTabLayout = new QFormLayout();
|
||||
graphTabLayout->addWidget(_separateGraphPage);
|
||||
@ -633,23 +641,23 @@ QWidget *OptionsDialog::createExportPage()
|
||||
QWidget *OptionsDialog::createSystemPage()
|
||||
{
|
||||
_useOpenGL = new QCheckBox(tr("Use OpenGL"));
|
||||
_useOpenGL->setChecked(_options->useOpenGL);
|
||||
_useOpenGL->setChecked(_options.useOpenGL);
|
||||
#ifdef ENABLE_HTTP2
|
||||
_enableHTTP2 = new QCheckBox(tr("Enable HTTP/2"));
|
||||
_enableHTTP2->setChecked(_options->enableHTTP2);
|
||||
_enableHTTP2->setChecked(_options.enableHTTP2);
|
||||
#endif // ENABLE_HTTP2
|
||||
|
||||
_pixmapCache = new QSpinBox();
|
||||
_pixmapCache->setMinimum(16);
|
||||
_pixmapCache->setMaximum(1024);
|
||||
_pixmapCache->setSuffix(UNIT_SPACE + tr("MB"));
|
||||
_pixmapCache->setValue(_options->pixmapCache);
|
||||
_pixmapCache->setValue(_options.pixmapCache);
|
||||
|
||||
_connectionTimeout = new QSpinBox();
|
||||
_connectionTimeout->setMinimum(30);
|
||||
_connectionTimeout->setMaximum(120);
|
||||
_connectionTimeout->setSuffix(UNIT_SPACE + tr("s"));
|
||||
_connectionTimeout->setValue(_options->connectionTimeout);
|
||||
_connectionTimeout->setValue(_options.connectionTimeout);
|
||||
|
||||
QFormLayout *formLayout = new QFormLayout();
|
||||
formLayout->addRow(tr("Image cache size:"), _pixmapCache);
|
||||
@ -674,8 +682,8 @@ QWidget *OptionsDialog::createSystemPage()
|
||||
return systemPage;
|
||||
}
|
||||
|
||||
OptionsDialog::OptionsDialog(Options *options, QWidget *parent)
|
||||
: QDialog(parent), _options(options)
|
||||
OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
|
||||
: QDialog(parent), _options(options), _units(units)
|
||||
{
|
||||
QStackedWidget *pages = new QStackedWidget();
|
||||
pages->addWidget(createAppearancePage());
|
||||
@ -727,81 +735,82 @@ OptionsDialog::OptionsDialog(Options *options, QWidget *parent)
|
||||
|
||||
void OptionsDialog::accept()
|
||||
{
|
||||
_options->palette.setColor(_baseColor->color());
|
||||
_options->palette.setShift(_colorOffset->value() / 100.0);
|
||||
_options->mapOpacity = _mapOpacity->value();
|
||||
_options->backgroundColor = _backgroundColor->color();
|
||||
_options->trackWidth = _trackWidth->value();
|
||||
_options->trackStyle = (Qt::PenStyle) _trackStyle->itemData(
|
||||
_options.palette.setColor(_baseColor->color());
|
||||
_options.palette.setShift(_colorOffset->value() / 100.0);
|
||||
_options.mapOpacity = _mapOpacity->value();
|
||||
_options.backgroundColor = _backgroundColor->color();
|
||||
_options.trackWidth = _trackWidth->value();
|
||||
_options.trackStyle = (Qt::PenStyle) _trackStyle->itemData(
|
||||
_trackStyle->currentIndex()).toInt();
|
||||
_options->routeWidth = _routeWidth->value();
|
||||
_options->routeStyle = (Qt::PenStyle) _routeStyle->itemData(
|
||||
_options.routeWidth = _routeWidth->value();
|
||||
_options.routeStyle = (Qt::PenStyle) _routeStyle->itemData(
|
||||
_routeStyle->currentIndex()).toInt();
|
||||
_options->pathAntiAliasing = _pathAA->isChecked();
|
||||
_options->areaWidth = _areaWidth->value();
|
||||
_options->areaStyle = (Qt::PenStyle) _areaStyle->itemData(
|
||||
_options.pathAntiAliasing = _pathAA->isChecked();
|
||||
_options.areaWidth = _areaWidth->value();
|
||||
_options.areaStyle = (Qt::PenStyle) _areaStyle->itemData(
|
||||
_areaStyle->currentIndex()).toInt();
|
||||
_options->areaOpacity = _areaOpacity->value();
|
||||
_options->waypointSize = _waypointSize->value();
|
||||
_options->waypointColor = _waypointColor->color();
|
||||
_options->poiSize = _poiSize->value();
|
||||
_options->poiColor = _poiColor->color();
|
||||
_options->graphWidth = _graphWidth->value();
|
||||
_options->sliderColor = _sliderColor->color();
|
||||
_options->graphAntiAliasing = _graphAA->isChecked();
|
||||
_options.areaOpacity = _areaOpacity->value();
|
||||
_options.waypointSize = _waypointSize->value();
|
||||
_options.waypointColor = _waypointColor->color();
|
||||
_options.poiSize = _poiSize->value();
|
||||
_options.poiColor = _poiColor->color();
|
||||
_options.graphWidth = _graphWidth->value();
|
||||
_options.sliderColor = _sliderColor->color();
|
||||
_options.graphAntiAliasing = _graphAA->isChecked();
|
||||
|
||||
_options->projection = _projection->itemData(_projection->currentIndex())
|
||||
_options.projection = _projection->itemData(_projection->currentIndex())
|
||||
.toInt();
|
||||
#ifdef ENABLE_HIDPI
|
||||
_options->hidpiMap = _hidpi->isChecked();
|
||||
_options.hidpiMap = _hidpi->isChecked();
|
||||
#endif // ENABLE_HIDPI
|
||||
|
||||
_options->elevationFilter = _elevationFilter->value();
|
||||
_options->speedFilter = _speedFilter->value();
|
||||
_options->heartRateFilter = _heartRateFilter->value();
|
||||
_options->cadenceFilter = _cadenceFilter->value();
|
||||
_options->powerFilter = _powerFilter->value();
|
||||
_options->outlierEliminate = _outlierEliminate->isChecked();
|
||||
_options->automaticPause = _automaticPause->isChecked();
|
||||
qreal pauseSpeed = (_options->units == Imperial)
|
||||
? _pauseSpeed->value() / MS2MIH : (_options->units == Nautical)
|
||||
_options.elevationFilter = _elevationFilter->value();
|
||||
_options.speedFilter = _speedFilter->value();
|
||||
_options.heartRateFilter = _heartRateFilter->value();
|
||||
_options.cadenceFilter = _cadenceFilter->value();
|
||||
_options.powerFilter = _powerFilter->value();
|
||||
_options.outlierEliminate = _outlierEliminate->isChecked();
|
||||
_options.automaticPause = _automaticPause->isChecked();
|
||||
qreal pauseSpeed = (_units == Imperial)
|
||||
? _pauseSpeed->value() / MS2MIH : (_units == Nautical)
|
||||
? _pauseSpeed->value() / MS2KN : _pauseSpeed->value() / MS2KMH;
|
||||
if (qAbs(pauseSpeed - _options->pauseSpeed) > 0.01)
|
||||
_options->pauseSpeed = pauseSpeed;
|
||||
_options->pauseInterval = _pauseInterval->value();
|
||||
_options->useReportedSpeed = _reportedSpeed->isChecked();
|
||||
_options->dataUseDEM = _dataDEMElevation->isChecked();
|
||||
_options->showSecondaryElevation = _showSecondaryElevation->isChecked();
|
||||
_options->showSecondarySpeed = _showSecondarySpeed->isChecked();
|
||||
if (qAbs(pauseSpeed - _options.pauseSpeed) > 0.01)
|
||||
_options.pauseSpeed = pauseSpeed;
|
||||
_options.pauseInterval = _pauseInterval->value();
|
||||
_options.useReportedSpeed = _reportedSpeed->isChecked();
|
||||
_options.dataUseDEM = _dataDEMElevation->isChecked();
|
||||
_options.showSecondaryElevation = _showSecondaryElevation->isChecked();
|
||||
_options.showSecondarySpeed = _showSecondarySpeed->isChecked();
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
_options->timeZone.setType(_utcZone->isChecked()
|
||||
_options.timeZone.setType(_utcZone->isChecked()
|
||||
? TimeZoneInfo::UTC : _systemZone->isChecked()
|
||||
? TimeZoneInfo::System : TimeZoneInfo::Custom);
|
||||
_options->timeZone.setCustomZone(QTimeZone(_timeZone->currentText()
|
||||
_options.timeZone.setCustomZone(QTimeZone(_timeZone->currentText()
|
||||
.toLatin1()));
|
||||
#endif // ENABLE_TIMEZONES
|
||||
_options.useSegments = _useSegments->isChecked();
|
||||
|
||||
qreal poiRadius = (_options->units == Imperial)
|
||||
? _poiRadius->value() * MIINM : (_options->units == Nautical)
|
||||
qreal poiRadius = (_units == Imperial)
|
||||
? _poiRadius->value() * MIINM : (_units == Nautical)
|
||||
? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM;
|
||||
if (qAbs(poiRadius - _options->poiRadius) > 0.01)
|
||||
_options->poiRadius = poiRadius;
|
||||
if (qAbs(poiRadius - _options.poiRadius) > 0.01)
|
||||
_options.poiRadius = poiRadius;
|
||||
|
||||
_options->useOpenGL = _useOpenGL->isChecked();
|
||||
_options.useOpenGL = _useOpenGL->isChecked();
|
||||
#ifdef ENABLE_HTTP2
|
||||
_options->enableHTTP2 = _enableHTTP2->isChecked();
|
||||
_options.enableHTTP2 = _enableHTTP2->isChecked();
|
||||
#endif // ENABLE_HTTP2
|
||||
_options->pixmapCache = _pixmapCache->value();
|
||||
_options->connectionTimeout = _connectionTimeout->value();
|
||||
_options.pixmapCache = _pixmapCache->value();
|
||||
_options.connectionTimeout = _connectionTimeout->value();
|
||||
|
||||
_options->hiresPrint = _hires->isChecked();
|
||||
_options->printName = _name->isChecked();
|
||||
_options->printDate = _date->isChecked();
|
||||
_options->printDistance = _distance->isChecked();
|
||||
_options->printTime = _time->isChecked();
|
||||
_options->printMovingTime = _movingTime->isChecked();
|
||||
_options->printItemCount = _itemCount->isChecked();
|
||||
_options->separateGraphPage = _separateGraphPage->isChecked();
|
||||
_options.hiresPrint = _hires->isChecked();
|
||||
_options.printName = _name->isChecked();
|
||||
_options.printDate = _date->isChecked();
|
||||
_options.printDistance = _distance->isChecked();
|
||||
_options.printTime = _time->isChecked();
|
||||
_options.printMovingTime = _movingTime->isChecked();
|
||||
_options.printItemCount = _itemCount->isChecked();
|
||||
_options.separateGraphPage = _separateGraphPage->isChecked();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ struct Options {
|
||||
#ifdef ENABLE_TIMEZONES
|
||||
TimeZoneInfo timeZone;
|
||||
#endif // ENABLE_TIMEZONES
|
||||
bool useSegments;
|
||||
// POI
|
||||
int poiRadius;
|
||||
// System
|
||||
@ -81,8 +82,6 @@ struct Options {
|
||||
bool printMovingTime;
|
||||
bool printItemCount;
|
||||
bool separateGraphPage;
|
||||
|
||||
Units units;
|
||||
};
|
||||
|
||||
class OptionsDialog : public QDialog
|
||||
@ -93,7 +92,7 @@ public slots:
|
||||
void accept();
|
||||
|
||||
public:
|
||||
OptionsDialog(Options *options, QWidget *parent = 0);
|
||||
OptionsDialog(Options &options, Units units, QWidget *parent = 0);
|
||||
|
||||
private slots:
|
||||
void automaticPauseDetectionSet(bool set);
|
||||
@ -106,8 +105,9 @@ private:
|
||||
QWidget *createSystemPage();
|
||||
QWidget *createExportPage();
|
||||
|
||||
Options *_options;
|
||||
Options &_options;
|
||||
|
||||
Units _units;
|
||||
// Appearance
|
||||
ColorBox *_baseColor;
|
||||
PercentSlider *_colorOffset;
|
||||
@ -157,6 +157,7 @@ private:
|
||||
QRadioButton *_customZone;
|
||||
QComboBox *_timeZone;
|
||||
#endif // ENABLE_TIMEZONES
|
||||
QCheckBox *_useSegments;
|
||||
// POI
|
||||
QDoubleSpinBox *_poiRadius;
|
||||
// System
|
||||
|
@ -1,30 +1,27 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QComboBox>
|
||||
#include <QRadioButton>
|
||||
#include <QPushButton>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QTabWidget>
|
||||
#include <QDoubleSpinBox>
|
||||
#include "marginswidget.h"
|
||||
#include "fileselectwidget.h"
|
||||
#include "units.h"
|
||||
#include "exportdialog.h"
|
||||
#include "pdfexportdialog.h"
|
||||
|
||||
|
||||
ExportDialog::ExportDialog(Export *exp, QWidget *parent)
|
||||
: QDialog(parent), _export(exp)
|
||||
PDFExportDialog::PDFExportDialog(PDFExport &exp, Units units, QWidget *parent)
|
||||
: QDialog(parent), _export(exp), _units(units)
|
||||
{
|
||||
int index;
|
||||
|
||||
_fileSelect = new FileSelectWidget();
|
||||
_fileSelect->setFilter(tr("PDF files") + " (*.pdf);;" + tr("All files")
|
||||
+ " (*)");
|
||||
_fileSelect->setFile(_export->fileName);
|
||||
_fileSelect->setFile(_export.fileName);
|
||||
|
||||
_paperSize = new QComboBox();
|
||||
_paperSize->addItem("A2", QPrinter::A2);
|
||||
@ -39,14 +36,14 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
|
||||
_paperSize->addItem("Tabloid", QPrinter::Tabloid);
|
||||
_paperSize->addItem("Legal", QPrinter::Legal);
|
||||
_paperSize->addItem("Letter", QPrinter::Letter);
|
||||
if ((index = _paperSize->findData(_export->paperSize)) >= 0)
|
||||
if ((index = _paperSize->findData(_export.paperSize)) >= 0)
|
||||
_paperSize->setCurrentIndex(index);
|
||||
|
||||
_resolution = new QComboBox();
|
||||
_resolution->addItem("150 DPI", 150);
|
||||
_resolution->addItem("300 DPI", 300);
|
||||
_resolution->addItem("600 DPI", 600);
|
||||
if ((index = _resolution->findData(_export->resolution)) >= 0)
|
||||
if ((index = _resolution->findData(_export.resolution)) >= 0)
|
||||
_resolution->setCurrentIndex(index);
|
||||
|
||||
_portrait = new QRadioButton(tr("Portrait"));
|
||||
@ -54,41 +51,16 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
|
||||
QHBoxLayout *orientationLayout = new QHBoxLayout();
|
||||
orientationLayout->addWidget(_portrait);
|
||||
orientationLayout->addWidget(_landscape);
|
||||
if (_export->orientation == QPrinter::Portrait)
|
||||
if (_export.orientation == QPrinter::Portrait)
|
||||
_portrait->setChecked(true);
|
||||
else
|
||||
_landscape->setChecked(true);
|
||||
|
||||
_topMargin = new QDoubleSpinBox();
|
||||
_bottomMargin = new QDoubleSpinBox();
|
||||
_leftMargin = new QDoubleSpinBox();
|
||||
_rightMargin = new QDoubleSpinBox();
|
||||
QString us = (_export->units == Metric) ? tr("mm") : tr("in");
|
||||
_topMargin->setSuffix(UNIT_SPACE + us);
|
||||
_bottomMargin->setSuffix(UNIT_SPACE + us);
|
||||
_leftMargin->setSuffix(UNIT_SPACE + us);
|
||||
_rightMargin->setSuffix(UNIT_SPACE + us);
|
||||
if (_export->units == Metric) {
|
||||
_topMargin->setValue(_export->margins.top());
|
||||
_bottomMargin->setValue(_export->margins.bottom());
|
||||
_leftMargin->setValue(_export->margins.left());
|
||||
_rightMargin->setValue(_export->margins.right());
|
||||
} else {
|
||||
_topMargin->setValue(_export->margins.top() * MM2IN);
|
||||
_bottomMargin->setValue(_export->margins.bottom() * MM2IN);
|
||||
_leftMargin->setValue(_export->margins.left() * MM2IN);
|
||||
_rightMargin->setValue(_export->margins.right() * MM2IN);
|
||||
_topMargin->setSingleStep(0.1);
|
||||
_bottomMargin->setSingleStep(0.1);
|
||||
_leftMargin->setSingleStep(0.1);
|
||||
_rightMargin->setSingleStep(0.1);
|
||||
}
|
||||
|
||||
QGridLayout *marginsLayout = new QGridLayout();
|
||||
marginsLayout->addWidget(_topMargin, 0, 0, 1, 2, Qt::AlignCenter);
|
||||
marginsLayout->addWidget(_leftMargin, 1, 0, 1, 1, Qt::AlignRight);
|
||||
marginsLayout->addWidget(_rightMargin, 1, 1, 1, 1, Qt::AlignLeft);
|
||||
marginsLayout->addWidget(_bottomMargin, 2, 0, 1, 2, Qt::AlignCenter);
|
||||
_margins = new MarginsFWidget();
|
||||
_margins->setUnits((units == Metric) ? tr("cm") : tr("in"));
|
||||
_margins->setSingleStep(0.1);
|
||||
_margins->setValue((units == Metric)
|
||||
? _export.margins * MM2CM : _export.margins * MM2IN);
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QGroupBox *pageSetupBox = new QGroupBox(tr("Page Setup"));
|
||||
@ -97,7 +69,7 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
|
||||
pageSetupLayout->addRow(tr("Page size:"), _paperSize);
|
||||
pageSetupLayout->addRow(tr("Resolution:"), _resolution);
|
||||
pageSetupLayout->addRow(tr("Orientation:"), orientationLayout);
|
||||
pageSetupLayout->addRow(tr("Margins:"), marginsLayout);
|
||||
pageSetupLayout->addRow(tr("Margins:"), _margins);
|
||||
#ifdef Q_OS_MAC
|
||||
QFrame *line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
@ -111,7 +83,7 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QGroupBox *outputFileBox = new QGroupBox(tr("Output file"));
|
||||
QHBoxLayout *outputFileLayout = new QHBoxLayout();
|
||||
QVBoxLayout *outputFileLayout = new QVBoxLayout();
|
||||
outputFileLayout->addWidget(_fileSelect);
|
||||
outputFileBox->setLayout(outputFileLayout);
|
||||
#endif // Q_OS_MAC
|
||||
@ -136,41 +108,13 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
bool ExportDialog::checkFile()
|
||||
void PDFExportDialog::accept()
|
||||
{
|
||||
if (_fileSelect->file().isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("No output file selected."));
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(_fileSelect->file());
|
||||
QFileInfo fi(file);
|
||||
bool exists = fi.exists();
|
||||
bool opened = false;
|
||||
|
||||
if (exists && fi.isDir()) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("%1 is a directory.")
|
||||
.arg(file.fileName()));
|
||||
return false;
|
||||
} else if ((exists && !fi.isWritable())
|
||||
|| !(opened = file.open(QFile::Append))) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("%1 is not writable.")
|
||||
.arg(file.fileName()));
|
||||
return false;
|
||||
}
|
||||
if (opened) {
|
||||
file.close();
|
||||
if (!exists)
|
||||
file.remove();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExportDialog::accept()
|
||||
{
|
||||
if (!checkFile())
|
||||
QString error;
|
||||
if (!_fileSelect->checkFile(error)) {
|
||||
QMessageBox::warning(this, tr("Error"), error);
|
||||
return;
|
||||
}
|
||||
|
||||
QPrinter::Orientation orientation = _portrait->isChecked()
|
||||
? QPrinter::Portrait : QPrinter::Landscape;
|
||||
@ -178,17 +122,12 @@ void ExportDialog::accept()
|
||||
(_paperSize->itemData(_paperSize->currentIndex()).toInt());
|
||||
int resolution = _resolution->itemData(_resolution->currentIndex()).toInt();
|
||||
|
||||
_export->fileName = _fileSelect->file();
|
||||
_export->paperSize = paperSize;
|
||||
_export->resolution = resolution;
|
||||
_export->orientation = orientation;
|
||||
if (_export->units == Imperial)
|
||||
_export->margins = MarginsF(_leftMargin->value() / MM2IN,
|
||||
_topMargin->value() / MM2IN, _rightMargin->value() / MM2IN,
|
||||
_bottomMargin->value() / MM2IN);
|
||||
else
|
||||
_export->margins = MarginsF(_leftMargin->value(), _topMargin->value(),
|
||||
_rightMargin->value(), _bottomMargin->value());
|
||||
_export.fileName = _fileSelect->file();
|
||||
_export.paperSize = paperSize;
|
||||
_export.resolution = resolution;
|
||||
_export.orientation = orientation;
|
||||
_export.margins = (_units == Imperial)
|
||||
? _margins->value() / MM2IN : _margins->value() / MM2CM;
|
||||
|
||||
QDialog::accept();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#ifndef EXPORTDIALOG_H
|
||||
#define EXPORTDIALOG_H
|
||||
#ifndef PDFEXPORTDIALOG_H
|
||||
#define PDFEXPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPrinter>
|
||||
@ -9,42 +9,37 @@
|
||||
class QComboBox;
|
||||
class QRadioButton;
|
||||
class FileSelectWidget;
|
||||
class QDoubleSpinBox;
|
||||
class MarginsFWidget;
|
||||
|
||||
struct Export {
|
||||
struct PDFExport
|
||||
{
|
||||
QString fileName;
|
||||
QPrinter::PaperSize paperSize;
|
||||
QPrinter::Orientation orientation;
|
||||
MarginsF margins;
|
||||
int resolution;
|
||||
|
||||
Units units;
|
||||
};
|
||||
|
||||
class ExportDialog : public QDialog
|
||||
class PDFExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ExportDialog(Export *exp, QWidget *parent = 0);
|
||||
PDFExportDialog(PDFExport &exp, Units units, QWidget *parent = 0);
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
bool checkFile();
|
||||
|
||||
Export *_export;
|
||||
PDFExport &_export;
|
||||
|
||||
Units _units;
|
||||
FileSelectWidget *_fileSelect;
|
||||
QComboBox *_paperSize;
|
||||
QComboBox *_resolution;
|
||||
QRadioButton *_portrait;
|
||||
QRadioButton *_landscape;
|
||||
QDoubleSpinBox *_topMargin;
|
||||
QDoubleSpinBox *_bottomMargin;
|
||||
QDoubleSpinBox *_leftMargin;
|
||||
QDoubleSpinBox *_rightMargin;
|
||||
MarginsFWidget *_margins;
|
||||
};
|
||||
|
||||
#endif // EXPORTDIALOG_H
|
||||
#endif // PDFEXPORTDIALOG_H
|
101
src/GUI/pngexportdialog.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QSpinBox>
|
||||
#include <QMessageBox>
|
||||
#include <QTabWidget>
|
||||
#include <QCheckBox>
|
||||
#include "units.h"
|
||||
#include "fileselectwidget.h"
|
||||
#include "marginswidget.h"
|
||||
#include "pngexportdialog.h"
|
||||
|
||||
|
||||
PNGExportDialog::PNGExportDialog(PNGExport &exp, QWidget *parent)
|
||||
: QDialog(parent), _export(exp)
|
||||
{
|
||||
_fileSelect = new FileSelectWidget();
|
||||
_fileSelect->setFilter(tr("PNG files") + " (*.png);;" + tr("All files")
|
||||
+ " (*)");
|
||||
_fileSelect->setFile(_export.fileName);
|
||||
|
||||
_width = new QSpinBox();
|
||||
_width->setMinimum(256);
|
||||
_width->setMaximum(4096);
|
||||
_width->setValue(_export.size.width());
|
||||
_width->setSuffix(UNIT_SPACE + tr("px"));
|
||||
_height = new QSpinBox();
|
||||
_height->setMinimum(256);
|
||||
_height->setMaximum(4096);
|
||||
_height->setValue(_export.size.height());
|
||||
_height->setSuffix(UNIT_SPACE + tr("px"));
|
||||
|
||||
_margins = new MarginsWidget();
|
||||
_margins->setValue(_export.margins);
|
||||
_margins->setUnits(tr("px"));
|
||||
|
||||
_antialiasing = new QCheckBox(tr("Use anti-aliasing"));
|
||||
_antialiasing->setChecked(_export.antialiasing);
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QGroupBox *pageSetupBox = new QGroupBox(tr("Image Setup"));
|
||||
#endif // Q_OS_MAC
|
||||
QFormLayout *pageSetupLayout = new QFormLayout;
|
||||
pageSetupLayout->addRow(tr("Image width:"), _width);
|
||||
pageSetupLayout->addRow(tr("Image height:"), _height);
|
||||
pageSetupLayout->addRow(tr("Margins:"), _margins);
|
||||
pageSetupLayout->addWidget(_antialiasing);
|
||||
#ifdef Q_OS_MAC
|
||||
QFrame *line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
pageSetupLayout->addRow(line);
|
||||
pageSetupLayout->addRow(tr("File:"), _fileSelect);
|
||||
pageSetupLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
|
||||
#else // Q_OS_MAC
|
||||
pageSetupBox->setLayout(pageSetupLayout);
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QGroupBox *outputFileBox = new QGroupBox(tr("Output file"));
|
||||
QVBoxLayout *outputFileLayout = new QVBoxLayout();
|
||||
outputFileLayout->addWidget(_fileSelect);
|
||||
outputFileBox->setLayout(outputFileLayout);
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox();
|
||||
buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
|
||||
buttonBox->addButton(QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
#ifdef Q_OS_MAC
|
||||
layout->addLayout(pageSetupLayout);
|
||||
#else // Q_OS_MAC
|
||||
layout->addWidget(pageSetupBox);
|
||||
layout->addWidget(outputFileBox);
|
||||
#endif // Q_OS_MAC
|
||||
layout->addWidget(buttonBox);
|
||||
setLayout(layout);
|
||||
|
||||
setWindowTitle(tr("Export to PNG"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
void PNGExportDialog::accept()
|
||||
{
|
||||
QString error;
|
||||
if (!_fileSelect->checkFile(error)) {
|
||||
QMessageBox::warning(this, tr("Error"), error);
|
||||
return;
|
||||
}
|
||||
|
||||
_export.fileName = _fileSelect->file();
|
||||
_export.size = QSize(_width->value(), _height->value());
|
||||
_export.margins = _margins->value();
|
||||
_export.antialiasing = _antialiasing->isChecked();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
41
src/GUI/pngexportdialog.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef PNGEXPORTDIALOG_H
|
||||
#define PNGEXPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMargins>
|
||||
#include "margins.h"
|
||||
|
||||
class FileSelectWidget;
|
||||
class MarginsWidget;
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
|
||||
struct PNGExport
|
||||
{
|
||||
QString fileName;
|
||||
QSize size;
|
||||
QMargins margins;
|
||||
bool antialiasing;
|
||||
};
|
||||
|
||||
class PNGExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PNGExportDialog(PNGExport &exp, QWidget *parent = 0);
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
PNGExport &_export;
|
||||
|
||||
FileSelectWidget *_fileSelect;
|
||||
QSpinBox *_width;
|
||||
QSpinBox *_height;
|
||||
MarginsWidget *_margins;
|
||||
QCheckBox *_antialiasing;
|
||||
};
|
||||
|
||||
#endif // PNGEXPORTDIALOG_H
|
@ -66,26 +66,45 @@
|
||||
#define SHOW_WAYPOINT_LABELS_SETTING "waypointLabels"
|
||||
#define SHOW_WAYPOINT_LABELS_DEFAULT true
|
||||
|
||||
#define EXPORT_SETTINGS_GROUP "Export"
|
||||
#define PDF_EXPORT_SETTINGS_GROUP "Export"
|
||||
#define PAPER_ORIENTATION_SETTING "orientation"
|
||||
#define PAPER_ORIENTATION_DEFAULT QPrinter::Portrait
|
||||
#define PAPER_SIZE_SETTING "size"
|
||||
#define PAPER_SIZE_DEFAULT (IMPERIAL_UNITS() ? QPrinter::Letter \
|
||||
: QPrinter::A4)
|
||||
#define MARGIN_LEFT_SETTING "marginLeft"
|
||||
#define MARGIN_LEFT_DEFAULT 5 /* mm */
|
||||
#define MARGIN_TOP_SETTING "marginTop"
|
||||
#define MARGIN_TOP_DEFAULT 5 /* mm */
|
||||
#define MARGIN_RIGHT_SETTING "marginRight"
|
||||
#define MARGIN_RIGHT_DEFAULT 5 /* mm */
|
||||
#define MARGIN_BOTTOM_SETTING "marginBottom"
|
||||
#define MARGIN_BOTTOM_DEFAULT 5 /* mm */
|
||||
#define EXPORT_FILENAME_SETTING "fileName"
|
||||
#define EXPORT_FILENAME_DEFAULT QString("%1/export.pdf"). \
|
||||
#define PDF_MARGIN_LEFT_SETTING "marginLeft"
|
||||
#define PDF_MARGIN_LEFT_DEFAULT 5 /* mm */
|
||||
#define PDF_MARGIN_TOP_SETTING "marginTop"
|
||||
#define PDF_MARGIN_TOP_DEFAULT 5 /* mm */
|
||||
#define PDF_MARGIN_RIGHT_SETTING "marginRight"
|
||||
#define PDF_MARGIN_RIGHT_DEFAULT 5 /* mm */
|
||||
#define PDF_MARGIN_BOTTOM_SETTING "marginBottom"
|
||||
#define PDF_MARGIN_BOTTOM_DEFAULT 5 /* mm */
|
||||
#define PDF_FILENAME_SETTING "fileName"
|
||||
#define PDF_FILENAME_DEFAULT QString("%1/export.pdf"). \
|
||||
arg(QDir::currentPath())
|
||||
#define RESOLUTION_SETTING "resolution"
|
||||
#define RESOLUTION_DEFAULT 600
|
||||
|
||||
#define PNG_EXPORT_SETTINGS_GROUP "PNGExport"
|
||||
#define PNG_WIDTH_SETTING "width"
|
||||
#define PNG_WIDTH_DEFAULT 600
|
||||
#define PNG_HEIGHT_SETTING "height"
|
||||
#define PNG_HEIGHT_DEFAULT 800
|
||||
#define PNG_MARGIN_LEFT_SETTING "marginLeft"
|
||||
#define PNG_MARGIN_LEFT_DEFAULT 5 /* px */
|
||||
#define PNG_MARGIN_TOP_SETTING "marginTop"
|
||||
#define PNG_MARGIN_TOP_DEFAULT 5 /* px */
|
||||
#define PNG_MARGIN_RIGHT_SETTING "marginRight"
|
||||
#define PNG_MARGIN_RIGHT_DEFAULT 5 /* px */
|
||||
#define PNG_MARGIN_BOTTOM_SETTING "marginBottom"
|
||||
#define PNG_MARGIN_BOTTOM_DEFAULT 5 /* px */
|
||||
#define PNG_ANTIALIASING_SETTING "antialiasing"
|
||||
#define PNG_ANTIALIASING_DEFAULT true
|
||||
#define PNG_FILENAME_SETTING "fileName"
|
||||
#define PNG_FILENAME_DEFAULT QString("%1/export.png"). \
|
||||
arg(QDir::currentPath())
|
||||
|
||||
#define OPTIONS_SETTINGS_GROUP "Options"
|
||||
#define PALETTE_COLOR_SETTING "paletteColor"
|
||||
#define PALETTE_COLOR_DEFAULT QColor(Qt::blue)
|
||||
@ -150,6 +169,8 @@
|
||||
#define SHOW_SECONDARY_SPEED_SETTING "showSecondarySpeed"
|
||||
#define SHOW_SECONDARY_SPEED_DEFAULT false
|
||||
#define TIME_ZONE_SETTING "timeZone"
|
||||
#define USE_SEGMENTS_SETTING "useSegments"
|
||||
#define USE_SEGMENTS_DEFAULT true
|
||||
#define POI_RADIUS_SETTING "poiRadius"
|
||||
#define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM)
|
||||
#define USE_OPENGL_SETTING "useOpenGL"
|
||||
|
@ -16,6 +16,7 @@ enum Units {
|
||||
#define MS2KN 1.943844490000 // m/s -> kn
|
||||
#define FT2MI 0.000189393939 // ft -> mi
|
||||
#define MM2IN 0.039370100000 // mm -> in
|
||||
#define MM2CM 0.100000000000 // mm -> cm
|
||||
#define H2S 0.000277777778 // h -> s
|
||||
#define MIN2S 0.016666666667 // min -> s
|
||||
|
||||
|
@ -18,8 +18,8 @@
|
||||
#define ENABLE_GEOJSON
|
||||
#endif // QT >= 5.0
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
|
||||
#define ENABLE_TIMEZONES
|
||||
#endif // QT >= 5.2
|
||||
#endif // QT >= 5.5
|
||||
|
||||
#endif /* CONFIG_H */
|
||||
|
@ -66,13 +66,25 @@ void TCXParser::heartRateBpm(Trackpoint &trackpoint)
|
||||
}
|
||||
}
|
||||
|
||||
void TCXParser::extensions(Trackpoint &trackpoint)
|
||||
void TCXParser::TPX(Trackpoint &trackpoint)
|
||||
{
|
||||
while (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == QLatin1String("RunCadence"))
|
||||
trackpoint.setCadence(number());
|
||||
else if (_reader.name() == QLatin1String("Watts"))
|
||||
trackpoint.setPower(number());
|
||||
else if (_reader.name() == QLatin1String("Speed"))
|
||||
trackpoint.setSpeed(number());
|
||||
else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void TCXParser::extensions(Trackpoint &trackpoint)
|
||||
{
|
||||
while (_reader.readNextStartElement()) {
|
||||
if (_reader.name() == QLatin1String("TPX"))
|
||||
TPX(trackpoint);
|
||||
else
|
||||
_reader.skipCurrentElement();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ private:
|
||||
void trackpointData(Trackpoint &trackpoint);
|
||||
void waypointData(Waypoint &waypoint);
|
||||
void extensions(Trackpoint &trackpoint);
|
||||
void TPX(Trackpoint &trackpoint);
|
||||
void heartRateBpm(Trackpoint &trackpoint);
|
||||
Coordinates position();
|
||||
qreal number();
|
||||
|
@ -17,7 +17,7 @@ bool Track::_useReportedSpeed = false;
|
||||
bool Track::_useDEM = false;
|
||||
bool Track::_show2ndElevation = false;
|
||||
bool Track::_show2ndSpeed = false;
|
||||
|
||||
bool Track::_useSegments = true;
|
||||
|
||||
static qreal avg(const QVector<qreal> &v)
|
||||
{
|
||||
@ -88,10 +88,20 @@ static GraphSegment filter(const GraphSegment &g, int window)
|
||||
}
|
||||
|
||||
|
||||
Track::Track(const TrackData &data) : _data(data), _pause(0)
|
||||
Track::Track(const TrackData &data) : _pause(0)
|
||||
{
|
||||
qreal ds, dt;
|
||||
|
||||
if (_useSegments)
|
||||
_data = data;
|
||||
else {
|
||||
if (!data.isEmpty()) {
|
||||
_data.append(data.first());
|
||||
for (int i = 1; i < data.size(); i++)
|
||||
_data.first() << data.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
const SegmentData &sd = _data.at(i);
|
||||
_segments.append(Segment());
|
||||
|
@ -49,10 +49,9 @@ public:
|
||||
{_outlierEliminate = eliminate;}
|
||||
static void useReportedSpeed(bool use) {_useReportedSpeed = use;}
|
||||
static void useDEM(bool use) {_useDEM = use;}
|
||||
static void showSecondaryElevation(bool show)
|
||||
{_show2ndElevation = show;}
|
||||
static void showSecondarySpeed(bool show)
|
||||
{_show2ndSpeed = show;}
|
||||
static void showSecondaryElevation(bool show) {_show2ndElevation = show;}
|
||||
static void showSecondarySpeed(bool show) {_show2ndSpeed = show;}
|
||||
static void useSegments(bool use) {_useSegments = use;}
|
||||
|
||||
private:
|
||||
struct Segment {
|
||||
@ -87,6 +86,7 @@ private:
|
||||
static bool _useDEM;
|
||||
static bool _show2ndElevation;
|
||||
static bool _show2ndSpeed;
|
||||
static bool _useSegments;
|
||||
};
|
||||
|
||||
#endif // TRACK_H
|
||||
|
@ -1,38 +1,9 @@
|
||||
#include "bitstream.h"
|
||||
|
||||
|
||||
bool BitStream1::read(int bits, quint32 &val)
|
||||
{
|
||||
val = 0;
|
||||
|
||||
for (int pos = 0; pos < bits; ) {
|
||||
if (!_remaining) {
|
||||
if (!_length || !_file.readUInt8(_hdl, _data))
|
||||
return false;
|
||||
_remaining = 8;
|
||||
_length--;
|
||||
}
|
||||
|
||||
quint32 get = bits - pos;
|
||||
if (get >= _remaining) {
|
||||
val |= _data << pos;
|
||||
pos += _remaining;
|
||||
_remaining = 0;
|
||||
} else {
|
||||
quint32 mask = (1<<get) - 1;
|
||||
val |= (_data & mask)<<pos;
|
||||
_data >>= get;
|
||||
_remaining -= get;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream1::flush()
|
||||
{
|
||||
if (_length && !_file.seek(_hdl, _hdl.pos() + _length))
|
||||
if (_length && !_file.seek(_hdl, _file.pos(_hdl) + _length))
|
||||
return false;
|
||||
|
||||
_length = 0;
|
||||
@ -41,8 +12,19 @@ bool BitStream1::flush()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream4F::flush()
|
||||
{
|
||||
if (_length && !_file.seek(_hdl, _file.pos(_hdl) + _length))
|
||||
return false;
|
||||
|
||||
bool BitStream4::read(int bits, quint32 &val)
|
||||
_length = 0;
|
||||
_used = 32;
|
||||
_unused = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream4F::read(int bits, quint32 &val)
|
||||
{
|
||||
if (bits <= 32 - (int)(_used + _unused)) {
|
||||
val = bits ? (_data << _used) >> (32 - bits) : 0;
|
||||
@ -50,6 +32,8 @@ bool BitStream4::read(int bits, quint32 &val)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_unused)
|
||||
return false;
|
||||
quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0;
|
||||
quint32 bytes = qMin(_length, 4U);
|
||||
|
||||
@ -66,14 +50,149 @@ bool BitStream4::read(int bits, quint32 &val)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream4::flush()
|
||||
BitStream4R::BitStream4R(const SubFile &file, SubFile::Handle &hdl,
|
||||
quint32 length) : BitStream4(file, hdl, length)
|
||||
{
|
||||
if (_length && !_file.seek(_hdl, _hdl.pos() + _length))
|
||||
return false;
|
||||
_file.seek(_hdl, _file.pos(_hdl) - 4);
|
||||
}
|
||||
|
||||
_length = 0;
|
||||
_used = 32;
|
||||
_unused = 0;
|
||||
bool BitStream4R::readBytes(int bytes, quint32 &val)
|
||||
{
|
||||
quint32 bits = _used % 8;
|
||||
quint32 b;
|
||||
|
||||
if (bits) {
|
||||
if (!read(8 - bits, b))
|
||||
return false;
|
||||
Q_ASSERT(!b);
|
||||
}
|
||||
|
||||
val = 0;
|
||||
for (int i = 0; i < bytes; i++) {
|
||||
if (!read(8, b))
|
||||
return false;
|
||||
val |= (b << (i * 8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream4R::readVUInt32(quint32 &val)
|
||||
{
|
||||
quint32 b;
|
||||
quint8 bytes, shift;
|
||||
|
||||
if (!readBytes(1, b))
|
||||
return false;
|
||||
|
||||
if ((b & 1) == 0) {
|
||||
if ((b & 2) == 0) {
|
||||
bytes = ((b >> 2) & 1) ^ 3;
|
||||
shift = 5;
|
||||
} else {
|
||||
shift = 6;
|
||||
bytes = 1;
|
||||
}
|
||||
} else {
|
||||
shift = 7;
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
val = b >> (8 - shift);
|
||||
|
||||
if (bytes) {
|
||||
if (!readBytes(bytes, b))
|
||||
return false;
|
||||
val = val | (b << shift);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream4R::readVuint32SM(quint32 &val1, quint32 &val2, quint32 &val2Bits)
|
||||
{
|
||||
quint32 b, eb;
|
||||
|
||||
if (!readBytes(1, b))
|
||||
return false;
|
||||
|
||||
if (!(b & 1)) {
|
||||
val1 = b >> 3;
|
||||
val2 = b >> 1 & 3;
|
||||
val2Bits = 2;
|
||||
} else {
|
||||
eb = b & 2;
|
||||
val2 = b >> 2 & 0x3f;
|
||||
val2Bits = eb * 2 + 6;
|
||||
if (!readBytes((eb >> 1 | 2) - 1, b))
|
||||
return false;
|
||||
if (eb) {
|
||||
val2 = val2 | (b & 0xf) << 6;
|
||||
b = b >> 4 & 0xfff;
|
||||
}
|
||||
val1 = b;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStream4R::skip(quint32 bytes)
|
||||
{
|
||||
if (bytes * 8 > bitsAvailable())
|
||||
return false;
|
||||
|
||||
quint32 ab = (32 - (_used + _unused))/8;
|
||||
if (bytes <= ab)
|
||||
_used += bytes * 8;
|
||||
else {
|
||||
quint32 seek = ((bytes - ab)/4)*4;
|
||||
quint32 read = (bytes - ab)%4;
|
||||
if (seek && !_file.seek(_hdl, _file.pos(_hdl) - seek))
|
||||
return false;
|
||||
_length -= seek;
|
||||
if (read) {
|
||||
quint32 rb = qMin(_length, 4U);
|
||||
if (!_file.readUInt32(_hdl, _data))
|
||||
return false;
|
||||
if (!_file.seek(_hdl, _file.pos(_hdl) - 8))
|
||||
return false;
|
||||
_length -= rb;
|
||||
_unused = (4 - rb) * 8;
|
||||
_used = read * 8;
|
||||
} else
|
||||
_used = 32;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitStream4R::resize(quint32 bytes)
|
||||
{
|
||||
quint32 ab = (32 - (_used + _unused) + 7)/8;
|
||||
|
||||
if (ab <= bytes)
|
||||
_length = bytes - ab;
|
||||
else {
|
||||
_length = 0;
|
||||
_unused += (ab - bytes) * 8;
|
||||
}
|
||||
}
|
||||
|
||||
void BitStream4R::save(State &state)
|
||||
{
|
||||
state.pos = _file.pos(_hdl);
|
||||
state.length = _length;
|
||||
state.used = _used;
|
||||
state.unused = _unused;
|
||||
state.data = _data;
|
||||
}
|
||||
|
||||
bool BitStream4R::restore(const State &state)
|
||||
{
|
||||
_length = state.length;
|
||||
_used = state.used;
|
||||
_unused = state.unused;
|
||||
_data = state.data;
|
||||
|
||||
return _file.seek(_hdl, state.pos);
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ public:
|
||||
BitStream1(const SubFile &file, SubFile::Handle &hdl, quint32 length)
|
||||
: _file(file), _hdl(hdl), _length(length), _remaining(0) {}
|
||||
|
||||
bool read(int bits, quint32 &val);
|
||||
template<typename T> bool read(int bits, T &val);
|
||||
bool flush();
|
||||
quint32 bitsAvailable() const {return _length * 8 + _remaining;}
|
||||
quint64 bitsAvailable() const {return (quint64)_length * 8 + _remaining;}
|
||||
|
||||
private:
|
||||
const SubFile &_file;
|
||||
@ -25,15 +25,105 @@ public:
|
||||
: _file(file), _hdl(hdl), _length(length), _used(32), _unused(0),
|
||||
_data(0) {}
|
||||
|
||||
bool read(int bits, quint32 &val);
|
||||
bool flush();
|
||||
quint32 bitsAvailable() const {return _length * 8 + (32 - _used) - _unused;}
|
||||
quint64 bitsAvailable() const
|
||||
{return (quint64)_length * 8 + (32 - _used) - _unused;}
|
||||
|
||||
private:
|
||||
protected:
|
||||
const SubFile &_file;
|
||||
SubFile::Handle &_hdl;
|
||||
quint32 _length, _used, _unused;
|
||||
quint32 _data;
|
||||
};
|
||||
|
||||
class BitStream4F : public BitStream4 {
|
||||
public:
|
||||
BitStream4F(const SubFile &file, SubFile::Handle &hdl, quint32 length)
|
||||
: BitStream4(file, hdl, length) {}
|
||||
|
||||
bool read(int bits, quint32 &val);
|
||||
bool flush();
|
||||
};
|
||||
|
||||
class BitStream4R : public BitStream4 {
|
||||
public:
|
||||
struct State {
|
||||
quint32 pos;
|
||||
quint32 length;
|
||||
quint32 used;
|
||||
quint32 unused;
|
||||
quint32 data;
|
||||
};
|
||||
|
||||
BitStream4R(const SubFile &file, SubFile::Handle &hdl, quint32 length);
|
||||
|
||||
template<typename T> bool read(int bits, T &val);
|
||||
bool readBytes(int bytes, quint32 &val);
|
||||
bool readVUInt32(quint32 &val);
|
||||
bool readVuint32SM(quint32 &val1, quint32 &val2, quint32 &val2Bits);
|
||||
|
||||
bool skip(quint32 bytes);
|
||||
void resize(quint32 bytes);
|
||||
void save(State &state);
|
||||
bool restore(const State &state);
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
bool BitStream1::read(int bits, T &val)
|
||||
{
|
||||
val = 0;
|
||||
|
||||
for (int pos = 0; pos < bits; ) {
|
||||
if (!_remaining) {
|
||||
if (!_length || !_file.readUInt8(_hdl, _data))
|
||||
return false;
|
||||
_remaining = 8;
|
||||
_length--;
|
||||
}
|
||||
|
||||
quint32 get = bits - pos;
|
||||
if (get >= _remaining) {
|
||||
val |= _data << pos;
|
||||
pos += _remaining;
|
||||
_remaining = 0;
|
||||
} else {
|
||||
quint32 mask = (1<<get) - 1;
|
||||
val |= (_data & mask)<<pos;
|
||||
_data >>= get;
|
||||
_remaining -= get;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool BitStream4R::read(int bits, T &val)
|
||||
{
|
||||
if (bits <= 32 - (int)(_used + _unused)) {
|
||||
val = bits ? (_data << _used) >> (32 - bits) : 0;
|
||||
_used += bits;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_unused)
|
||||
return false;
|
||||
quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0;
|
||||
quint32 bytes = qMin(_length, 4U);
|
||||
|
||||
if (!_file.readUInt32(_hdl, _data))
|
||||
return false;
|
||||
if (!_file.seek(_hdl, _file.pos(_hdl) - 8))
|
||||
return false;
|
||||
|
||||
_length -= bytes;
|
||||
_used -= 32 - bits;
|
||||
_unused = (4 - bytes) * 8;
|
||||
|
||||
val = _data >> (32 - _used) | old;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // BITSTREAM_H
|
||||
|
@ -1,74 +1,41 @@
|
||||
#include "huffmanstream.h"
|
||||
|
||||
|
||||
HuffmanStream::HuffmanStream(const SubFile &file, SubFile::Handle &hdl,
|
||||
quint32 length, const HuffmanTable &table, bool line)
|
||||
: BitStream4(file, hdl, length), _table(table), _symbolDataSize(0),
|
||||
_symbolData(0)
|
||||
bool HuffmanStreamF::init(bool line)
|
||||
{
|
||||
if (line) {
|
||||
if (!(sign(_lonSign) && sign(_latSign)))
|
||||
return;
|
||||
return false;
|
||||
} else {
|
||||
_lonSign = 0;
|
||||
_latSign = 0;
|
||||
}
|
||||
|
||||
quint32 eb;
|
||||
if (!read(1, eb))
|
||||
return;
|
||||
if (eb) {
|
||||
qWarning("Extended polygon/lines not supported");
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
bool HuffmanStream::sign(int &val)
|
||||
{
|
||||
quint32 bit;
|
||||
val = 0;
|
||||
|
||||
if (!read(1, bit))
|
||||
if (!_bs.read(1, eb))
|
||||
return false;
|
||||
|
||||
Q_ASSERT(!eb);
|
||||
if (eb)
|
||||
return false;
|
||||
if (bit) {
|
||||
if (!read(1, bit))
|
||||
return false;
|
||||
val = bit ? -1 : 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HuffmanStream::readDelta(int sign, qint32 &symbol)
|
||||
bool HuffmanStreamR::init()
|
||||
{
|
||||
quint8 size;
|
||||
quint32 next;
|
||||
quint8 nextSize = qMin((quint32)(32 - _symbolDataSize), bitsAvailable());
|
||||
|
||||
if (!read(nextSize, next))
|
||||
if (!(sign(_lonSign) && sign(_latSign)))
|
||||
return false;
|
||||
|
||||
_symbolData = (_symbolData << nextSize) | next;
|
||||
_symbolDataSize += nextSize;
|
||||
|
||||
symbol = _table.symbol(_symbolData << (32 - _symbolDataSize), size);
|
||||
|
||||
if (size <= _symbolDataSize)
|
||||
_symbolDataSize -= size;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (symbol && !sign) {
|
||||
if (!_symbolDataSize)
|
||||
return false;
|
||||
else {
|
||||
sign = ((1U << (_symbolDataSize - 1)) & _symbolData) ? -1 : 1;
|
||||
_symbolDataSize--;
|
||||
}
|
||||
}
|
||||
symbol = sign * symbol;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HuffmanStreamR::init(int lonSign, int latSign, quint32 data,
|
||||
quint32 dataSize)
|
||||
{
|
||||
_lonSign = lonSign;
|
||||
_latSign = latSign;
|
||||
_symbolData = data;
|
||||
_symbolDataSize = dataSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4,11 +4,15 @@
|
||||
#include "bitstream.h"
|
||||
#include "huffmantable.h"
|
||||
|
||||
class HuffmanStream : public BitStream4 {
|
||||
template <class BitStream>
|
||||
class HuffmanStream {
|
||||
public:
|
||||
HuffmanStream(const SubFile &file, SubFile::Handle &hdl, quint32 length,
|
||||
const HuffmanTable &table, bool line);
|
||||
HuffmanStream(BitStream &bitstream, const HuffmanTable &table)
|
||||
: _bs(bitstream), _table(table), _symbolDataSize(0), _symbolData(0),
|
||||
_lonSign(0), _latSign(0) {}
|
||||
|
||||
bool read(int bits, quint32 &val);
|
||||
bool readSymbol(quint32 &symbol);
|
||||
bool readNext(qint32 &lonDelta, qint32 &latDelta)
|
||||
{
|
||||
if (!(readDelta(_lonSign, lonDelta) && readDelta(_latSign, latDelta)))
|
||||
@ -17,19 +21,121 @@ public:
|
||||
return (lonDelta || latDelta);
|
||||
}
|
||||
|
||||
bool readOffset(qint32 &lonDelta, qint32 &latDelta)
|
||||
{return (readDelta(1, lonDelta) && readDelta(1, latDelta));}
|
||||
bool atEnd() const
|
||||
{return _symbolDataSize + bitsAvailable() < _table.maxSymbolSize();}
|
||||
{return _symbolDataSize + _bs.bitsAvailable() < _table.maxSymbolSize();}
|
||||
bool flush() {return _bs.flush();}
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool sign(int &val);
|
||||
bool readDelta(int sign, qint32 &delta);
|
||||
|
||||
BitStream &_bs;
|
||||
const HuffmanTable &_table;
|
||||
quint32 _symbolDataSize;
|
||||
quint32 _symbolData;
|
||||
int _lonSign, _latSign;
|
||||
};
|
||||
|
||||
template <class BitStream>
|
||||
bool HuffmanStream<BitStream>::sign(int &val)
|
||||
{
|
||||
quint32 bit;
|
||||
val = 0;
|
||||
|
||||
if (!_bs.read(1, bit))
|
||||
return false;
|
||||
if (bit) {
|
||||
if (!_bs.read(1, bit))
|
||||
return false;
|
||||
val = bit ? -1 : 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class BitStream>
|
||||
bool HuffmanStream<BitStream>::read(int bits, quint32 &val)
|
||||
{
|
||||
if (_symbolDataSize < (quint32)bits) {
|
||||
quint32 next;
|
||||
quint8 nextSize = qMin((quint64)(32 - _symbolDataSize),
|
||||
_bs.bitsAvailable());
|
||||
|
||||
if (!_bs.read(nextSize, next))
|
||||
return false;
|
||||
|
||||
_symbolData = (_symbolData << nextSize) | next;
|
||||
_symbolDataSize += nextSize;
|
||||
}
|
||||
|
||||
if (_symbolDataSize < (quint32)bits)
|
||||
return false;
|
||||
|
||||
val = (_symbolData << (32-_symbolDataSize)) >> (32 - bits);
|
||||
_symbolDataSize -= bits;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class BitStream>
|
||||
bool HuffmanStream<BitStream>::readSymbol(quint32 &symbol)
|
||||
{
|
||||
quint8 size;
|
||||
quint32 next;
|
||||
quint8 nextSize = qMin((quint64)(32 - _symbolDataSize), _bs.bitsAvailable());
|
||||
|
||||
if (!_bs.read(nextSize, next))
|
||||
return false;
|
||||
|
||||
_symbolData = (_symbolData << nextSize) | next;
|
||||
_symbolDataSize += nextSize;
|
||||
|
||||
symbol = _table.symbol(_symbolData << (32 - _symbolDataSize), size);
|
||||
if (size > _symbolDataSize)
|
||||
return false;
|
||||
|
||||
_symbolDataSize -= size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class BitStream>
|
||||
bool HuffmanStream<BitStream>::readDelta(int sign, qint32 &delta)
|
||||
{
|
||||
quint32 symbol;
|
||||
if (!readSymbol(symbol))
|
||||
return false;
|
||||
|
||||
if (symbol && !sign) {
|
||||
if (!_symbolDataSize)
|
||||
return false;
|
||||
else {
|
||||
sign = ((1U << (_symbolDataSize - 1)) & _symbolData) ? -1 : 1;
|
||||
_symbolDataSize--;
|
||||
}
|
||||
}
|
||||
delta = sign * symbol;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class HuffmanStreamF : public HuffmanStream<BitStream4F> {
|
||||
public:
|
||||
HuffmanStreamF(BitStream4F &bitstream, const HuffmanTable &table)
|
||||
: HuffmanStream(bitstream, table) {}
|
||||
|
||||
bool init(bool line);
|
||||
bool readOffset(qint32 &lonDelta, qint32 &latDelta)
|
||||
{return (readDelta(1, lonDelta) && readDelta(1, latDelta));}
|
||||
};
|
||||
|
||||
class HuffmanStreamR : public HuffmanStream<BitStream4R> {
|
||||
public:
|
||||
HuffmanStreamR(BitStream4R &bitstream, const HuffmanTable &table)
|
||||
: HuffmanStream(bitstream, table) {}
|
||||
|
||||
bool init();
|
||||
bool init(int lonSign, int latSign, quint32 data, quint32 dataSize);
|
||||
};
|
||||
|
||||
#endif // HUFFMANSTREAM_H
|
||||
|
@ -42,6 +42,8 @@ bool HuffmanTable::load(const SubFile &file, SubFile::Handle &hdl,
|
||||
_s10 = _s14 + _s1c * _s1d;
|
||||
_s18 = _s10 + (_s1 << _s0);
|
||||
|
||||
_id = id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -55,7 +57,7 @@ bool HuffmanTable::getBuffer(const SubFile &file, SubFile::Handle &hdl,
|
||||
return false;
|
||||
if (!file.readVUInt32(hdl, recordSize))
|
||||
return false;
|
||||
recordOffset = hdl.pos() + recordSize;
|
||||
recordOffset = file.pos(hdl) + recordSize;
|
||||
if (recordOffset > offset + size)
|
||||
return false;
|
||||
};
|
||||
|
@ -13,6 +13,8 @@ public:
|
||||
quint8 maxSymbolSize() const {return _s2;}
|
||||
quint32 symbol(quint32 data, quint8 &size) const;
|
||||
|
||||
quint8 id() const {return _id;}
|
||||
|
||||
private:
|
||||
bool getBuffer(const SubFile &file, SubFile::Handle &hdl, quint32 offset,
|
||||
quint32 size, quint8 id);
|
||||
@ -22,6 +24,8 @@ private:
|
||||
quint8 *_s10, *_s14, *_s18;
|
||||
quint8 _s1c, _s1d, _s1e, _s1f, _s20;
|
||||
quint16 _s22;
|
||||
|
||||
quint8 _id;
|
||||
};
|
||||
|
||||
#endif // HUFFMANTABLE_H
|
||||
|
@ -59,7 +59,7 @@ IMG::IMG(const QString &fileName) : _file(fileName)
|
||||
|
||||
QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2)));
|
||||
_name = QString::fromLatin1(nba.constData(), nba.size()-1).trimmed();
|
||||
_blockSize = 1 << (e1 + e2);
|
||||
_blockBits = e1 + e2;
|
||||
|
||||
// Read the FAT table
|
||||
quint8 flag;
|
||||
@ -132,7 +132,6 @@ IMG::IMG(const QString &fileName) : _file(fileName)
|
||||
}
|
||||
|
||||
// Create tile tree
|
||||
|
||||
int minMapZoom = 24;
|
||||
for (TileMap::const_iterator it = tileMap.constBegin();
|
||||
it != tileMap.constEnd(); ++it) {
|
||||
@ -159,14 +158,19 @@ IMG::IMG(const QString &fileName) : _file(fileName)
|
||||
minMapZoom = tile->zooms().min();
|
||||
}
|
||||
|
||||
for (TileMap::const_iterator it = tileMap.constBegin();
|
||||
it != tileMap.constEnd(); ++it) {
|
||||
VectorTile *tile = it.value();
|
||||
// Detect and mark basemap
|
||||
TileTree::Iterator it;
|
||||
for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it)) {
|
||||
VectorTile *tile = _tileTree.GetAt(it);
|
||||
if (tile->zooms().min() > minMapZoom)
|
||||
_baseMap = true;
|
||||
if (tile->zooms().min() == minMapZoom)
|
||||
tile->markAsBasemap();
|
||||
}
|
||||
// Allow some extra zoom out on maps without basemaps, but not too much as
|
||||
// this would kill the rendering performance
|
||||
if (!_baseMap)
|
||||
_zooms.setMin(_zooms.min() - 2);
|
||||
|
||||
if (!_tileTree.Count())
|
||||
_errorString = "No usable map tile found";
|
||||
@ -197,9 +201,9 @@ template<class T> bool IMG::readValue(T &val)
|
||||
|
||||
bool IMG::readBlock(int blockNum, char *data)
|
||||
{
|
||||
if (!_file.seek((qint64)blockNum * (qint64)_blockSize))
|
||||
if (!_file.seek((quint64)blockNum << _blockBits))
|
||||
return false;
|
||||
if (read(data, _blockSize) < _blockSize)
|
||||
if (read(data, 1U<<_blockBits) < 1U<<_blockBits)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -14,14 +14,14 @@ public:
|
||||
private:
|
||||
friend class SubFile;
|
||||
|
||||
int blockSize() const {return _blockSize;}
|
||||
unsigned blockBits() const {return _blockBits;}
|
||||
bool readBlock(int blockNum, char *data);
|
||||
qint64 read(char *data, qint64 maxSize);
|
||||
template<class T> bool readValue(T &val);
|
||||
|
||||
QFile _file;
|
||||
quint8 _key;
|
||||
int _blockSize;
|
||||
unsigned _blockBits;
|
||||
};
|
||||
|
||||
#endif // IMG_H
|
||||
|
@ -54,7 +54,7 @@ inline bool pointCb(VectorTile *tile, void *context)
|
||||
}
|
||||
|
||||
|
||||
MapData::MapData() : _typ(0), _style(0), _zooms(15, 28), _baseMap(false),
|
||||
MapData::MapData() : _typ(0), _style(0), _zooms(24, 28), _baseMap(false),
|
||||
_valid(false)
|
||||
{
|
||||
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);
|
||||
|
@ -1,26 +1,508 @@
|
||||
#include "bitstream.h"
|
||||
#include "huffmanstream.h"
|
||||
#include "subdiv.h"
|
||||
#include "nodfile.h"
|
||||
#include "lblfile.h"
|
||||
#include "netfile.h"
|
||||
|
||||
|
||||
static bool readAdjCounts(BitStream4R &bs, QVector<quint16> &cnts, quint16 &mask)
|
||||
{
|
||||
quint32 val, cnt, bits;
|
||||
if (!bs.read(4, val))
|
||||
return false;
|
||||
|
||||
cnt = ((val >> 2) & 3) + 2;
|
||||
bits = ((val * 2) & 6) + 4;
|
||||
mask = 1<<(3 + ((val * 2) & 6));
|
||||
if (cnt == 5) {
|
||||
if (!bs.read(8, cnt))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cnt < 2)
|
||||
return false;
|
||||
cnts.resize(cnt - 1);
|
||||
for (int i = 0; i < cnts.size(); i++)
|
||||
if (!bs.read(bits, cnts[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool skipShape(BitStream4R &bs)
|
||||
{
|
||||
quint32 v1, v2, v2b;
|
||||
|
||||
if (!bs.readVuint32SM(v1, v2, v2b))
|
||||
return false;
|
||||
|
||||
return bs.skip(v1);
|
||||
}
|
||||
|
||||
static bool skipAdjShapes(BitStream4R &bs, const QVector<quint16> &cnts,
|
||||
quint16 mask, bool firstIsShape)
|
||||
{
|
||||
if (firstIsShape && !skipShape(bs))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < cnts.size(); i++) {
|
||||
if (cnts.at(i) & mask) {
|
||||
if (!skipShape(bs))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool seekToLevel(BitStream4R &bs, quint8 level)
|
||||
{
|
||||
quint32 v1, v2, v2b;
|
||||
|
||||
for (quint8 i = 1; i < level; ) {
|
||||
if (!bs.readVuint32SM(v1, v2, v2b))
|
||||
return false;
|
||||
if (!bs.skip(v1))
|
||||
return false;
|
||||
|
||||
if (v2 & 2)
|
||||
return false;
|
||||
if (v2 & 1)
|
||||
i++;
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool seekToLine(BitStream4R &bs, quint8 line)
|
||||
{
|
||||
quint32 v1, v2, v2b;
|
||||
|
||||
for (quint8 i = 0; i < line; i++) {
|
||||
if (!bs.readVuint32SM(v1, v2, v2b))
|
||||
return false;
|
||||
if (!bs.skip(v1))
|
||||
return false;
|
||||
|
||||
if (v2 & 2)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readLine(BitStream4R &bs, const SubDiv *subdiv,
|
||||
const HuffmanTable &table, IMG::Poly &poly)
|
||||
{
|
||||
quint32 v1, v2, v2b;
|
||||
if (!bs.readVuint32SM(v1, v2, v2b))
|
||||
return false;
|
||||
bs.resize(v1);
|
||||
|
||||
quint32 lon, lat;
|
||||
if (!(bs.read(0x12 - v2b, lon) && bs.read(16, lat)))
|
||||
return false;
|
||||
if (2 < v2b)
|
||||
lon |= (v2 >> 2) << (0x12U - v2b);
|
||||
|
||||
QPoint pos = QPoint(LS(subdiv->lon(), 8) + LS((qint16)lon, 32-subdiv->bits()),
|
||||
LS(subdiv->lat(), 8) + LS((qint16)lat, 32-subdiv->bits()));
|
||||
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
|
||||
|
||||
poly.boundingRect = RectC(c, c);
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
|
||||
HuffmanStreamR stream(bs, table);
|
||||
if (!stream.init())
|
||||
return false;
|
||||
qint32 lonDelta, latDelta;
|
||||
|
||||
while (stream.readNext(lonDelta, latDelta)) {
|
||||
pos.rx() += LS(lonDelta, 32-subdiv->bits());
|
||||
if (pos.rx() < 0 && subdiv->lon() >= 0)
|
||||
pos.rx() = 0x7fffffff;
|
||||
pos.ry() += LS(latDelta, 32-subdiv->bits());
|
||||
|
||||
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
}
|
||||
|
||||
return stream.atEnd();
|
||||
}
|
||||
|
||||
static bool readNodeGeometry(NODFile *nod, SubFile::Handle &nodHdl,
|
||||
NODFile::AdjacencyInfo &adj, IMG::Poly &poly, quint16 cnt = 0xFFFF)
|
||||
{
|
||||
for (int i = 0; i <= cnt; i++) {
|
||||
int ret = nod->nextNode(nodHdl, adj);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else if (ret > 0)
|
||||
return (cnt == 0xFFFF);
|
||||
|
||||
Coordinates c(toWGS32(adj.nodeInfo.pos.x()),
|
||||
toWGS32(adj.nodeInfo.pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool skipNodes(NODFile *nod, SubFile::Handle &nodHdl,
|
||||
NODFile::AdjacencyInfo &adj, int cnt)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
if (nod->nextNode(nodHdl, adj))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readShape(NODFile *nod, SubFile::Handle &nodHdl,
|
||||
NODFile::AdjacencyInfo &adj, BitStream4R &bs, const HuffmanTable &table,
|
||||
const SubDiv *subdiv, quint32 shift, IMG::Poly &poly, quint16 cnt = 0xFFFF,
|
||||
bool check = false)
|
||||
{
|
||||
quint32 v1, v2, v2b;
|
||||
if (!bs.readVuint32SM(v1, v2, v2b))
|
||||
return false;
|
||||
BitStream4R::State state;
|
||||
bs.save(state);
|
||||
bs.resize(v1);
|
||||
|
||||
quint32 flags;
|
||||
if (!bs.read(8, flags))
|
||||
return false;
|
||||
flags |= (v2 << 8);
|
||||
|
||||
bool hasCoordinatesAdjustBit = flags & (1 << (v2b + 7));
|
||||
bool useEosBit = flags & (1 << (v2b + 5));
|
||||
bool startWithStream = flags & (1 << (v2b + 6));
|
||||
|
||||
quint32 extraBits;
|
||||
int lonSign, latSign;
|
||||
|
||||
if ((flags >> (v2b + 4) & 1) == 0) {
|
||||
extraBits = v2b + 4;
|
||||
lonSign = 0;
|
||||
} else {
|
||||
extraBits = v2b + 3;
|
||||
lonSign = 1;
|
||||
if ((flags >> (v2b + 3) & 1) != 0) {
|
||||
lonSign = -1;
|
||||
}
|
||||
}
|
||||
extraBits -= 1;
|
||||
if ((flags >> extraBits & 1) == 0) {
|
||||
latSign = 0;
|
||||
} else {
|
||||
extraBits -= 1;
|
||||
latSign = -1;
|
||||
if ((flags >> extraBits & 1) == 0) {
|
||||
latSign = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nod->nextNode(nodHdl, adj))
|
||||
return false;
|
||||
QPoint pos(adj.nodeInfo.pos);
|
||||
quint16 nodes = 0;
|
||||
|
||||
if (!startWithStream) {
|
||||
Coordinates c(toWGS32(adj.nodeInfo.pos.x()),
|
||||
toWGS32(adj.nodeInfo.pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
|
||||
while (!adj.eog) {
|
||||
int ret = nod->nextNode(nodHdl, adj);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else if (ret > 0)
|
||||
break;
|
||||
nodes++;
|
||||
|
||||
c = Coordinates(toWGS32(adj.nodeInfo.pos.x()),
|
||||
toWGS32(adj.nodeInfo.pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
pos = adj.nodeInfo.pos;
|
||||
}
|
||||
}
|
||||
|
||||
HuffmanStreamR stream(bs, table);
|
||||
if (!stream.init(lonSign, latSign, flags, extraBits))
|
||||
return false;
|
||||
qint32 lonDelta, latDelta;
|
||||
QVector<QPoint> deltas;
|
||||
|
||||
quint32 adjustBit = 0;
|
||||
quint32 stepsCnt = 0;
|
||||
quint32 steps = 0;
|
||||
quint32 eos = 0;
|
||||
|
||||
while (true) {
|
||||
if ((stepsCnt == steps) && !useEosBit) {
|
||||
if (!stream.readSymbol(steps))
|
||||
break;
|
||||
if (!steps)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!stream.readNext(lonDelta, latDelta))
|
||||
break;
|
||||
|
||||
if (hasCoordinatesAdjustBit && !stream.read(1, adjustBit))
|
||||
return false;
|
||||
|
||||
stepsCnt++;
|
||||
|
||||
if (useEosBit) {
|
||||
if (!stream.read(1, eos))
|
||||
return false;
|
||||
} else {
|
||||
if (steps == stepsCnt)
|
||||
eos = 1;
|
||||
}
|
||||
|
||||
if (!startWithStream) {
|
||||
pos.rx() += LS(lonDelta, 32-subdiv->bits()-shift);
|
||||
pos.ry() += LS(latDelta, 32-subdiv->bits()-shift);
|
||||
|
||||
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
} else {
|
||||
deltas.append(QPoint(lonDelta, latDelta));
|
||||
poly.points.append(QPointF());
|
||||
}
|
||||
|
||||
if (startWithStream && eos) {
|
||||
for (int i = deltas.size() - 1, j = 0; i >= 0; i--, j++) {
|
||||
pos.rx() -= LS(deltas.at(i).x(), 32-subdiv->bits()-shift);
|
||||
pos.ry() -= LS(deltas.at(i).y(), 32-subdiv->bits()-shift);
|
||||
|
||||
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
|
||||
poly.points[poly.points.size() - 1 - j] = QPointF(c.lon(), c.lat());
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
}
|
||||
|
||||
pos = adj.nodeInfo.pos;
|
||||
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
|
||||
stepsCnt = 0;
|
||||
steps = 0;
|
||||
startWithStream = false;
|
||||
|
||||
if (adj.eog)
|
||||
eos = 0;
|
||||
}
|
||||
|
||||
if (eos) {
|
||||
if (nodes >= cnt)
|
||||
break;
|
||||
|
||||
do {
|
||||
int ret = nod->nextNode(nodHdl, adj);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else if (ret > 0)
|
||||
break;
|
||||
nodes++;
|
||||
|
||||
if (check && nodes == cnt) {
|
||||
if (!(bs.restore(state) && bs.skip(v1)
|
||||
&& bs.readVuint32SM(v1, v2, v2b)))
|
||||
return false;
|
||||
if (5 < v2b)
|
||||
v2 >>= v2b - 2;
|
||||
if (v2 & 1)
|
||||
break;
|
||||
}
|
||||
|
||||
Coordinates c(toWGS32(adj.nodeInfo.pos.x()),
|
||||
toWGS32(adj.nodeInfo.pos.y()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
poly.boundingRect = poly.boundingRect.united(c);
|
||||
pos = adj.nodeInfo.pos;
|
||||
} while (!adj.eog && nodes < cnt);
|
||||
|
||||
if (nodes == cnt)
|
||||
break;
|
||||
|
||||
steps = 0;
|
||||
stepsCnt = 0;
|
||||
eos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool NETFile::linkLabel(Handle &hdl, quint32 offset, quint32 size, LBLFile *lbl,
|
||||
Handle &lblHdl, Label &label)
|
||||
{
|
||||
if (!seek(hdl, offset))
|
||||
return false;
|
||||
BitStream1 bs(*this, hdl, size);
|
||||
|
||||
quint32 flags, b, labelPtr = 0;
|
||||
if (!bs.read(8, flags))
|
||||
return false;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!bs.read(8, b))
|
||||
return false;
|
||||
labelPtr |= (b << (i * 8));
|
||||
}
|
||||
|
||||
if (lbl && (labelPtr & 0x3FFFFF)) {
|
||||
if (labelPtr & 0x400000) {
|
||||
quint32 lblOff;
|
||||
if (lblOffset(hdl, labelPtr & 0x3FFFFF, lblOff) && lblOff)
|
||||
label = lbl->label(lblHdl, lblOff);
|
||||
|
||||
} else
|
||||
label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NETFile::init(Handle &hdl)
|
||||
{
|
||||
quint8 multiplier;
|
||||
quint16 hdrLen;
|
||||
|
||||
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)
|
||||
&& seek(hdl, _gmpOffset + 0x15) && readUInt32(hdl, _offset)
|
||||
&& readUInt32(hdl, _size) && readUInt8(hdl, multiplier)))
|
||||
&& readUInt32(hdl, _size) && readUInt8(hdl, _shift)))
|
||||
return false;
|
||||
|
||||
_multiplier = 1<<multiplier;
|
||||
if (hdrLen >= 0x47) {
|
||||
quint32 info;
|
||||
if (!(seek(hdl, _gmpOffset + 0x37) && readUInt32(hdl, info)))
|
||||
return false;
|
||||
_tableId = ((info >> 2) & 0xF);
|
||||
|
||||
if (!(seek(hdl, _gmpOffset + 0x43) && readUInt32(hdl, _linksOffset)
|
||||
&& readUInt32(hdl, _linksSize) && readUInt8(hdl, _linksShift)))
|
||||
return false;
|
||||
}
|
||||
|
||||
_init = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NETFile::link(const SubDiv *subdiv, quint32 shift, Handle &hdl,
|
||||
NODFile *nod, Handle &nodHdl, LBLFile *lbl, Handle &lblHdl,
|
||||
const NODFile::BlockInfo blockInfo, quint8 linkId, quint8 lineId,
|
||||
const HuffmanTable &table, QList<IMG::Poly> *lines)
|
||||
{
|
||||
if (!_init && !init(hdl))
|
||||
return false;
|
||||
|
||||
Q_ASSERT(_tableId == table.id());
|
||||
if (_tableId != table.id())
|
||||
return false;
|
||||
|
||||
IMG::Poly poly;
|
||||
if (!nod->linkType(nodHdl, blockInfo, linkId, poly.type))
|
||||
return false;
|
||||
|
||||
NODFile::LinkInfo linkInfo;
|
||||
if (!nod->linkInfo(nodHdl, blockInfo, linkId, linkInfo))
|
||||
return false;
|
||||
|
||||
quint32 linkOffset = _linksOffset + (linkInfo.linkOffset << _linksShift);
|
||||
if (linkOffset > _linksOffset + _linksSize)
|
||||
return false;
|
||||
if (!seek(hdl, linkOffset))
|
||||
return false;
|
||||
BitStream4R bs(*this, hdl, linkOffset - _linksOffset);
|
||||
QVector<quint16> ca;
|
||||
quint16 mask = 0;
|
||||
quint32 size;
|
||||
|
||||
quint8 s68 = (linkInfo.flags >> 0x12) & 1;
|
||||
quint8 s69 = (linkInfo.flags >> 0x11) & 1;
|
||||
quint8 s6a = (linkInfo.flags >> 0x13) & 1;
|
||||
|
||||
if (s69 == 0 || s6a == 1) {
|
||||
if (!bs.readVUInt32(size))
|
||||
return false;
|
||||
}
|
||||
if (s69 == 0) {
|
||||
if (!readAdjCounts(bs, ca, mask))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subdiv->level()) {
|
||||
NODFile::AdjacencyInfo adj(nod, blockInfo, linkId, linkInfo);
|
||||
|
||||
if (s69 == 1) {
|
||||
if (s68 == 1) {
|
||||
if (!readShape(nod, nodHdl, adj, bs, table, subdiv, shift, poly))
|
||||
return false;
|
||||
} else {
|
||||
if (!readNodeGeometry(nod, nodHdl, adj, poly))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
quint16 mask2 = mask + 0xffff;
|
||||
for (int i = 0; i <= ca.size(); i++) {
|
||||
quint16 step = (i < ca.size()) ? ca.at(i) & mask2 : 0xFFFF;
|
||||
bool shape = (i > 0) ? ca.at(i-1) & mask : (s68 == 1);
|
||||
if (i == lineId) {
|
||||
if (shape) {
|
||||
bool check = (i < ca.size()) ? (ca.at(i) & mask) : false;
|
||||
if (!readShape(nod, nodHdl, adj, bs, table, subdiv,
|
||||
shift, poly, step, check))
|
||||
return false;
|
||||
} else {
|
||||
if (!readNodeGeometry(nod, nodHdl, adj, poly, step))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (shape && !skipShape(bs))
|
||||
return false;
|
||||
if (!skipNodes(nod, nodHdl, adj, step))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!skipAdjShapes(bs, ca, mask, s68 == 1))
|
||||
return false;
|
||||
|
||||
if (!seekToLevel(bs, subdiv->level()))
|
||||
return false;
|
||||
if (!seekToLine(bs, lineId))
|
||||
return false;
|
||||
if (!readLine(bs, subdiv, table, poly))
|
||||
return false;
|
||||
}
|
||||
|
||||
linkLabel(hdl, linkOffset, _linksSize - (linkOffset - _linksOffset), lbl,
|
||||
lblHdl, poly.label);
|
||||
|
||||
lines->append(poly);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NETFile::lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset)
|
||||
{
|
||||
if (!_multiplier && !init(hdl))
|
||||
if (!_init && !init(hdl))
|
||||
return false;
|
||||
|
||||
if (!(seek(hdl, _offset + netOffset * _multiplier)
|
||||
if (!(seek(hdl, _offset + (netOffset << _shift))
|
||||
&& readUInt24(hdl, lblOffset)))
|
||||
return false;
|
||||
|
||||
|
@ -1,25 +1,42 @@
|
||||
#ifndef NETFILE_H
|
||||
#define NETFILE_H
|
||||
|
||||
#include "img.h"
|
||||
#include "subfile.h"
|
||||
#include "nodfile.h"
|
||||
|
||||
class NODFile;
|
||||
class LBLFile;
|
||||
class SubDiv;
|
||||
class HuffmanTable;
|
||||
|
||||
class NETFile : public SubFile
|
||||
{
|
||||
public:
|
||||
NETFile(IMG *img)
|
||||
: SubFile(img), _offset(0), _size(0), _multiplier(0) {}
|
||||
NETFile(const QString &path)
|
||||
: SubFile(path), _offset(0), _size(0), _multiplier(0) {}
|
||||
NETFile(SubFile *gmp, quint32 offset)
|
||||
: SubFile(gmp, offset), _offset(0), _size(0), _multiplier(0) {}
|
||||
NETFile(IMG *img) : SubFile(img), _offset(0), _size(0), _linksOffset(0),
|
||||
_linksSize(0), _shift(0), _linksShift(0), _init(false) {}
|
||||
NETFile(const QString &path) : SubFile(path), _offset(0), _size(0),
|
||||
_linksOffset(0), _linksSize(0), _shift(0), _linksShift(0),
|
||||
_init(false) {}
|
||||
NETFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset),
|
||||
_offset(0), _size(0), _linksOffset(0), _linksSize(0), _shift(0),
|
||||
_linksShift(0), _init(false) {}
|
||||
|
||||
bool lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset);
|
||||
bool link(const SubDiv *subdiv, quint32 shift, Handle &hdl, NODFile *nod,
|
||||
Handle &nodHdl, LBLFile *lbl, Handle &lblHdl,
|
||||
const NODFile::BlockInfo blockInfo, quint8 linkId, quint8 lineId,
|
||||
const HuffmanTable &table, QList<IMG::Poly> *lines);
|
||||
|
||||
private:
|
||||
bool init(Handle &hdl);
|
||||
bool linkLabel(Handle &hdl, quint32 offset, quint32 size, LBLFile *lbl,
|
||||
Handle &lblHdl, Label &label);
|
||||
|
||||
quint32 _offset, _size;
|
||||
quint8 _multiplier;
|
||||
quint32 _offset, _size, _linksOffset, _linksSize;
|
||||
quint8 _shift, _linksShift;
|
||||
quint8 _tableId;
|
||||
bool _init;
|
||||
};
|
||||
|
||||
#endif // NETFILE_H
|
||||
|
553
src/map/IMG/nodfile.cpp
Normal file
@ -0,0 +1,553 @@
|
||||
#include "bitstream.h"
|
||||
#include "nodfile.h"
|
||||
|
||||
|
||||
#define ARRAY_SIZE(array) \
|
||||
(sizeof(array) / sizeof(array[0]))
|
||||
|
||||
static const struct
|
||||
{
|
||||
quint8 lon;
|
||||
quint8 lat;
|
||||
} LLBITS[] = {
|
||||
{0xc, 0xc}, {0x8, 0x10}, {0x10, 0x8}, {0x10, 0x10}, {0xc, 0x14},
|
||||
{0x14, 0xc}, {0x14, 0x14}
|
||||
};
|
||||
|
||||
struct NodeOffset
|
||||
{
|
||||
bool ext;
|
||||
union {
|
||||
qint32 offset;
|
||||
quint8 id;
|
||||
} u;
|
||||
};
|
||||
|
||||
static bool adjDistInfo(BitStream1 &bs, bool extraBit, bool &eog)
|
||||
{
|
||||
quint32 data, cnt;
|
||||
|
||||
if (!bs.read(extraBit | 8, data))
|
||||
return false;
|
||||
|
||||
data <<= !extraBit;
|
||||
eog |= (quint8)data & 1;
|
||||
data >>= 1;
|
||||
|
||||
for (cnt = 0; (data >> cnt) & 1; cnt++) {
|
||||
if (cnt == 4)
|
||||
break;
|
||||
}
|
||||
if (!bs.read(cnt * 4, data))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool adjNodeInfo(BitStream1 &bs, bool extraBit, NodeOffset &offset)
|
||||
{
|
||||
quint32 data;
|
||||
|
||||
if (!bs.read(9, data))
|
||||
return false;
|
||||
|
||||
data <<= !extraBit;
|
||||
|
||||
if (data & 1) {
|
||||
offset.ext = true;
|
||||
offset.u.id = data >> 1;
|
||||
} else {
|
||||
quint32 bits = (data >> 1) & 7;
|
||||
quint32 data2;
|
||||
|
||||
if (!bs.read(bits + extraBit + 1, data2))
|
||||
return false;
|
||||
|
||||
data = data2 << (6 - extraBit) | data >> 4;
|
||||
bits = 0x19 - bits;
|
||||
offset.ext = false;
|
||||
offset.u.offset = ((qint32)(data << bits) >> bits);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool skipOptAdjData(BitStream1 &bs)
|
||||
{
|
||||
// TODO
|
||||
Q_UNUSED(bs);
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool NODFile::init(Handle &hdl)
|
||||
{
|
||||
quint16 hdrLen;
|
||||
|
||||
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)))
|
||||
return false;
|
||||
if (hdrLen < 0x7b)
|
||||
return false;
|
||||
|
||||
if (!(seek(hdl, _gmpOffset + 0x1d) && readUInt32(hdl, _flags)
|
||||
&& readUInt8(hdl, _blockShift) && readUInt8(hdl, _nodeShift)))
|
||||
return false;
|
||||
|
||||
if (!(seek(hdl, _gmpOffset + 0x67) && readUInt32(hdl, _blockOffset)
|
||||
&& readUInt32(hdl, _blockSize) && readUInt16(hdl, _blockRecordSize)
|
||||
&& readUInt32(hdl, _indexOffset) && readUInt32(hdl, _indexSize)
|
||||
&& readUInt16(hdl, _indexRecordSize) && readUInt32(hdl, _indexFlags)))
|
||||
return false;
|
||||
|
||||
if (!_indexRecordSize)
|
||||
return false;
|
||||
quint32 indexCount = _indexSize / _indexRecordSize;
|
||||
if (indexCount <= 0x100)
|
||||
_indexIdSize = 1;
|
||||
else if (indexCount <= 0x1000)
|
||||
_indexIdSize = 2;
|
||||
else if (indexCount <= 0x1000000)
|
||||
_indexIdSize = 3;
|
||||
|
||||
return (_indexIdSize > 0);
|
||||
}
|
||||
|
||||
quint32 NODFile::indexIdSize(Handle &hdl)
|
||||
{
|
||||
if (!_indexIdSize && !init(hdl))
|
||||
return 0;
|
||||
|
||||
return _indexIdSize;
|
||||
}
|
||||
|
||||
bool NODFile::readBlock(Handle &hdl, quint32 blockOffset,
|
||||
BlockInfo &blockInfo) const
|
||||
{
|
||||
blockInfo.offset = blockOffset;
|
||||
|
||||
if (!(seek(hdl, blockInfo.offset + _blockOffset)
|
||||
&& readUInt16(hdl, blockInfo.hdr.s0) && readUInt32(hdl, blockInfo.hdr.s2)
|
||||
&& readUInt32(hdl, blockInfo.hdr.s6) && readUInt32(hdl, blockInfo.hdr.sa)
|
||||
&& readUInt16(hdl, blockInfo.hdr.se) && readUInt8(hdl, blockInfo.hdr.s10)
|
||||
&& readUInt8(hdl, blockInfo.hdr.s11) && readUInt8(hdl, blockInfo.hdr.s12)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NODFile::blockInfo(Handle &hdl, quint32 blockId, BlockInfo &blockInfo) const
|
||||
{
|
||||
quint32 blockOffset;
|
||||
quint32 offset = _indexRecordSize * blockId + _indexOffset;
|
||||
quint32 offsetSize = (_indexFlags & 3) + 1;
|
||||
|
||||
if (offset > _indexOffset + _indexSize)
|
||||
return false;
|
||||
if (!(seek(hdl, offset) && readVUInt32(hdl, offsetSize, blockOffset)))
|
||||
return false;
|
||||
|
||||
return readBlock(hdl, blockOffset << _blockShift, blockInfo);
|
||||
}
|
||||
|
||||
bool NODFile::linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
|
||||
LinkInfo &linkInfo) const
|
||||
{
|
||||
if (linkId >= blockInfo.hdr.s10)
|
||||
return false;
|
||||
|
||||
quint32 infoOffset = ((blockInfo.hdr.se * linkId) >> 3) + 0x13
|
||||
+ ((blockInfo.hdr.s0 >> 0xb) & 1) + blockInfo.offset + _blockOffset;
|
||||
quint32 s1 = ((blockInfo.hdr.s0 >> 2) & 0x1f) + 8;
|
||||
quint32 s2 = (blockInfo.hdr.s0 >> 7) & 0xf;
|
||||
quint32 skip = (blockInfo.hdr.se * linkId) & 7;
|
||||
|
||||
if (infoOffset > _blockOffset + _blockSize || infoOffset < blockInfo.offset)
|
||||
return false;
|
||||
if (!seek(hdl, infoOffset))
|
||||
return false;
|
||||
|
||||
quint32 unused, flags;
|
||||
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
|
||||
if (!(bs.read(skip, unused) && bs.read(0xc, flags)))
|
||||
return false;
|
||||
|
||||
linkInfo.flags = ((flags << 8) & 0xf0000) | (flags & 0xff);
|
||||
|
||||
if (!(flags << 8 & 0x10000)) {
|
||||
if (!bs.read(s1, linkInfo.linkOffset))
|
||||
return false;
|
||||
} else {
|
||||
if (!bs.read(s1 - s2, linkInfo.linkOffset))
|
||||
return false;
|
||||
linkInfo.linkOffset += blockInfo.hdr.sa;
|
||||
}
|
||||
|
||||
if (!bs.read(s2, linkInfo.nodeOffset))
|
||||
return false;
|
||||
linkInfo.nodeOffset = (blockInfo.offset - linkInfo.nodeOffset)
|
||||
>> _nodeShift;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NODFile::nodeInfo(Handle &hdl, const BlockInfo &blockInfo,
|
||||
quint32 nodeOffset, NodeInfo &nodeInfo) const
|
||||
{
|
||||
quint32 infoOffset = (nodeOffset << _nodeShift) + _blockOffset;
|
||||
if (infoOffset > _blockOffset + _blockSize || infoOffset < blockInfo.offset)
|
||||
return false;
|
||||
if (!seek(hdl, infoOffset))
|
||||
return false;
|
||||
|
||||
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
|
||||
|
||||
if (!bs.read(8, nodeInfo.flags))
|
||||
return false;
|
||||
|
||||
if ((nodeInfo.flags & 7) >= ARRAY_SIZE(LLBITS))
|
||||
return false;
|
||||
quint8 lonBits = LLBITS[nodeInfo.flags & 7].lon;
|
||||
quint8 latBits = LLBITS[nodeInfo.flags & 7].lat;
|
||||
quint8 maxBits = ((_flags >> 10) & 7) | 0x18;
|
||||
|
||||
quint32 lon, lat;
|
||||
if (!(bs.read(lonBits, lon) && bs.read(latBits, lat)))
|
||||
return false;
|
||||
|
||||
quint8 lonShift = 0x20 - lonBits;
|
||||
quint8 latShift = 0x20 - latBits;
|
||||
quint8 shift = 0x20 - maxBits;
|
||||
QPoint pos((((int)(lon << lonShift) >> lonShift) << shift)
|
||||
+ blockInfo.hdr.s2, (((int)(lat << latShift) >> latShift) << shift)
|
||||
+ blockInfo.hdr.s6);
|
||||
nodeInfo.bytes = ((lonBits + latBits) >> 3) + 1;
|
||||
|
||||
if ((maxBits < 0x1c) && (nodeInfo.flags & 8)) {
|
||||
quint8 extraBits = 0x1c - maxBits;
|
||||
quint32 extraLon, extraLat;
|
||||
|
||||
if (!(bs.read(extraBits, extraLon) && bs.read(extraBits, extraLat)))
|
||||
return false;
|
||||
pos.setX(pos.x() | extraLon << 4); pos.setY(pos.y() | extraLat << 4);
|
||||
nodeInfo.bytes++;
|
||||
}
|
||||
// TODO?: extra bits
|
||||
|
||||
nodeInfo.pos = pos;
|
||||
nodeInfo.flags &= 0xf8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NODFile::nodeOffset(Handle &hdl, const BlockInfo &blockInfo,
|
||||
quint8 nodeId, quint32 &nodeOffset) const
|
||||
{
|
||||
if (nodeId >= blockInfo.hdr.s11)
|
||||
return false;
|
||||
|
||||
quint32 offset = ((blockInfo.hdr.s10 * blockInfo.hdr.se + 7) >> 3)
|
||||
+ 0x13 + nodeId * 3 + _blockOffset + blockInfo.offset
|
||||
+ ((blockInfo.hdr.s0 >> 0xb) & 1);
|
||||
|
||||
if (!(seek(hdl, offset) && readUInt24(hdl, nodeOffset)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NODFile::nodeBlock(Handle &hdl, quint32 nodeOffset,
|
||||
BlockInfo &blockInfo) const
|
||||
{
|
||||
int low = 0;
|
||||
int high = _indexSize / _indexRecordSize - 1;
|
||||
quint32 offsetSize = (_indexFlags & 3) + 1;
|
||||
|
||||
while (low <= high) {
|
||||
quint32 m = ((low + high) / 2);
|
||||
quint32 offset = _indexRecordSize * m + _indexOffset;
|
||||
quint32 blockOffset, prevBlockOffset;
|
||||
|
||||
if (m > 0) {
|
||||
if (!(seek(hdl, offset - _indexRecordSize)
|
||||
&& readVUInt32(hdl, offsetSize, prevBlockOffset)
|
||||
&& readVUInt32(hdl, offsetSize, blockOffset)))
|
||||
return false;
|
||||
} else {
|
||||
if (!(seek(hdl, offset)
|
||||
&& readVUInt32(hdl, offsetSize, blockOffset)))
|
||||
return false;
|
||||
prevBlockOffset = 0;
|
||||
}
|
||||
prevBlockOffset <<= _blockShift;
|
||||
blockOffset <<= _blockShift;
|
||||
|
||||
if (blockOffset < nodeOffset)
|
||||
low = m + 1;
|
||||
else {
|
||||
if (prevBlockOffset <= nodeOffset)
|
||||
return readBlock(hdl, blockOffset, blockInfo);
|
||||
else
|
||||
high = m - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NODFile::absAdjInfo(Handle &hdl, AdjacencyInfo &adj) const
|
||||
{
|
||||
quint32 infoOffset = (adj.nodeOffset << _nodeShift) + _blockOffset
|
||||
+ adj.nodeInfo.bytes;
|
||||
if (!seek(hdl, infoOffset))
|
||||
return false;
|
||||
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
|
||||
|
||||
quint8 linkId = adj.blockInfo.hdr.s10;
|
||||
quint32 m2p = 2;
|
||||
quint32 skip = 8;
|
||||
quint32 flags;
|
||||
quint32 nextOffset = 0xFFFFFFFF;
|
||||
bool extraBit = (adj.nodeInfo.flags >> 6) & 1;
|
||||
bool linkIdValid = true;
|
||||
bool firstLoop = true;
|
||||
NodeOffset offset;
|
||||
|
||||
do {
|
||||
adj.eog = false;
|
||||
|
||||
if (!bs.read(8, flags))
|
||||
return false;
|
||||
|
||||
if (firstLoop) {
|
||||
skip >>= (flags >> 5) & 1;
|
||||
flags |= 0x20;
|
||||
}
|
||||
firstLoop = false;
|
||||
quint32 f4 = flags & 0x10;
|
||||
quint32 f4sn = (f4 >> 4) ^ 1;
|
||||
quint32 m1 = (flags >> 5) & f4sn;
|
||||
quint32 m2 = (f4 >> 3) | (f4sn & (flags >> 6));
|
||||
|
||||
if (m1) {
|
||||
if (!bs.read(8, linkId))
|
||||
return false;
|
||||
linkIdValid = true;
|
||||
}
|
||||
|
||||
if ((m2 != m2p) || (flags & 0x10) || m1) {
|
||||
quint32 data;
|
||||
if (!bs.read(skip, data))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(flags & 0x10)) {
|
||||
if (!adjDistInfo(bs, (m2 == 1 && linkIdValid), adj.eog))
|
||||
return false;
|
||||
|
||||
if (!adjNodeInfo(bs, extraBit, offset))
|
||||
return false;
|
||||
if (!offset.ext)
|
||||
nextOffset = adj.nodeOffset + offset.u.offset;
|
||||
else if (!nodeOffset(adj.extHdl, adj.blockInfo, offset.u.id,
|
||||
nextOffset))
|
||||
return false;
|
||||
|
||||
m2p = m2;
|
||||
}
|
||||
|
||||
if (flags & 0x8) {
|
||||
quint32 data;
|
||||
if (!bs.read(8, data))
|
||||
return false;
|
||||
if (!(data & 0xe0)) {
|
||||
if (!bs.read(8, data))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_flags & 0x18) && !skipOptAdjData(bs))
|
||||
return false;
|
||||
|
||||
if ((m2 == 1) && linkIdValid) {
|
||||
LinkInfo li;
|
||||
if (adj.linkId == 0xFFFFFFFF) {
|
||||
if (!linkInfo(adj.extHdl, adj.blockInfo, linkId, li))
|
||||
return false;
|
||||
} else
|
||||
li.linkOffset = 0xFFFFFFFF;
|
||||
|
||||
if ((adj.linkOffset == li.linkOffset) || (adj.linkId == linkId)) {
|
||||
adj.nodeOffset = nextOffset;
|
||||
if (offset.ext) {
|
||||
adj.linkId = 0xFFFFFFFF;
|
||||
return nodeBlock(hdl, adj.nodeOffset << _nodeShift,
|
||||
adj.blockInfo);
|
||||
} else {
|
||||
adj.linkId = linkId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
linkIdValid = false;
|
||||
}
|
||||
} while (!(flags & 0x80));
|
||||
|
||||
adj.nodeOffset = 0xFFFFFFFF;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NODFile::relAdjInfo(Handle &hdl, AdjacencyInfo &adj) const
|
||||
{
|
||||
quint32 infoOffset = (adj.nodeOffset << _nodeShift) + _blockOffset
|
||||
+ adj.nodeInfo.bytes;
|
||||
if (!seek(hdl, infoOffset))
|
||||
return false;
|
||||
|
||||
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
|
||||
|
||||
quint32 linkId = adj.blockInfo.hdr.s10;
|
||||
quint32 skip = 8;
|
||||
quint32 flagsBits = 8;
|
||||
quint32 flags;
|
||||
quint32 nextOffset = 0xFFFFFFFF;
|
||||
NodeOffset offset;
|
||||
bool extraBit = (adj.nodeInfo.flags >> 6) & 1;
|
||||
bool linkIdValid = true;
|
||||
bool firstLoop = true;
|
||||
|
||||
do {
|
||||
adj.eog = false;
|
||||
|
||||
if (!bs.read(flagsBits, flags))
|
||||
return false;
|
||||
|
||||
flags <<= (8U - flagsBits);
|
||||
if (firstLoop) {
|
||||
skip >>= (flags >> 5) & 1;
|
||||
flags = ((flags >> 1) & 0x20) | (flags & 0xffffffdf);
|
||||
}
|
||||
firstLoop = false;
|
||||
flagsBits >>= (flags >> 3) & 1;
|
||||
quint32 m = (((flags & 0x70) == 0x30) << 1) | ((flags >> 6) & 1);
|
||||
if (!m) {
|
||||
adj.nodeOffset = 0xFFFFFFFF;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((flags & 0x60) == 0x60) {
|
||||
if (!bs.read(8, linkId))
|
||||
return false;
|
||||
linkIdValid = true;
|
||||
}
|
||||
|
||||
if (((flags & 0x70) == 0x70)) {
|
||||
quint32 data;
|
||||
if (!bs.read(skip, data))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & 0x50) == 0x50) {
|
||||
if (!adjDistInfo(bs, false, adj.eog))
|
||||
return false;
|
||||
adj.eog = true;
|
||||
}
|
||||
if ((flags >> 6) & 1) {
|
||||
if (!adjNodeInfo(bs, extraBit, offset))
|
||||
return false;
|
||||
if (!offset.ext)
|
||||
nextOffset = adj.nodeOffset + offset.u.offset;
|
||||
else if (!nodeOffset(adj.extHdl, adj.blockInfo, offset.u.id,
|
||||
nextOffset))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((_flags & 0x18) && !skipOptAdjData(bs))
|
||||
return false;
|
||||
|
||||
if (((m == 1) && linkIdValid)) {
|
||||
LinkInfo li;
|
||||
if (adj.linkId == 0xFFFFFFFF) {
|
||||
if (!linkInfo(adj.extHdl, adj.blockInfo, linkId, li))
|
||||
return false;
|
||||
} else
|
||||
li.linkOffset = 0xFFFFFFFF;
|
||||
|
||||
if ((adj.linkOffset == li.linkOffset) || (adj.linkId == linkId)) {
|
||||
adj.nodeOffset = nextOffset;
|
||||
if (offset.ext) {
|
||||
adj.linkId = 0xFFFFFFFF;
|
||||
return nodeBlock(hdl, adj.nodeOffset << _nodeShift,
|
||||
adj.blockInfo);
|
||||
} else {
|
||||
adj.linkId = linkId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
linkIdValid = false;
|
||||
}
|
||||
} while (!(flags & 0x80));
|
||||
|
||||
adj.nodeOffset = 0xFFFFFFFF;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int NODFile::nextNode(Handle &hdl, AdjacencyInfo &adjInfo)
|
||||
{
|
||||
if (adjInfo.nodeOffset == 0xFFFFFFFF)
|
||||
return 1;
|
||||
|
||||
if (!nodeInfo(hdl, adjInfo.blockInfo, adjInfo.nodeOffset,
|
||||
adjInfo.nodeInfo))
|
||||
return -1;
|
||||
if (!adjacencyInfo(hdl, adjInfo))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool NODFile::linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
|
||||
quint32 &type) const
|
||||
{
|
||||
quint32 offset = ((blockInfo.hdr.s10 * blockInfo.hdr.se + 7) >> 3) + 0x13
|
||||
+ blockInfo.offset + _blockOffset + ((blockInfo.hdr.s0 >> 0xb) & 1)
|
||||
+ blockInfo.hdr.s11 * 3;
|
||||
quint32 low = 0;
|
||||
quint32 high = blockInfo.hdr.s12 - 1;
|
||||
quint32 pos = blockInfo.hdr.s12;
|
||||
quint16 val;
|
||||
|
||||
if (high > 1) {
|
||||
do {
|
||||
pos = (low + high) / 2;
|
||||
|
||||
if (!(seek(hdl, offset + _blockRecordSize * pos)
|
||||
&& readUInt16(hdl, val)))
|
||||
return false;
|
||||
|
||||
if ((val >> 8) <= linkId)
|
||||
low = pos;
|
||||
else
|
||||
high = pos;
|
||||
} while (low + 1 < high);
|
||||
}
|
||||
|
||||
if (!(seek(hdl, offset + _blockRecordSize * low) && readUInt16(hdl, val)))
|
||||
return false;
|
||||
|
||||
type = val & 0x3f;
|
||||
|
||||
if ((low < high) && (pos != high)) {
|
||||
if (!(seek(hdl, offset + _blockRecordSize * high)
|
||||
&& readUInt16(hdl, val)))
|
||||
return false;
|
||||
if ((val >> 8) <= linkId)
|
||||
type = (val & 0x3f);
|
||||
}
|
||||
|
||||
type <<= 8;
|
||||
|
||||
return true;
|
||||
}
|
98
src/map/IMG/nodfile.h
Normal file
@ -0,0 +1,98 @@
|
||||
#ifndef NODFILE_H
|
||||
#define NODFILE_H
|
||||
|
||||
#include "img.h"
|
||||
#include "subfile.h"
|
||||
|
||||
class NODFile : public SubFile
|
||||
{
|
||||
public:
|
||||
struct BlockInfo
|
||||
{
|
||||
quint32 offset;
|
||||
struct
|
||||
{
|
||||
quint32 s2; // node lon base
|
||||
quint32 s6; // node lat base
|
||||
quint32 sa;
|
||||
quint16 s0; // flags
|
||||
quint16 se; // link info bit size
|
||||
quint8 s10; // links count
|
||||
quint8 s11; // nodes count
|
||||
quint8 s12; // link types count
|
||||
} hdr;
|
||||
};
|
||||
|
||||
struct LinkInfo
|
||||
{
|
||||
quint32 linkOffset;
|
||||
quint32 nodeOffset;
|
||||
quint32 flags;
|
||||
};
|
||||
|
||||
struct NodeInfo
|
||||
{
|
||||
QPoint pos;
|
||||
quint8 flags;
|
||||
quint8 bytes;
|
||||
};
|
||||
|
||||
struct AdjacencyInfo
|
||||
{
|
||||
AdjacencyInfo(const SubFile *file, const BlockInfo &blockInfo,
|
||||
quint32 linkId, const LinkInfo &linkInfo) : extHdl(file),
|
||||
blockInfo(blockInfo), nodeOffset(linkInfo.nodeOffset),
|
||||
linkOffset(linkInfo.linkOffset), linkId(linkId)
|
||||
{}
|
||||
|
||||
Handle extHdl;
|
||||
BlockInfo blockInfo;
|
||||
NodeInfo nodeInfo;
|
||||
quint32 nodeOffset;
|
||||
quint32 linkOffset;
|
||||
quint32 linkId;
|
||||
bool eog;
|
||||
};
|
||||
|
||||
NODFile(IMG *img) : SubFile(img), _indexOffset(0), _indexSize(0),
|
||||
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
|
||||
_blockRecordSize(0), _blockShift(0), _nodeShift(0), _indexIdSize(0) {}
|
||||
NODFile(const QString &path) : SubFile(path), _indexOffset(0), _indexSize(0),
|
||||
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
|
||||
_blockRecordSize(0), _blockShift(0), _nodeShift(0), _indexIdSize(0) {}
|
||||
NODFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset),
|
||||
_indexOffset(0), _indexSize(0), _indexFlags(0), _blockOffset(0),
|
||||
_blockSize(0), _indexRecordSize(0), _blockRecordSize(0), _blockShift(0),
|
||||
_nodeShift(0), _indexIdSize(0) {}
|
||||
|
||||
quint32 indexIdSize(Handle &hdl);
|
||||
bool blockInfo(Handle &hdl, quint32 blockId, BlockInfo &blockInfo) const;
|
||||
bool linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
|
||||
LinkInfo &linkInfo) const;
|
||||
bool linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
|
||||
quint32 &type) const;
|
||||
int nextNode(Handle &hdl, AdjacencyInfo &adjInfo);
|
||||
|
||||
private:
|
||||
bool init(Handle &hdl);
|
||||
bool nodeInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 nodeOffset,
|
||||
NodeInfo &nodeInfo) const;
|
||||
bool nodeOffset(Handle &hdl, const BlockInfo &blockInfo, quint8 nodeId,
|
||||
quint32 &nodeOffset) const;
|
||||
bool absAdjInfo(Handle &hdl, AdjacencyInfo &adj) const;
|
||||
bool relAdjInfo(Handle &hdl, AdjacencyInfo &adj) const;
|
||||
bool adjacencyInfo(Handle &hdl, AdjacencyInfo &adj) const
|
||||
{
|
||||
return (adj.nodeInfo.flags & 0x20) ? absAdjInfo(hdl, adj)
|
||||
: relAdjInfo(hdl, adj);
|
||||
}
|
||||
bool nodeBlock(Handle &hdl, quint32 nodeOffset, BlockInfo &blockInfo) const;
|
||||
bool readBlock(Handle &hdl, quint32 blockOffset, BlockInfo &blockInfo) const;
|
||||
|
||||
quint32 _flags, _indexOffset, _indexSize, _indexFlags, _blockOffset,
|
||||
_blockSize;
|
||||
quint16 _indexRecordSize, _blockRecordSize;
|
||||
quint8 _blockShift, _nodeShift, _indexIdSize;
|
||||
};
|
||||
|
||||
#endif // NETFILE_H
|
@ -1,13 +1,15 @@
|
||||
#include <cstring>
|
||||
#include "common/rectc.h"
|
||||
#include "common/garmin.h"
|
||||
#include "deltastream.h"
|
||||
#include "huffmanstream.h"
|
||||
#include "lblfile.h"
|
||||
#include "netfile.h"
|
||||
#include "nodfile.h"
|
||||
#include "rgnfile.h"
|
||||
|
||||
|
||||
#define MASK(bits) ((2U << ((bits) - 1U)) - 1U)
|
||||
|
||||
static quint64 pointId(const QPoint &pos, quint32 type, quint32 labelPtr)
|
||||
{
|
||||
quint64 id;
|
||||
@ -49,11 +51,10 @@ bool RGNFile::skipClassFields(Handle &hdl) const
|
||||
break;
|
||||
}
|
||||
|
||||
return seek(hdl, hdl.pos() + rs);
|
||||
return seek(hdl, pos(hdl) + rs);
|
||||
}
|
||||
|
||||
bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3],
|
||||
SegmentType type) const
|
||||
bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3]) const
|
||||
{
|
||||
quint32 bitfield = 0xFFFFFFFF;
|
||||
|
||||
@ -61,34 +62,38 @@ bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3],
|
||||
if (!readVBitfield32(hdl, bitfield))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 29; i++) {
|
||||
for (int i = 0, j = 0; i < 29; i++) {
|
||||
if ((flags[0] >> i) & 1) {
|
||||
if (bitfield & 1) {
|
||||
quint32 m = flags[(i >> 4) + 1] >> ((i * 2) & 0x1e) & 3;
|
||||
switch (i) {
|
||||
case 5:
|
||||
if (m == 1 && type == Point) {
|
||||
quint16 u16;
|
||||
if (!readUInt16(hdl, u16))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
quint32 m = flags[(j >> 4) + 1] >> ((j * 2) & 0x1e) & 3;
|
||||
|
||||
quint32 skip = 0;
|
||||
if (m == 3) {
|
||||
if (!readVUInt32(hdl, skip))
|
||||
return false;
|
||||
} else
|
||||
skip = m + 1;
|
||||
if (!seek(hdl, pos(hdl) + skip))
|
||||
return false;
|
||||
}
|
||||
bitfield >>= 1;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RGNFile::clearFlags()
|
||||
bool RGNFile::skipGblFields(Handle &hdl, quint32 flags) const
|
||||
{
|
||||
memset(_polygonsFlags, 0, sizeof(_polygonsFlags));
|
||||
memset(_linesFlags, 0, sizeof(_linesFlags));
|
||||
memset(_pointsFlags, 0, sizeof(_pointsFlags));
|
||||
int cnt = 0;
|
||||
|
||||
do {
|
||||
cnt = cnt + (flags & 3);
|
||||
flags = flags >> 2;
|
||||
} while (flags != 0);
|
||||
|
||||
return seek(hdl, pos(hdl) + cnt);
|
||||
}
|
||||
|
||||
bool RGNFile::init(Handle &hdl)
|
||||
@ -102,14 +107,20 @@ bool RGNFile::init(Handle &hdl)
|
||||
|
||||
if (hdrLen >= 0x68) {
|
||||
if (!(readUInt32(hdl, _polygonsOffset) && readUInt32(hdl, _polygonsSize)
|
||||
&& seek(hdl, _gmpOffset + 0x2D) && readUInt32(hdl, _polygonsFlags[0])
|
||||
&& readUInt32(hdl, _polygonsFlags[1]) && readUInt32(hdl, _polygonsFlags[2])
|
||||
&& seek(hdl, _gmpOffset + 0x29) && readUInt32(hdl, _polygonsGblFlags)
|
||||
&& readUInt32(hdl, _polygonsLclFlags[0])
|
||||
&& readUInt32(hdl, _polygonsLclFlags[1])
|
||||
&& readUInt32(hdl, _polygonsLclFlags[2])
|
||||
&& readUInt32(hdl, _linesOffset) && readUInt32(hdl, _linesSize)
|
||||
&& seek(hdl, _gmpOffset + 0x49) && readUInt32(hdl, _linesFlags[0])
|
||||
&& readUInt32(hdl, _linesFlags[1]) && readUInt32(hdl, _linesFlags[2])
|
||||
&& seek(hdl, _gmpOffset + 0x45) && readUInt32(hdl, _linesGblFlags)
|
||||
&& readUInt32(hdl, _linesLclFlags[0])
|
||||
&& readUInt32(hdl, _linesLclFlags[1])
|
||||
&& readUInt32(hdl, _linesLclFlags[2])
|
||||
&& readUInt32(hdl, _pointsOffset) && readUInt32(hdl, _pointsSize)
|
||||
&& seek(hdl, _gmpOffset + 0x65) && readUInt32(hdl, _pointsFlags[0])
|
||||
&& readUInt32(hdl, _pointsFlags[1]) && readUInt32(hdl, _pointsFlags[2])))
|
||||
&& seek(hdl, _gmpOffset + 0x61) && readUInt32(hdl, _pointsGblFlags)
|
||||
&& readUInt32(hdl, _pointsLclFlags[0])
|
||||
&& readUInt32(hdl, _pointsLclFlags[1])
|
||||
&& readUInt32(hdl, _pointsLclFlags[2])))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -147,7 +158,7 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
|
||||
qint16 lon, lat;
|
||||
quint16 len;
|
||||
|
||||
while (hdl.pos() < (int)segment.end()) {
|
||||
while (pos(hdl) < segment.end()) {
|
||||
IMG::Poly poly;
|
||||
|
||||
if (!(readUInt8(hdl, type) && readUInt24(hdl, labelPtr)
|
||||
@ -222,7 +233,7 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
if (!seek(hdl, segment.offset()))
|
||||
return false;
|
||||
|
||||
while (hdl.pos() < (int)segment.end()) {
|
||||
while (pos(hdl) < segment.end()) {
|
||||
IMG::Poly poly;
|
||||
QPoint pos;
|
||||
|
||||
@ -230,6 +241,7 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
&& readInt16(hdl, lon) && readInt16(hdl, lat)
|
||||
&& readVUInt32(hdl, len)))
|
||||
return false;
|
||||
Q_ASSERT(SubFile::pos(hdl) + len <= segment.end());
|
||||
|
||||
poly.type = 0x10000 | (quint16(type)<<8) | (subtype & 0x1F);
|
||||
labelPtr = 0;
|
||||
@ -239,8 +251,10 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
|
||||
|
||||
qint32 lonDelta, latDelta;
|
||||
HuffmanStream stream(*this, hdl, len, _huffmanTable,
|
||||
segmentType == Line);
|
||||
BitStream4F bs(*this, hdl, len);
|
||||
HuffmanStreamF stream(bs, _huffmanTable);
|
||||
if (!stream.init(segmentType == Line))
|
||||
return false;
|
||||
|
||||
if (shift) {
|
||||
if (!stream.readOffset(lonDelta, latDelta))
|
||||
@ -298,7 +312,11 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
if (subtype & 0x80 && !skipClassFields(hdl))
|
||||
return false;
|
||||
if (subtype & 0x40 && !skipLclFields(hdl, segmentType == Line
|
||||
? _linesFlags : _polygonsFlags, segmentType))
|
||||
? _linesLclFlags : _polygonsLclFlags))
|
||||
return false;
|
||||
quint32 gblFlags = (segmentType == Line)
|
||||
? _linesGblFlags : _polygonsGblFlags;
|
||||
if (gblFlags && !skipGblFields(hdl, gblFlags))
|
||||
return false;
|
||||
|
||||
if (lbl && (labelPtr & 0x3FFFFF))
|
||||
@ -317,12 +335,13 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
|
||||
const SubDiv::Segment &segment = (segmentType == IndexedPoint)
|
||||
? subdiv->idxPoints() : subdiv->points();
|
||||
|
||||
|
||||
if (!segment.isValid())
|
||||
return true;
|
||||
if (!seek(hdl, segment.offset()))
|
||||
return false;
|
||||
|
||||
while (hdl.pos() < (int)segment.end()) {
|
||||
while (pos(hdl) < segment.end()) {
|
||||
IMG::Point point;
|
||||
quint8 type, subtype;
|
||||
qint16 lon, lat;
|
||||
@ -365,7 +384,7 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
|
||||
if (!seek(hdl, segment.offset()))
|
||||
return false;
|
||||
|
||||
while (hdl.pos() < (int)segment.end()) {
|
||||
while (pos(hdl) < segment.end()) {
|
||||
IMG::Point point;
|
||||
qint16 lon, lat;
|
||||
quint8 type, subtype;
|
||||
@ -379,7 +398,9 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
|
||||
return false;
|
||||
if (subtype & 0x80 && !skipClassFields(hdl))
|
||||
return false;
|
||||
if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags, Point))
|
||||
if (subtype & 0x40 && !skipLclFields(hdl, _pointsLclFlags))
|
||||
return false;
|
||||
if (_pointsGblFlags && !skipGblFields(hdl, _pointsGblFlags))
|
||||
return false;
|
||||
|
||||
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
|
||||
@ -403,6 +424,87 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RGNFile::links(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
NETFile *net, Handle &netHdl, NODFile *nod, Handle &nodHdl, LBLFile *lbl,
|
||||
Handle &lblHdl, QList<IMG::Poly> *lines) const
|
||||
{
|
||||
quint32 size, blockIndexIdSize, blockIndexId;
|
||||
quint8 flags;
|
||||
const SubDiv::Segment &segment = subdiv->roadReferences();
|
||||
|
||||
if (!segment.isValid())
|
||||
return true;
|
||||
if (!seek(hdl, segment.offset()))
|
||||
return false;
|
||||
|
||||
if (!net || !nod)
|
||||
return false;
|
||||
if (!(blockIndexIdSize = nod->indexIdSize(nodHdl)))
|
||||
return false;
|
||||
|
||||
while (pos(hdl) < segment.end()) {
|
||||
if (!readVUInt32(hdl, size))
|
||||
return false;
|
||||
|
||||
quint32 entryStart = pos(hdl);
|
||||
|
||||
if (!(readUInt8(hdl, flags) && readVUInt32(hdl, blockIndexIdSize,
|
||||
blockIndexId)))
|
||||
return false;
|
||||
|
||||
quint8 bits[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
bits[i] = 0x4000a08 >> (((flags >> (2*i) & 3) << 3) ^ 0x10);
|
||||
quint8 byteSize = ((bits[0] + bits[1] + bits[2]) + 7) >> 3;
|
||||
|
||||
quint32 counts;
|
||||
if (!readVUInt32(hdl, byteSize, counts))
|
||||
return false;
|
||||
|
||||
quint16 b8 = bits[0] ? (MASK(bits[0]) & counts) + 1 : 0;
|
||||
quint16 b10 = bits[1] ? (MASK(bits[1]) & (counts >> bits[0])) + 1 : 0;
|
||||
quint16 b16 = bits[2] ? (MASK(bits[2]) & (counts >> (bits[0] + bits[1])))
|
||||
+ 1 : 0;
|
||||
|
||||
NODFile::BlockInfo blockInfo;
|
||||
if (!nod->blockInfo(nodHdl, blockIndexId, blockInfo))
|
||||
return false;
|
||||
|
||||
quint8 linkId, lineId;
|
||||
for (int i = 0; i < b8 + b10 + b16; i++) {
|
||||
if (!b8 || b8 <= i) {
|
||||
quint16 v16;
|
||||
if (!readUInt16(hdl, v16))
|
||||
return false;
|
||||
|
||||
if (!b16 || b8 + b16 <= i) {
|
||||
int shift = ((i - (b8 + b16)) * 10) % 8;
|
||||
linkId = (quint8)(v16 >> shift);
|
||||
lineId = (((v16 >> shift) >> 8) & 3) + 1;
|
||||
|
||||
if (shift < 6 && i < b8 + b10 + b16 - 1)
|
||||
seek(hdl, pos(hdl) - 1);
|
||||
} else {
|
||||
linkId = (quint8)v16;
|
||||
lineId = v16 >> 8;
|
||||
Q_ASSERT(lineId > 4);
|
||||
}
|
||||
} else {
|
||||
if (!readUInt8(hdl, linkId))
|
||||
return false;
|
||||
lineId = 0;
|
||||
}
|
||||
|
||||
net->link(subdiv, shift, netHdl, nod, nodHdl, lbl, lblHdl,
|
||||
blockInfo, linkId, lineId, _huffmanTable, lines);
|
||||
}
|
||||
|
||||
Q_ASSERT(entryStart + size == pos(hdl));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QMap<RGNFile::SegmentType, SubDiv::Segment> RGNFile::segments(Handle &hdl,
|
||||
SubDiv *subdiv) const
|
||||
{
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
class LBLFile;
|
||||
class NETFile;
|
||||
class NODFile;
|
||||
|
||||
class RGNFile : public SubFile
|
||||
{
|
||||
@ -23,15 +24,14 @@ public:
|
||||
RGNFile(IMG *img)
|
||||
: SubFile(img), _offset(0), _size(0), _polygonsOffset(0),
|
||||
_polygonsSize(0), _linesOffset(0), _linesSize(0), _pointsOffset(0),
|
||||
_pointsSize(0), _init(false) {clearFlags();}
|
||||
_pointsSize(0), _init(false) {}
|
||||
RGNFile(const QString &path)
|
||||
: SubFile(path), _offset(0), _size(0), _polygonsOffset(0),
|
||||
_polygonsSize(0), _linesOffset(0), _linesSize(0), _pointsOffset(0),
|
||||
_pointsSize(0), _init(false) {clearFlags();}
|
||||
_pointsSize(0), _init(false) {}
|
||||
RGNFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset), _offset(0),
|
||||
_size(0), _polygonsOffset(0), _polygonsSize(0), _linesOffset(0),
|
||||
_linesSize(0), _pointsOffset(0), _pointsSize(0), _init(false)
|
||||
{clearFlags();}
|
||||
_linesSize(0), _pointsOffset(0), _pointsSize(0), _init(false) {}
|
||||
|
||||
bool initialized() const {return _init;}
|
||||
bool init(Handle &hdl);
|
||||
@ -46,29 +46,35 @@ public:
|
||||
QList<IMG::Poly> *polys) const;
|
||||
bool extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
|
||||
Handle &lblHdl, QList<IMG::Point> *points) const;
|
||||
bool links(Handle &hdl, const SubDiv *subdiv, quint32 shift, NETFile *net,
|
||||
Handle &netHdl, NODFile *nod, Handle &nodHdl, LBLFile *lbl, Handle &lblHdl,
|
||||
QList<IMG::Poly> *lines) const;
|
||||
|
||||
bool subdivInit(Handle &hdl, SubDiv *subdiv) const;
|
||||
|
||||
private:
|
||||
QMap<SegmentType, SubDiv::Segment> segments(Handle &hdl, SubDiv *subdiv)
|
||||
const;
|
||||
void clearFlags();
|
||||
bool skipClassFields(Handle &hdl) const;
|
||||
bool skipLclFields(Handle &hdl, const quint32 flags[3], SegmentType type)
|
||||
bool skipLclFields(Handle &hdl, const quint32 flags[3])
|
||||
const;
|
||||
bool skipGblFields(Handle &hdl, quint32 flags) const;
|
||||
|
||||
quint32 _offset;
|
||||
quint32 _size;
|
||||
|
||||
quint32 _polygonsOffset;
|
||||
quint32 _polygonsSize;
|
||||
quint32 _polygonsFlags[3];
|
||||
quint32 _polygonsLclFlags[3];
|
||||
quint32 _polygonsGblFlags;
|
||||
quint32 _linesOffset;
|
||||
quint32 _linesSize;
|
||||
quint32 _linesFlags[3];
|
||||
quint32 _linesLclFlags[3];
|
||||
quint32 _linesGblFlags;
|
||||
quint32 _pointsOffset;
|
||||
quint32 _pointsSize;
|
||||
quint32 _pointsFlags[3];
|
||||
quint32 _pointsLclFlags[3];
|
||||
quint32 _pointsGblFlags;
|
||||
|
||||
HuffmanTable _huffmanTable;
|
||||
|
||||
|
@ -359,7 +359,7 @@ static bool skipLocalization(SubFile *file, SubFile::Handle &hdl)
|
||||
len = len >> 2;
|
||||
}
|
||||
|
||||
if (!file->seek(hdl, hdl.pos() + len))
|
||||
if (!file->seek(hdl, file->pos(hdl) + len))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -21,8 +21,9 @@ public:
|
||||
quint32 _offset, _end;
|
||||
};
|
||||
|
||||
SubDiv(quint32 offset, qint32 lon, qint32 lat, int bits, quint8 objects)
|
||||
: _lon(lon), _lat(lat), _bits(bits), _init(false)
|
||||
SubDiv(quint32 offset, qint32 lon, qint32 lat, quint8 level, quint8 bits,
|
||||
quint8 objects) : _lon(lon), _lat(lat), _level(level), _bits(bits),
|
||||
_init(false)
|
||||
{
|
||||
_tre.objects = objects;
|
||||
_tre.offset = offset;
|
||||
@ -78,6 +79,7 @@ public:
|
||||
qint32 lon() const {return _lon;}
|
||||
qint32 lat() const {return _lat;}
|
||||
quint8 bits() const {return _bits;}
|
||||
quint8 level() const {return _level;}
|
||||
|
||||
// Valid only after initialization
|
||||
Segment points() const
|
||||
@ -94,6 +96,8 @@ public:
|
||||
{return Segment(_rgn.extLinesOffset, _rgn.extLinesEnd);}
|
||||
Segment extPolygons() const
|
||||
{return Segment(_rgn.extPolygonsOffset, _rgn.extPolygonsEnd);}
|
||||
Segment roadReferences() const
|
||||
{return Segment(_rgn.roadReferencesOffset, _rgn.roadReferencesEnd);}
|
||||
|
||||
// Valid only until initialization
|
||||
quint8 objects() const {return _tre.objects;}
|
||||
@ -142,6 +146,7 @@ private:
|
||||
};
|
||||
|
||||
qint32 _lon, _lat;
|
||||
quint8 _level;
|
||||
quint8 _bits;
|
||||
bool _init;
|
||||
union {
|
||||
|
@ -3,26 +3,28 @@
|
||||
#include "subfile.h"
|
||||
|
||||
|
||||
#define mod2n(x, m) ((x) & ((m) - 1));
|
||||
|
||||
bool SubFile::seek(Handle &handle, quint32 pos) const
|
||||
{
|
||||
if (handle._file) {
|
||||
int blockNum = pos / BLOCK_SIZE;
|
||||
int blockNum = pos >> BLOCK_BITS;
|
||||
|
||||
if (handle._blockNum != blockNum) {
|
||||
if (!handle._file->seek((qint64)blockNum * BLOCK_SIZE))
|
||||
if (!handle._file->seek((quint64)blockNum << BLOCK_BITS))
|
||||
return false;
|
||||
if (handle._file->read(handle._data.data(), BLOCK_SIZE) < 0)
|
||||
if (handle._file->read(handle._data.data(), (1<<BLOCK_BITS)) < 0)
|
||||
return false;
|
||||
handle._blockNum = blockNum;
|
||||
}
|
||||
|
||||
handle._blockPos = pos % BLOCK_SIZE;
|
||||
handle._blockPos = mod2n(pos, 1U<<BLOCK_BITS);
|
||||
handle._pos = pos;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
quint32 blockSize = _img->blockSize();
|
||||
int blockNum = pos / blockSize;
|
||||
quint32 blockBits = _img->blockBits();
|
||||
int blockNum = pos >> blockBits;
|
||||
|
||||
if (handle._blockNum != blockNum) {
|
||||
if (blockNum >= _blocks->size())
|
||||
@ -32,7 +34,7 @@ bool SubFile::seek(Handle &handle, quint32 pos) const
|
||||
handle._blockNum = blockNum;
|
||||
}
|
||||
|
||||
handle._blockPos = pos % blockSize;
|
||||
handle._blockPos = mod2n(pos, 1U<<blockBits);
|
||||
handle._pos = pos;
|
||||
|
||||
return true;
|
||||
@ -70,6 +72,22 @@ bool SubFile::readVUInt32(Handle &hdl, quint32 &val) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SubFile::readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const
|
||||
{
|
||||
switch (bytes) {
|
||||
case 1:
|
||||
return readUInt8(hdl, val);
|
||||
case 2:
|
||||
return readUInt16(hdl, val);
|
||||
case 3:
|
||||
return readUInt24(hdl, val);
|
||||
case 4:
|
||||
return readUInt32(hdl, val);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SubFile::readVBitfield32(Handle &hdl, quint32 &bitfield) const
|
||||
{
|
||||
quint8 bits;
|
||||
|
@ -6,12 +6,12 @@
|
||||
#include "img.h"
|
||||
|
||||
|
||||
#define BLOCK_SIZE 4096
|
||||
#define BLOCK_BITS 12 /* 4096 bytes */
|
||||
|
||||
class SubFile
|
||||
{
|
||||
public:
|
||||
enum Type {Unknown, TRE, RGN, LBL, NET, TYP, GMP};
|
||||
enum Type {Unknown, TRE, RGN, LBL, NET, NOD, TYP, GMP};
|
||||
|
||||
class Handle
|
||||
{
|
||||
@ -22,14 +22,12 @@ public:
|
||||
if (subFile && subFile->_path) {
|
||||
_file = new QFile(*(subFile->_path));
|
||||
_file->open(QIODevice::ReadOnly);
|
||||
_data.resize(BLOCK_SIZE);
|
||||
_data.resize(1U<<BLOCK_BITS);
|
||||
} else if (subFile)
|
||||
_data.resize(subFile->_img->blockSize());
|
||||
_data.resize(1U<<subFile->_img->blockBits());
|
||||
}
|
||||
~Handle() {delete _file;}
|
||||
|
||||
int pos() const {return _pos;}
|
||||
|
||||
private:
|
||||
friend class SubFile;
|
||||
|
||||
@ -57,6 +55,7 @@ public:
|
||||
void addBlock(quint16 block) {_blocks->append(block);}
|
||||
|
||||
bool seek(Handle &handle, quint32 pos) const;
|
||||
quint32 pos(Handle &handle) const {return handle._pos;}
|
||||
|
||||
template<typename T>
|
||||
bool readUInt8(Handle &handle, T &val) const
|
||||
@ -132,6 +131,7 @@ public:
|
||||
}
|
||||
|
||||
bool readVUInt32(Handle &hdl, quint32 &val) const;
|
||||
bool readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const;
|
||||
bool readVBitfield32(Handle &hdl, quint32 &bitfield) const;
|
||||
|
||||
QString fileName() const {return _path ? *_path : _img->fileName();}
|
||||
@ -142,7 +142,7 @@ protected:
|
||||
private:
|
||||
bool readByte(Handle &handle, quint8 &val) const
|
||||
{
|
||||
int blockSize = _img ? _img->blockSize() : BLOCK_SIZE;
|
||||
int blockSize = _img ? 1U<<_img->blockBits() : 1U<<BLOCK_BITS;
|
||||
val = handle._data.at(handle._blockPos++);
|
||||
handle._pos++;
|
||||
return (handle._blockPos >= blockSize)
|
||||
|
@ -70,14 +70,16 @@ bool TREFile::init()
|
||||
return false;
|
||||
|
||||
if (hdrLen > 0x9A) {
|
||||
// TRE7 info
|
||||
// TRE7 info + flags
|
||||
if (!(seek(hdl, _gmpOffset + 0x7C) && readUInt32(hdl, _extended.offset)
|
||||
&& readUInt32(hdl, _extended.size)
|
||||
&& readUInt16(hdl, _extended.itemSize)))
|
||||
return false;
|
||||
// flags
|
||||
if (!(seek(hdl, _gmpOffset + 0x86) && readUInt32(hdl, _flags)))
|
||||
&& readUInt16(hdl, _extended.itemSize) && readUInt32(hdl, _flags)))
|
||||
return false;
|
||||
} else {
|
||||
_extended.offset = 0;
|
||||
_extended.size = 0;
|
||||
_extended.itemSize = 0;
|
||||
_flags = 0;
|
||||
}
|
||||
|
||||
// Tile levels
|
||||
@ -120,15 +122,43 @@ bool TREFile::init()
|
||||
return (_firstLevel >= 0);
|
||||
}
|
||||
|
||||
int TREFile::readExtEntry(Handle &hdl, quint32 &polygons, quint32 &lines,
|
||||
quint32 &points)
|
||||
{
|
||||
int rb = 0;
|
||||
|
||||
if (_flags & 1) {
|
||||
if (!readUInt32(hdl, polygons))
|
||||
return -1;
|
||||
rb += 4;
|
||||
} else
|
||||
polygons = 0;
|
||||
if (_flags & 2) {
|
||||
if (!readUInt32(hdl, lines))
|
||||
return -1;
|
||||
rb += 4;
|
||||
} else
|
||||
lines = 0;
|
||||
if (_flags & 4) {
|
||||
if (!readUInt32(hdl, points))
|
||||
return -1;
|
||||
rb += 4;
|
||||
} else
|
||||
points = 0;
|
||||
|
||||
return rb;
|
||||
}
|
||||
|
||||
bool TREFile::load(int idx)
|
||||
{
|
||||
Handle hdl(this);
|
||||
QList<SubDiv*> sl;
|
||||
SubDiv *s = 0;
|
||||
SubDivTree *tree = new SubDivTree();
|
||||
const MapLevel &level = _levels.at(idx);
|
||||
|
||||
|
||||
_subdivs.insert(_levels.at(idx).bits, tree);
|
||||
_subdivs.insert(level.bits, tree);
|
||||
|
||||
quint32 skip = 0;
|
||||
for (int i = 0; i < idx; i++)
|
||||
@ -137,7 +167,7 @@ bool TREFile::load(int idx)
|
||||
if (!seek(hdl, _subdivOffset + skip * 16))
|
||||
return false;
|
||||
|
||||
for (int j = 0; j < _levels.at(idx).subdivs; j++) {
|
||||
for (int j = 0; j < level.subdivs; j++) {
|
||||
quint32 oo;
|
||||
qint32 lon, lat, width, height;
|
||||
quint16 nextLevel;
|
||||
@ -156,10 +186,10 @@ bool TREFile::load(int idx)
|
||||
s->setEnd(offset);
|
||||
|
||||
width &= 0x7FFF;
|
||||
width = LS(width, 24 - _levels.at(idx).bits);
|
||||
height = LS(height, 24 - _levels.at(idx).bits);
|
||||
width = LS(width, 24 - level.bits);
|
||||
height = LS(height, 24 - level.bits);
|
||||
|
||||
s = new SubDiv(offset, lon, lat, _levels.at(idx).bits, objects);
|
||||
s = new SubDiv(offset, lon, lat, level.level, level.bits, objects);
|
||||
sl.append(s);
|
||||
|
||||
double min[2], max[2];
|
||||
@ -184,36 +214,31 @@ bool TREFile::load(int idx)
|
||||
|
||||
|
||||
// Objects with extended types (TRE7)
|
||||
if (_extended.size && _extended.itemSize >= 12) {
|
||||
/* Some maps skip entries for the inherited levels, some don't. Our
|
||||
decision is based on the difference between the extended subdivs
|
||||
count and the total subdivs count. */
|
||||
if (_extended.size && _extended.itemSize) {
|
||||
quint32 totalSubdivs = 0;
|
||||
for (int i = 0; i < _levels.size(); i++)
|
||||
totalSubdivs += _levels.at(i).subdivs;
|
||||
quint32 extendedSubdivs = _extended.size / _extended.itemSize;
|
||||
quint32 diff = totalSubdivs - extendedSubdivs + 1;
|
||||
|
||||
quint32 polygons, lines, points;
|
||||
if (!seek(hdl, _extended.offset + (skip - diff) * _extended.itemSize))
|
||||
goto error;
|
||||
|
||||
quint32 polygons, lines, points;
|
||||
int rb;
|
||||
for (int i = 0; i < sl.size(); i++) {
|
||||
if (!(readUInt32(hdl, polygons) && readUInt32(hdl, lines)
|
||||
&& readUInt32(hdl, points)))
|
||||
if ((rb = readExtEntry(hdl, polygons, lines, points)) < 0)
|
||||
goto error;
|
||||
|
||||
sl.at(i)->setExtOffsets(polygons, lines, points);
|
||||
if (i)
|
||||
sl.at(i-1)->setExtEnds(polygons, lines, points);
|
||||
|
||||
if (!seek(hdl, hdl.pos() + _extended.itemSize - 12))
|
||||
if (!seek(hdl, pos(hdl) + _extended.itemSize - rb))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (idx != _levels.size() - 1) {
|
||||
if (!(readUInt32(hdl, polygons) && readUInt32(hdl, lines)
|
||||
&& readUInt32(hdl, points)))
|
||||
if (readExtEntry(hdl, polygons, lines, points) < 0)
|
||||
goto error;
|
||||
sl.last()->setExtEnds(polygons, lines, points);
|
||||
}
|
||||
|
@ -46,9 +46,8 @@ private:
|
||||
|
||||
bool load(int idx);
|
||||
int level(int bits, bool baseMap);
|
||||
bool parsePoly(Handle hdl, quint32 pos, const QMap<int, int> &level2bits,
|
||||
QMap<quint32, int> &map);
|
||||
bool parsePoints(Handle hdl, quint32 pos, const QMap<int, int> &level2bits);
|
||||
int readExtEntry(Handle &hdl, quint32 &polygons, quint32 &lines,
|
||||
quint32 &points);
|
||||
|
||||
friend QDebug operator<<(QDebug dbg, const MapLevel &level);
|
||||
|
||||
|
@ -29,6 +29,8 @@ SubFile *VectorTile::file(SubFile::Type type)
|
||||
return _lbl;
|
||||
case SubFile::NET:
|
||||
return _net;
|
||||
case SubFile::NOD:
|
||||
return _nod;
|
||||
case SubFile::GMP:
|
||||
return _gmp;
|
||||
default:
|
||||
@ -51,6 +53,9 @@ SubFile *VectorTile::addFile(IMG *img, SubFile::Type type)
|
||||
case SubFile::NET:
|
||||
_net = new NETFile(img);
|
||||
return _net;
|
||||
case SubFile::NOD:
|
||||
_nod = new NODFile(img);
|
||||
return _nod;
|
||||
case SubFile::GMP:
|
||||
_gmp = new SubFile(img);
|
||||
return _gmp;
|
||||
@ -74,6 +79,9 @@ SubFile *VectorTile::addFile(const QString &path, SubFile::Type type)
|
||||
case SubFile::NET:
|
||||
_net = new NETFile(path);
|
||||
return _net;
|
||||
case SubFile::NOD:
|
||||
_nod = new NODFile(path);
|
||||
return _nod;
|
||||
case SubFile::GMP:
|
||||
_gmp = new SubFile(path);
|
||||
return _gmp;
|
||||
@ -96,17 +104,18 @@ bool VectorTile::init()
|
||||
bool VectorTile::initGMP()
|
||||
{
|
||||
SubFile::Handle hdl(_gmp);
|
||||
quint32 tre, rgn, lbl, net;
|
||||
quint32 tre, rgn, lbl, net, nod;
|
||||
|
||||
if (!(_gmp->seek(hdl, 0x19) && _gmp->readUInt32(hdl, tre)
|
||||
&& _gmp->readUInt32(hdl, rgn) && _gmp->readUInt32(hdl, lbl)
|
||||
&& _gmp->readUInt32(hdl, net)))
|
||||
&& _gmp->readUInt32(hdl, net) && _gmp->readUInt32(hdl, nod)))
|
||||
return false;
|
||||
|
||||
_tre = tre ? new TREFile(_gmp, tre) : 0;
|
||||
_rgn = rgn ? new RGNFile(_gmp, rgn) : 0;
|
||||
_lbl = lbl ? new LBLFile(_gmp, lbl) : 0;
|
||||
_net = net ? new NETFile(_gmp, net) : 0;
|
||||
_nod = nod ? new NODFile(_gmp, nod) : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -115,7 +124,7 @@ void VectorTile::polys(const RectC &rect, int bits, bool baseMap,
|
||||
QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
|
||||
QCache<const SubDiv *, IMG::Polys> *polyCache) const
|
||||
{
|
||||
SubFile::Handle rgnHdl(_rgn), lblHdl(_lbl), netHdl(_net);
|
||||
SubFile::Handle rgnHdl(_rgn), lblHdl(_lbl), netHdl(_net), nodHdl(_nod);
|
||||
|
||||
if (!_rgn->initialized() && !_rgn->init(rgnHdl))
|
||||
return;
|
||||
@ -140,6 +149,8 @@ void VectorTile::polys(const RectC &rect, int bits, bool baseMap,
|
||||
lblHdl, &p);
|
||||
_rgn->extPolyObjects(rgnHdl, subdiv, shift, RGNFile::Line, _lbl,
|
||||
lblHdl, &l);
|
||||
_rgn->links(rgnHdl, subdiv, shift, _net, netHdl, _nod, nodHdl, _lbl,
|
||||
lblHdl, &l);
|
||||
|
||||
copyPolys(rect, &p, polygons);
|
||||
copyPolys(rect, &l, lines);
|
||||
|
@ -6,13 +6,15 @@
|
||||
#include "rgnfile.h"
|
||||
#include "lblfile.h"
|
||||
#include "netfile.h"
|
||||
#include "nodfile.h"
|
||||
|
||||
class VectorTile {
|
||||
public:
|
||||
VectorTile() : _tre(0), _rgn(0), _lbl(0), _net(0), _gmp(0) {}
|
||||
VectorTile() : _tre(0), _rgn(0), _lbl(0), _net(0), _nod(0), _gmp(0) {}
|
||||
~VectorTile()
|
||||
{
|
||||
delete _tre; delete _rgn; delete _lbl; delete _net; delete _gmp;
|
||||
delete _tre; delete _rgn; delete _lbl; delete _net; delete _nod;
|
||||
delete _gmp;
|
||||
}
|
||||
|
||||
bool init();
|
||||
@ -37,7 +39,7 @@ public:
|
||||
{
|
||||
return (type == SubFile::TRE || type == SubFile::LBL
|
||||
|| type == SubFile::RGN || type == SubFile::NET
|
||||
|| type == SubFile::GMP);
|
||||
|| type == SubFile::NOD || type == SubFile::GMP);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -47,6 +49,7 @@ private:
|
||||
RGNFile *_rgn;
|
||||
LBLFile *_lbl;
|
||||
NETFile *_net;
|
||||
NODFile *_nod;
|
||||
SubFile *_gmp;
|
||||
};
|
||||
|
||||
|
@ -167,11 +167,6 @@ void IMGMap::ll2xy(QList<MapData::Point> &points)
|
||||
}
|
||||
}
|
||||
|
||||
static void render(RasterTile &tile)
|
||||
{
|
||||
tile.render();
|
||||
}
|
||||
|
||||
void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
{
|
||||
Q_UNUSED(flags);
|
||||
@ -224,7 +219,7 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
}
|
||||
}
|
||||
|
||||
QFuture<void> future = QtConcurrent::map(tiles, render);
|
||||
QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
|
||||
future.waitForFinished();
|
||||
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
|
@ -49,11 +49,6 @@ private:
|
||||
|
||||
#define META_TYPE(type) static_cast<QMetaType::Type>(type)
|
||||
|
||||
static void render(MBTile &tile)
|
||||
{
|
||||
tile.load();
|
||||
}
|
||||
|
||||
static double index2mercator(int index, int zoom)
|
||||
{
|
||||
return rad2deg(-M_PI + 2 * M_PI * ((double)index / (1<<zoom)));
|
||||
@ -314,7 +309,7 @@ void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
}
|
||||
}
|
||||
|
||||
QFuture<void> future = QtConcurrent::map(tiles, render);
|
||||
QFuture<void> future = QtConcurrent::map(tiles, &MBTile::load);
|
||||
future.waitForFinished();
|
||||
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
|
@ -41,11 +41,6 @@ private:
|
||||
QImage _image;
|
||||
};
|
||||
|
||||
static void render(TileImage &ti)
|
||||
{
|
||||
ti.load();
|
||||
}
|
||||
|
||||
static QString quadKey(const QPoint &xy, int zoom)
|
||||
{
|
||||
QString qk;
|
||||
@ -101,7 +96,7 @@ void TileLoader::loadTilesAsync(QVector<Tile> &list)
|
||||
if (!dl.empty())
|
||||
_downloader->get(dl, _authorization);
|
||||
|
||||
QFuture<void> future = QtConcurrent::map(imgs, render);
|
||||
QFuture<void> future = QtConcurrent::map(imgs, &TileImage::load);
|
||||
future.waitForFinished();
|
||||
|
||||
for (int i = 0; i < imgs.size(); i++) {
|
||||
@ -153,7 +148,7 @@ void TileLoader::loadTilesSync(QVector<Tile> &list)
|
||||
}
|
||||
}
|
||||
|
||||
QFuture<void> future = QtConcurrent::map(imgs, render);
|
||||
QFuture<void> future = QtConcurrent::map(imgs, &TileImage::load);
|
||||
future.waitForFinished();
|
||||
|
||||
for (int i = 0; i < imgs.size(); i++)
|
||||
|