diff --git a/src/GUI/gui.cpp b/src/GUI/gui.cpp index ac7c695e..a7591a1f 100644 --- a/src/GUI/gui.cpp +++ b/src/GUI/gui.cpp @@ -973,7 +973,8 @@ void GUI::openOptions() SET_VIEW_OPTION(pathAntiAliasing, useAntiAliasing); SET_VIEW_OPTION(useOpenGL, useOpenGL); SET_VIEW_OPTION(sliderColor, setMarkerColor); - SET_VIEW_OPTION(projection, setProjection); + SET_VIEW_OPTION(outputProjection, setOutputProjection); + SET_VIEW_OPTION(inputProjection, setInputProjection); SET_TAB_OPTION(palette, setPalette); SET_TAB_OPTION(graphWidth, setGraphWidth); @@ -2149,8 +2150,10 @@ void GUI::writeSettings() _options.separateGraphPage); if (_options.sliderColor != SLIDER_COLOR_DEFAULT) settings.setValue(SLIDER_COLOR_SETTING, _options.sliderColor); - if (_options.projection != PROJECTION_DEFAULT) - settings.setValue(PROJECTION_SETTING, _options.projection); + if (_options.outputProjection != OUTPUT_PROJECTION_DEFAULT) + settings.setValue(OUTPUT_PROJECTION_SETTING, _options.outputProjection); + if (_options.inputProjection != INPUT_PROJECTION_DEFAULT) + settings.setValue(INPUT_PROJECTION_SETTING, _options.outputProjection); if (_options.hidpiMap != HIDPI_MAP_DEFAULT) settings.setValue(HIDPI_MAP_SETTING, _options.hidpiMap); settings.endGroup(); @@ -2442,8 +2445,10 @@ void GUI::readSettings() SEPARATE_GRAPH_PAGE_DEFAULT).toBool(); _options.sliderColor = settings.value(SLIDER_COLOR_SETTING, SLIDER_COLOR_DEFAULT).value(); - _options.projection = settings.value(PROJECTION_SETTING, PROJECTION_DEFAULT) - .toInt(); + _options.outputProjection = settings.value(OUTPUT_PROJECTION_SETTING, + OUTPUT_PROJECTION_DEFAULT).toInt(); + _options.inputProjection = settings.value(INPUT_PROJECTION_SETTING, + INPUT_PROJECTION_DEFAULT).toInt(); _options.hidpiMap = settings.value(HIDPI_MAP_SETTING, HIDPI_MAP_SETTING) .toBool(); @@ -2467,7 +2472,8 @@ void GUI::readSettings() _mapView->useOpenGL(true); _mapView->setDevicePixelRatio(devicePixelRatioF(), _options.hidpiMap ? devicePixelRatioF() : 1.0); - _mapView->setProjection(_options.projection); + _mapView->setOutputProjection(_options.outputProjection); + _mapView->setInputProjection(_options.inputProjection); _mapView->setTimeZone(_options.timeZone.zone()); for (int i = 0; i < _tabs.count(); i++) { diff --git a/src/GUI/mapview.cpp b/src/GUI/mapview.cpp index de8eeea7..cf9df962 100644 --- a/src/GUI/mapview.cpp +++ b/src/GUI/mapview.cpp @@ -67,10 +67,12 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent) _coordinates->setVisible(false); _scene->addItem(_coordinates); - _projection = PCS::pcs(3857); + _outputProjection = PCS::pcs(3857); + _inputProjection = GCS::gcs(4326); _map = map; _map->load(); - _map->setProjection(_projection); + _map->setOutputProjection(_outputProjection); + _map->setInputProjection(_inputProjection); connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap())); _poi = poi; @@ -369,7 +371,8 @@ void MapView::setMap(Map *map) _map = map; _map->load(); - _map->setProjection(_projection); + _map->setOutputProjection(_outputProjection); + _map->setInputProjection(_inputProjection); _map->setDevicePixelRatio(_deviceRatio, _mapRatio); connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap())); @@ -1072,20 +1075,38 @@ void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) reloadMap(); } -void MapView::setProjection(int id) +void MapView::setOutputProjection(int id) { const PCS *pcs; const GCS *gcs; Coordinates center = _map->xy2ll(mapToScene(viewport()->rect().center())); if ((pcs = PCS::pcs(id))) - _projection = Projection(pcs); + _outputProjection = Projection(pcs); else if ((gcs = GCS::gcs(id))) - _projection = Projection(gcs); + _outputProjection = Projection(gcs); else qWarning("%d: Unknown PCS/GCS id", id); - _map->setProjection(_projection); + _map->setOutputProjection(_outputProjection); + rescale(); + centerOn(_map->ll2xy(center)); +} + +void MapView::setInputProjection(int id) +{ + const PCS *pcs; + const GCS *gcs; + Coordinates center = _map->xy2ll(mapToScene(viewport()->rect().center())); + + if ((pcs = PCS::pcs(id))) + _inputProjection = Projection(pcs); + else if ((gcs = GCS::gcs(id))) + _inputProjection = Projection(gcs); + else + qWarning("%d: Unknown PCS/GCS id", id); + + _map->setInputProjection(_inputProjection); rescale(); centerOn(_map->ll2xy(center)); } diff --git a/src/GUI/mapview.h b/src/GUI/mapview.h index 4b8b329c..98b56b90 100644 --- a/src/GUI/mapview.h +++ b/src/GUI/mapview.h @@ -98,7 +98,8 @@ public slots: void setCoordinatesFormat(CoordinatesFormat format); void setTimeZone(const QTimeZone &zone); void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); - void setProjection(int id); + void setOutputProjection(int id); + void setInputProjection(int id); void fitContentToSize(); @@ -155,7 +156,7 @@ private: Palette _palette; qreal _mapOpacity; - Projection _projection; + Projection _outputProjection, _inputProjection; bool _showMap, _showTracks, _showRoutes, _showAreas, _showWaypoints, _showWaypointLabels, _showPOI, _showPOILabels, _showRouteWaypoints, diff --git a/src/GUI/optionsdialog.cpp b/src/GUI/optionsdialog.cpp index f9ad625f..3d22738a 100644 --- a/src/GUI/optionsdialog.cpp +++ b/src/GUI/optionsdialog.cpp @@ -46,17 +46,52 @@ void OptionsDialog::automaticPauseDetectionSet(bool set) QWidget *OptionsDialog::createMapPage() { - _projection = new LimitedComboBox(200); + int last = -1; + _outputProjection = new LimitedComboBox(200); QList > projections(GCS::list() + PCS::list()); std::sort(projections.begin(), projections.end()); - for (int i = 0; i < projections.size(); i++) { - QString text = QString::number(projections.at(i).key()) + " - " - + projections.at(i).value(); - _projection->addItem(text, QVariant(projections.at(i).key())); + const KV &proj = projections.at(i); + // There may be same EPSG codes with different names + if (proj.key() == last) + continue; + else + last = proj.key(); + QString text = QString::number(proj.key()) + " - " + proj.value(); + _outputProjection->addItem(text, QVariant(proj.key())); } - _projection->setCurrentIndex(_projection->findData(_options.projection)); + _outputProjection->setCurrentIndex(_outputProjection->findData( + _options.outputProjection)); + + _inputProjection = new LimitedComboBox(200); + last = -1; + for (int i = 0; i < projections.size(); i++) { + const KV &proj = projections.at(i); + // There may be same EPSG codes with different names + if (proj.key() == last) + continue; + else + last = proj.key(); + if (proj.key() == 4326 || proj.key() == 3857) { + QString text = QString::number(proj.key()) + " - " + proj.value(); + _inputProjection->addItem(text, QVariant(proj.key())); + } + } + _inputProjection->setCurrentIndex(_inputProjection->findData( + _options.inputProjection)); + + QLabel *inInfo = new QLabel(tr("Select the propper projection of" + " JNX and KMZ maps. Both EPSG:3857 and EPSG:4326 projected maps" + " exist and there is no projection info in the map file.")); + QLabel *outInfo = new QLabel(tr("Select the desired projection of IMG" + " maps. The projection must be valid for the whole map area.")); + QFont f = inInfo->font(); + f.setPointSize(f.pointSize() - 1); + inInfo->setWordWrap(true); + outInfo->setWordWrap(true); + inInfo->setFont(f); + outInfo->setFont(f); _hidpi = new QRadioButton(tr("High-resolution")); _lodpi = new QRadioButton(tr("Standard")); @@ -68,21 +103,24 @@ QWidget *OptionsDialog::createMapPage() "The map is sharp but map objects are small/hard to read.")); QLabel *llo = new QLabel(tr("Non-HiDPI maps are loaded such as they are. " "Map objects have the expected size but the map is blurry.")); - QFont f = lhi->font(); f.setPointSize(f.pointSize() - 1); lhi->setWordWrap(true); llo->setWordWrap(true); lhi->setFont(f); llo->setFont(f); - QFormLayout *vectorLayout = new QFormLayout(); - vectorLayout->addRow(tr("Projection:"), _projection); - QWidget *vectorMapsTab = new QWidget(); - QVBoxLayout *vectorMapsTabLayout = new QVBoxLayout(); - vectorMapsTabLayout->addLayout(vectorLayout); - vectorMapsTabLayout->addStretch(); - vectorMapsTab->setLayout(vectorMapsTabLayout); + QFormLayout *projLayout = new QFormLayout(); + projLayout->addRow(tr("Input:"), _inputProjection); + projLayout->addWidget(inInfo); + projLayout->addRow(tr("Output:"), _outputProjection); + projLayout->addWidget(outInfo); + + QWidget *projectionTab = new QWidget(); + QVBoxLayout *projectionTabLayout = new QVBoxLayout(); + projectionTabLayout->addLayout(projLayout); + projectionTabLayout->addStretch(); + projectionTab->setLayout(projectionTabLayout); QVBoxLayout *hidpiTabLayout = new QVBoxLayout(); hidpiTabLayout->addWidget(_lodpi); @@ -96,7 +134,7 @@ QWidget *OptionsDialog::createMapPage() hidpiTab->setLayout(hidpiTabLayout); QTabWidget *mapPage = new QTabWidget(); - mapPage->addTab(vectorMapsTab, tr("Vector maps")); + mapPage->addTab(projectionTab, tr("Projection")); mapPage->addTab(hidpiTab, tr("HiDPI display mode")); return mapPage; @@ -742,8 +780,10 @@ void OptionsDialog::accept() _options.sliderColor = _sliderColor->color(); _options.graphAntiAliasing = _graphAA->isChecked(); - _options.projection = _projection->itemData(_projection->currentIndex()) - .toInt(); + _options.outputProjection = _outputProjection->itemData( + _outputProjection->currentIndex()).toInt(); + _options.inputProjection = _inputProjection->itemData( + _inputProjection->currentIndex()).toInt(); _options.hidpiMap = _hidpi->isChecked(); _options.elevationFilter = _elevationFilter->value(); diff --git a/src/GUI/optionsdialog.h b/src/GUI/optionsdialog.h index 4a738825..11c5c5b3 100644 --- a/src/GUI/optionsdialog.h +++ b/src/GUI/optionsdialog.h @@ -39,7 +39,8 @@ struct Options { int mapOpacity; QColor backgroundColor; // Map - int projection; + int outputProjection; + int inputProjection; bool hidpiMap; // Data int elevationFilter; @@ -120,7 +121,8 @@ private: ColorBox *_sliderColor; QCheckBox *_graphAA; // Map - LimitedComboBox *_projection; + LimitedComboBox *_outputProjection; + LimitedComboBox *_inputProjection; QRadioButton *_hidpi; QRadioButton *_lodpi; // Data diff --git a/src/GUI/settings.h b/src/GUI/settings.h index bde9eac7..6ed2eb31 100644 --- a/src/GUI/settings.h +++ b/src/GUI/settings.h @@ -200,8 +200,10 @@ #define SEPARATE_GRAPH_PAGE_DEFAULT false #define SLIDER_COLOR_SETTING "sliderColor" #define SLIDER_COLOR_DEFAULT QColor(Qt::red) -#define PROJECTION_SETTING "projection" -#define PROJECTION_DEFAULT 3857 +#define OUTPUT_PROJECTION_SETTING "outputProjection" +#define OUTPUT_PROJECTION_DEFAULT 3857 +#define INPUT_PROJECTION_SETTING "inputProjection" +#define INPUT_PROJECTION_DEFAULT 4326 #define HIDPI_MAP_SETTING "HiDPIMap" #define HIDPI_MAP_DEFAULT true diff --git a/src/map/imgmap.cpp b/src/map/imgmap.cpp index 890fea31..efc1b0b2 100644 --- a/src/map/imgmap.cpp +++ b/src/map/imgmap.cpp @@ -202,7 +202,7 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags) } } -void IMGMap::setProjection(const Projection &projection) +void IMGMap::setOutputProjection(const Projection &projection) { if (projection == _projection) return; diff --git a/src/map/imgmap.h b/src/map/imgmap.h index 29a70e3f..24194847 100644 --- a/src/map/imgmap.h +++ b/src/map/imgmap.h @@ -33,7 +33,7 @@ public: void draw(QPainter *painter, const QRectF &rect, Flags flags); - void setProjection(const Projection &projection); + void setOutputProjection(const Projection &projection); void load(); void unload(); diff --git a/src/map/jnxmap.cpp b/src/map/jnxmap.cpp index 2c9c5ea0..8d425855 100644 --- a/src/map/jnxmap.cpp +++ b/src/map/jnxmap.cpp @@ -84,15 +84,6 @@ bool JNXMap::readTiles() } } - QByteArray guid; - if (!(readValue(dummy) && readString(guid))) - return false; - /* Use WebMercator projection for nakarte.tk maps */ - if (guid == "12345678-1234-1234-1234-123456789ABC") - _projection = Projection(PCS::pcs(3857)); - else - _projection = Projection(GCS::gcs(4326)); - _zooms = QVector(lh.size()); for (int i = 0; i < lh.count(); i++) { Zoom &z = _zooms[i]; @@ -104,21 +95,22 @@ bool JNXMap::readTiles() z.tiles = QVector(l.count); for (quint32 j = 0; j < l.count; j++) { Tile &tile = z.tiles[j]; - qint32 top, right, bottom, left; - quint16 width, height; - if (!(readValue(top) && readValue(right) && readValue(bottom) - && readValue(left) && readValue(width) - && readValue(height) && readValue(tile.size) - && readValue(tile.offset))) + if (!(readValue(tile.top) && readValue(tile.right) + && readValue(tile.bottom) && readValue(tile.left) + && readValue(tile.width) && readValue(tile.height) + && readValue(tile.size) && readValue(tile.offset))) return false; - RectD rect(_projection.ll2xy(Coordinates(ic2dc(left), ic2dc(top))), - _projection.ll2xy(Coordinates(ic2dc(right), ic2dc(bottom)))); + RectC llrect(Coordinates(ic2dc(tile.left), ic2dc(tile.top)), + Coordinates(ic2dc(tile.right), ic2dc(tile.bottom))); + RectD rect(_projection.ll2xy(llrect.topLeft()), + _projection.ll2xy(llrect.bottomRight())); if (j == 0) { ReferencePoint tl(PointD(0, 0), rect.topLeft()); - ReferencePoint br(PointD(width, height), rect.bottomRight()); + ReferencePoint br(PointD(tile.width, tile.height), + rect.bottomRight()); z.transform = Transform(tl, br); } @@ -143,6 +135,7 @@ JNXMap::JNXMap(const QString &fileName, QObject *parent) _valid(false) { _name = QFileInfo(fileName).fileName(); + _projection = Projection(GCS::gcs(4326)); if (!_file.open(QIODevice::ReadOnly)) { _errorString = fileName + ": " + _file.errorString(); @@ -267,3 +260,44 @@ void JNXMap::draw(QPainter *painter, const QRectF &rect, Flags flags) max[1] = rr.bottom(); tree.Search(min, max, cb, &ctx); } + +void JNXMap::setInputProjection(const Projection &projection) +{ + if (projection == _projection) + return; + + _projection = projection; + + for (int i = 0; i < _zooms.size(); i++) { + Zoom &z = _zooms[i]; + + z.tree.RemoveAll(); + + for (int j = 0; j < z.tiles.size(); j++) { + Tile &tile = z.tiles[j]; + + RectC llrect(Coordinates(ic2dc(tile.left), ic2dc(tile.top)), + Coordinates(ic2dc(tile.right), ic2dc(tile.bottom))); + RectD rect(_projection.ll2xy(llrect.topLeft()), + _projection.ll2xy(llrect.bottomRight())); + + if (j == 0) { + ReferencePoint tl(PointD(0, 0), rect.topLeft()); + ReferencePoint br(PointD(tile.width, tile.height), + rect.bottomRight()); + z.transform = Transform(tl, br); + } + + QRectF trect(z.transform.proj2img(rect.topLeft()), + z.transform.proj2img(rect.bottomRight())); + tile.pos = trect.topLeft(); + + qreal min[2], max[2]; + min[0] = trect.left(); + min[1] = trect.top(); + max[0] = trect.right(); + max[1] = trect.bottom(); + z.tree.Insert(min, max, &tile); + } + } +} diff --git a/src/map/jnxmap.h b/src/map/jnxmap.h index e0d2a851..5b5e5fe7 100644 --- a/src/map/jnxmap.h +++ b/src/map/jnxmap.h @@ -36,6 +36,7 @@ public: void draw(QPainter *painter, const QRectF &rect, Flags flags); + void setInputProjection(const Projection &projection); void setDevicePixelRatio(qreal /*deviceRatio*/, qreal mapRatio) {_mapRatio = mapRatio;} @@ -44,9 +45,12 @@ public: private: struct Tile { - QPointF pos; + qint32 top, right, bottom, left; + quint16 width, height; quint32 size; quint32 offset; + + QPointF pos; }; struct Zoom { diff --git a/src/map/kmzmap.cpp b/src/map/kmzmap.cpp index 468211a5..60f65272 100644 --- a/src/map/kmzmap.cpp +++ b/src/map/kmzmap.cpp @@ -28,13 +28,15 @@ #define TL(m) ((m).bbox().topLeft()) #define BR(m) ((m).bbox().bottomRight()) + KMZMap::Overlay::Overlay(const QString &path, const QSize &size, - const RectC &bbox, double rotation) : _path(path), _bbox(bbox), - _rotation(rotation), _img(0) + const RectC &bbox, double rotation, const Projection *proj) + : _path(path), _size(size), _bbox(bbox), _rotation(rotation), _img(0), + _proj(proj) { - ReferencePoint tl(PointD(0, 0), PointD(bbox.left(), bbox.top())); + ReferencePoint tl(PointD(0, 0), _proj->ll2xy(bbox.topLeft())); ReferencePoint br(PointD(size.width(), size.height()), - PointD(bbox.right(), bbox.bottom())); + _proj->ll2xy(bbox.bottomRight())); QTransform t; t.rotate(-rotation); @@ -87,6 +89,24 @@ void KMZMap::Overlay::unload() _img = 0; } +void KMZMap::Overlay::setProjection(const Projection *proj) +{ + _proj = proj; + + ReferencePoint tl(PointD(0, 0), _proj->ll2xy(_bbox.topLeft())); + ReferencePoint br(PointD(_size.width(), _size.height()), + _proj->ll2xy(_bbox.bottomRight())); + + QTransform t; + t.rotate(-_rotation); + QRectF b(0, 0, _size.width(), _size.height()); + QPolygonF ma = t.map(b); + _bounds = ma.boundingRect(); + + _transform = Transform(tl, br); +} + + bool KMZMap::resCmp(const Overlay &m1, const Overlay &m2) { qreal r1, r2; @@ -150,7 +170,6 @@ void KMZMap::computeBounds() _bounds = QVector(_maps.count()); for (int i = 0; i < _maps.count(); i++) { QRectF xy(offsets.at(i), _maps.at(i).bounds().size()); - _bounds[i] = Bounds(_maps.at(i).bbox(), xy); _adjust = qMin(qMin(_maps.at(i).bounds().left(), _maps.at(i).bounds().top()), _adjust); @@ -227,7 +246,7 @@ void KMZMap::groundOverlay(QXmlStreamReader &reader, QZipReader &zip) QSize size(ir.size()); if (size.isValid()) - _maps.append(Overlay(image, size, rect, rotation)); + _maps.append(Overlay(image, size, rect, rotation, &_projection)); else reader.raiseError(image + ": Invalid image file"); } else @@ -282,6 +301,8 @@ KMZMap::KMZMap(const QString &fileName, QObject *parent) QByteArray xml(zip.fileData("doc.kml")); QXmlStreamReader reader(xml); + _projection = Projection(GCS::gcs(4326)); + if (reader.readNextStartElement()) { if (reader.name() == QLatin1String("kml")) kml(reader, zip); @@ -450,6 +471,20 @@ void KMZMap::unload() _zip = 0; } +void KMZMap::setInputProjection(const Projection &projection) +{ + if (projection == _projection) + return; + + _projection = projection; + + for (int i = 0; i < _maps.size(); i++) + _maps[i].setProjection(&_projection); + + _bounds.clear(); + computeBounds(); +} + void KMZMap::draw(QPainter *painter, const QRectF &rect, int mapIndex, Flags flags) { diff --git a/src/map/kmzmap.h b/src/map/kmzmap.h index 06356ccb..049423a6 100644 --- a/src/map/kmzmap.h +++ b/src/map/kmzmap.h @@ -1,6 +1,7 @@ #ifndef KMZMAP_H #define KMZMAP_H +#include "projection.h" #include "transform.h" #include "rectd.h" #include "map.h" @@ -34,6 +35,8 @@ public: void load(); void unload(); + void setInputProjection(const Projection &projection); + bool isValid() const {return _valid;} QString errorString() const {return _errorString;} @@ -41,18 +44,16 @@ private: class Overlay { public: Overlay(const QString &path, const QSize &size, const RectC &bbox, - double rotation); + double rotation, const Projection *proj); bool operator==(const Overlay &other) const {return _path == other._path;} QPointF ll2xy(const Coordinates &c) const - {return QPointF(_transform.proj2img(QPointF(c.lon(), c.lat())));} + {return QPointF(_transform.proj2img(_proj->ll2xy(c)));} Coordinates xy2ll(const QPointF &p) const - { - PointD pp(_transform.img2proj(p)); - return Coordinates(pp.x(), pp.y()); - } + {return _proj->xy2ll(_transform.img2proj(p));} + const QString &path() const {return _path;} const RectC &bbox() const {return _bbox;} const QRectF &bounds() const {return _bounds;} qreal resolution(const QRectF &rect) const; @@ -63,13 +64,16 @@ private: void load(QZipReader *zip); void unload(); + void setProjection(const Projection *proj); + private: QString _path; + QSize _size; QRectF _bounds; RectC _bbox; qreal _rotation; Image *_img; - + const Projection *_proj; Transform _transform; }; @@ -112,6 +116,7 @@ private: int _mapIndex; QZipReader *_zip; qreal _adjust; + Projection _projection; bool _valid; QString _errorString; diff --git a/src/map/map.h b/src/map/map.h index a550e06a..ab172cec 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -49,7 +49,8 @@ public: virtual void load() {} virtual void unload() {} virtual void setDevicePixelRatio(qreal, qreal) {} - virtual void setProjection(const Projection &) {} + virtual void setOutputProjection(const Projection &) {} + virtual void setInputProjection(const Projection &) {} virtual bool isValid() const {return true;} virtual bool isReady() const {return true;}