1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-03 22:39:15 +02:00

Compare commits

...

30 Commits
7.22 ... 7.24

Author SHA1 Message Date
bb6d6a4044 Version++ 2020-03-01 14:39:40 +01:00
521369a6ec Make the WMS tile size configurable 2020-03-01 13:59:15 +01:00
45a6cdeda0 Strip the format parameters for format comparsion 2020-03-01 13:26:19 +01:00
12827edcb2 Removed obsolete include 2020-03-01 11:46:44 +01:00
ee24bd54f1 Fixed tile cache reload issues 2020-03-01 11:43:08 +01:00
cc22df3bf2 Cosmetics 2020-03-01 10:30:00 +01:00
d7f0cda4b2 Properly parse the ScaleHint tag 2020-02-29 21:40:13 +01:00
a898ff2807 Use 72dpi in the ScaleHint to scaleDenominator transformation 2020-02-29 20:11:49 +01:00
9dd4e117f6 Added missing WMS 1.1 ScaleHint handling 2020-02-29 13:47:27 +01:00
92deaaaf2b Use the latest xmlns 2020-02-23 12:45:36 +01:00
015a9187a0 Fixed memory leak
(formal only, the data is allocated during the whole application life anyway)
2020-02-20 09:02:01 +01:00
1de9c6ef5d Version++ 2020-02-17 20:19:11 +01:00
54b6225c6c Fixed "rect inversion" problems 2020-02-17 19:23:36 +01:00
48c7299ba6 Improved railroad lines default style 2020-02-17 19:21:36 +01:00
c284b9fa7c Back to lon, lat order to not correspond with all the APIs 2020-02-17 19:20:18 +01:00
2c503a2406 Enable world basemap projection in web mercator projection 2020-02-17 09:47:47 +01:00
27edc4d6b5 Properly handle IMG basemaps (gmapbmap.img) 2020-02-17 09:19:15 +01:00
f333a76ef7 Some more regions/countries rendering improvement 2020-02-16 20:31:09 +01:00
2c114f43c5 Code cleanup 2020-02-16 15:59:51 +01:00
29e29591f8 Fixed typo 2020-02-16 13:59:19 +01:00
e4ac9fda0e Improved country names labels handling 2020-02-16 12:57:40 +01:00
26229e5871 Fixed tile bounds exceeding map bounds 2020-02-16 08:43:37 +01:00
64bee2f2f4 Use a beter default min zoom value 2020-02-15 21:58:29 +01:00
e4288ee95c Remove devel code 2020-02-15 21:51:47 +01:00
c9b3c2eedd Compute the min map zoom from the tiles 2020-02-15 21:49:00 +01:00
42e4b0769f Fixed broken tile/subdivs/polygon bounds/coordinates
+ do the coordinates left shift in a C++ standard defined way
2020-02-15 11:46:16 +01:00
ce043ef8fa Do not try to open the user style file when it is not set 2020-02-14 20:00:42 +01:00
8c7050e273 Fixed broken subdiv bounds 2020-02-14 09:20:10 +01:00
d670107a11 Fixed gap between the basemap and normal map tiles on some maps 2020-02-13 22:49:15 +01:00
7b03c4d852 Fixed error handling 2020-02-13 22:48:47 +01:00
38 changed files with 284 additions and 151 deletions

View File

@ -1,4 +1,4 @@
version: 7.22.{build}
version: 7.24.{build}
configuration:
- Release

View File

@ -3,7 +3,7 @@ unix:!macx {
} else {
TARGET = GPXSee
}
VERSION = 7.22
VERSION = 7.24
QT += core \
gui \

View File

@ -7,7 +7,7 @@
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "7.22"
!define VERSION "7.24"
; The file to write
OutFile "GPXSee-${VERSION}.exe"

View File

@ -7,7 +7,7 @@
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "7.22"
!define VERSION "7.24"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3">
<map xmlns="http://www.gpxsee.org/map/1.4">
<name>4UMaps</name>
<url>https://tileserver.4umaps.com/$z/$x/$y.png</url>
<zoom min="2" max="15"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3" type="WMTS">
<map xmlns="http://www.gpxsee.org/map/1.4" type="WMTS">
<name>Antarctica</name>
<url type="REST">https://gis.ngdc.noaa.gov/arcgis/rest/services/antarctic/antarctic_basemap/MapServer/WMTS/1.0.0/WMTSCapabilities.xml</url>
<copyright>NOAA National Centers for Environmental Information (NCEI); International Bathymetric Chart of the Southern Ocean (IBCSO); General Bathymetric Chart of the Oceans (GEBCO); Natural Earth</copyright>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3">
<map xmlns="http://www.gpxsee.org/map/1.4">
<name>Open Street Map</name>
<url>https://tile.openstreetmap.org/$z/$x/$y.png</url>
<copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © OpenStreetMap (CC-BY-SA)</copyright>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3">
<map xmlns="http://www.gpxsee.org/map/1.4">
<name>Open Topo Map</name>
<url>https://a.tile.opentopomap.org/$z/$x/$y.png</url>
<zoom max="17"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3">
<map xmlns="http://www.gpxsee.org/map/1.4">
<name>USGS Imagery</name>
<url>https://basemap.nationalmap.gov/ArcGIS/rest/services/USGSImageryOnly/MapServer/tile/$z/$y/$x</url>
<zoom min="2" max="15"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3">
<map xmlns="http://www.gpxsee.org/map/1.4">
<name>USGS Topo</name>
<url>https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/$z/$y/$x</url>
<zoom min="2" max="15"/>

View File

@ -2,7 +2,6 @@
#include <QGraphicsScene>
#include <QWheelEvent>
#include <QApplication>
#include <QPixmapCache>
#include <QScrollBar>
#include "data/poi.h"
#include "data/data.h"
@ -351,7 +350,6 @@ void MapView::setMap(Map *map)
centerOn(nc);
reloadMap();
QPixmapCache::clear();
}
void MapView::setPOI(POI *poi)
@ -982,7 +980,6 @@ void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
_deviceRatio = deviceRatio;
_mapRatio = mapRatio;
QPixmapCache::clear();
QRectF vr(mapToScene(viewport()->rect()).boundingRect()
.intersected(_map->bounds()));

View File

@ -14,8 +14,8 @@ double Coordinates::distanceTo(const Coordinates &c) const
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Coordinates &c)
{
dbg.nospace() << qSetRealNumberPrecision(10) << "Coordinates(" << c.lat()
<< ", " << c.lon() << ")";
dbg.nospace() << qSetRealNumberPrecision(10) << "Coordinates(" << c.lon()
<< ", " << c.lat() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

View File

@ -3,14 +3,16 @@
#include <QtGlobal>
#define LS(val, bits) ((qint32)(((quint32)(val))<<(bits)))
inline double toWGS32(qint32 v)
{
return (double)(((double)v / (double)(1U<<31)) * (double)180);
return ((double)v / (double)(1U<<31)) * 180.0;
}
inline double toWGS24(qint32 v)
{
return toWGS32(v<<8);
return toWGS32(LS(v, 8));
}
#endif // GARMIN_H

View File

@ -28,6 +28,9 @@ public:
double left() const {return _tl.lon();}
double right() const {return _br.lon();}
double width() const {return (right() - left());}
double height() const {return (top() - bottom());}
void setLeft(double val) {_tl.rlon() = val;}
void setRight(double val) {_br.rlon() = val;}
void setTop(double val) {_tl.rlat() = val;}

View File

@ -1,7 +1,13 @@
#include <QPainter>
#include <QImage>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#include <QtCore/qmath.h>
#else // QT5
#include <QtMath>
#endif // QT5
#include "bitmapline.h"
static QImage img2line(const QImage &img, int width)
{
Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied);
@ -32,8 +38,8 @@ void BitmapLine::draw(QPainter *painter, const QPolygonF &line,
painter->save();
painter->translate(segment.p1());
painter->rotate(-segment.angle());
painter->drawImage(0, -img.height()/2, img2line(img, segment.length()));
painter->drawImage(0.0, -img.height()/2.0, img2line(img,
qCeil(segment.length())));
painter->restore();
}
}

View File

@ -1,5 +1,6 @@
#include <QXmlStreamReader>
#include <QDir>
#include "map/osm.h"
#include "vectortile.h"
#include "gmap.h"
@ -84,11 +85,13 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
tile->addFile(fi.absoluteFilePath(), tileType(fi.suffix()));
}
if (!tile->init(baseMap)) {
if (!tile->init()) {
qWarning("%s: Invalid map tile", qPrintable(dir.path()));
delete tile;
return false;
}
if (baseMap)
tile->markAsBasemap();
double min[2], max[2];
min[0] = tile->bounds().left();
@ -98,6 +101,13 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
_tileTree.Insert(min, max, tile);
_bounds |= tile->bounds();
if (tile->zooms().min() < _zooms.min())
_zooms.setMin(tile->zooms().min());
// Limit world maps bounds so that the maps can be projected using
// the default Web Mercator projection
if (_bounds.height() > 120)
_bounds &= OSM::BOUNDS;
return true;
}

View File

@ -1,5 +1,6 @@
#include <QMap>
#include <QtEndian>
#include "map/osm.h"
#include "vectortile.h"
#include "img.h"
@ -132,11 +133,13 @@ IMG::IMG(const QString &fileName) : _file(fileName)
}
// Create tile tree
int minMapZoom = 24;
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
if (!tile->init(false)) {
if (!tile->init()) {
qWarning("%s: %s: Invalid map tile", qPrintable(_file.fileName()),
qPrintable(it.key()));
delete tile;
@ -151,8 +154,26 @@ IMG::IMG(const QString &fileName) : _file(fileName)
_tileTree.Insert(min, max, tile);
_bounds |= tile->bounds();
if (tile->zooms().min() < _zooms.min())
_zooms.setMin(tile->zooms().min());
if (tile->zooms().min() < minMapZoom)
minMapZoom = tile->zooms().min();
}
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
if (tile->zooms().min() > minMapZoom)
_baseMap = true;
if (tile->zooms().min() == minMapZoom)
tile->markAsBasemap();
}
// Limit world maps bounds so that the maps can be projected using
// the default Web Mercator projection
if (_bounds.height() > 120)
_bounds &= OSM::BOUNDS;
if (!_tileTree.Count())
_errorString = "No usable map tile found";
else

View File

@ -30,15 +30,20 @@ static quint8 SPECIAL_CHARS[] = {
'8', '9', '~', '~', '~', '~', '~', '~'
};
static QString capitalize(const QString &str)
static bool isAllUpperCase(const QString &str)
{
if (str.isEmpty())
return str;
return false;
for (int i = 0; i < str.size(); i++)
if (str.at(i).isLetter() && !(str.at(i).isUpper()
|| str.at(i) == QChar(0x00DF)))
return str;
return false;
return true;
}
static QString capitalized(const QString &str)
{
QString ret(str);
for (int i = 0; i < str.size(); i++)
if (i && !str.at(i-1).isSpace())
@ -77,7 +82,7 @@ bool LBLFile::init(Handle &hdl)
return true;
}
Label LBLFile::label6b(Handle &hdl, quint32 offset) const
Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
{
Label::Shield::Type shieldType = Label::Shield::None;
QByteArray label, shieldLabel;
@ -95,9 +100,12 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset) const
int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F};
for (int cpt = 0; cpt < 4; cpt++) {
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d))
return Label(capitalize(QString::fromLatin1(label)),
Label::Shield(shieldType, shieldLabel));
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
QString text(QString::fromLatin1(label));
return Label(capitalize && isAllUpperCase(text)
? capitalized(text) : text, Label::Shield(shieldType,
shieldLabel));
}
switch (curCharSet) {
case Normal:
if (c[cpt] == 0x1c)
@ -127,7 +135,7 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset) const
}
}
Label LBLFile::label8b(Handle &hdl, quint32 offset) const
Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize) const
{
Label::Shield::Type shieldType = Label::Shield::None;
QByteArray label, shieldLabel;
@ -157,12 +165,15 @@ Label LBLFile::label8b(Handle &hdl, quint32 offset) const
bap->append(c);
}
return Label(capitalize(_codec ? _codec->toUnicode(label)
: QString::fromLatin1(label)), Label::Shield(shieldType, _codec
? _codec->toUnicode(shieldLabel) : QString::fromLatin1(shieldLabel)));
QString text(_codec ? _codec->toUnicode(label) : QString::fromLatin1(label));
QString shieldText(_codec ? _codec->toUnicode(shieldLabel)
: QString::fromLatin1(shieldLabel));
return Label(capitalize && isAllUpperCase(text) ? capitalized(text) : text,
Label::Shield(shieldType, shieldText));
}
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi)
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize)
{
if (!_multiplier && !init(hdl))
return QString();
@ -183,10 +194,10 @@ Label LBLFile::label(Handle &hdl, quint32 offset, bool poi)
switch (_encoding) {
case 6:
return label6b(hdl, labelOffset);
return label6b(hdl, labelOffset, capitalize);
case 9:
case 10:
return label8b(hdl, labelOffset);
return label8b(hdl, labelOffset, capitalize);
default:
return Label();
}

View File

@ -19,13 +19,14 @@ public:
_codec(0), _offset(0), _size(0), _poiOffset(0), _poiSize(0),
_poiMultiplier(0), _multiplier(0), _encoding(0) {}
Label label(Handle &hdl, quint32 offset, bool poi = false);
Label label(Handle &hdl, quint32 offset, bool poi = false,
bool capitalize = true);
private:
bool init(Handle &hdl);
Label label6b(Handle &hdl, quint32 offset) const;
Label label8b(Handle &hdl, quint32 offset) const;
Label label6b(Handle &hdl, quint32 offset, bool capitalize) const;
Label label8b(Handle &hdl, quint32 offset, bool capitalize) const;
QTextCodec *_codec;
quint32 _offset;

View File

@ -54,7 +54,8 @@ inline bool pointCb(VectorTile *tile, void *context)
}
MapData::MapData() : _typ(0), _style(0), _baseMap(false), _valid(false)
MapData::MapData() : _typ(0), _style(0), _zooms(15, 28), _baseMap(false),
_valid(false)
{
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);
_pointCache.setMaxCost(CACHED_SUBDIVS_COUNT);
@ -104,8 +105,12 @@ void MapData::load()
if (_typ)
_style = new Style(_typ);
else {
SubFile typ(ProgramPaths::typFile());
_style = new Style(&typ);
QString typFile(ProgramPaths::typFile());
if (!typFile.isEmpty()) {
SubFile typ(typFile);
_style = new Style(&typ);
} else
_style = new Style();
}
}

View File

@ -7,6 +7,7 @@
#include <QDebug>
#include "common/rectc.h"
#include "common/rtree.h"
#include "common/range.h"
#include "label.h"
class Style;
@ -58,6 +59,7 @@ public:
const QString &name() const {return _name;}
const RectC &bounds() const {return _bounds;}
const Range &zooms() const {return _zooms;}
const Style *style() const {return _style;}
void polys(const RectC &rect, int bits, QList<Poly> *polygons,
QList<Poly> *lines);
@ -79,6 +81,7 @@ protected:
SubFile *_typ;
Style *_style;
TileTree _tileTree;
Range _zooms;
bool _baseMap;
bool _valid;

View File

@ -8,6 +8,20 @@
#include "rgnfile.h"
static quint64 pointId(const QPoint &pos, quint32 type, quint32 labelPtr)
{
quint64 id;
uint hash = qHash(QPair<uint,uint>(qHash(QPair<int, int>(pos.x(),
pos.y())), labelPtr & 0x3FFFFF));
id = ((quint64)type)<<32 | hash;
// Make country labels precedent over city labels
if (!(type >= 0x1400 && type <= 0x153f))
id |= 1ULL<<63;
return id;
}
bool RGNFile::skipClassFields(Handle &hdl) const
{
quint8 flags;
@ -154,8 +168,8 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8;
QPoint pos(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())),
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits())));
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + LS(lat, 24-subdiv->bits()));
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat()));
@ -164,8 +178,10 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
DeltaStream stream(*this, hdl, len, bitstreamInfo, labelPtr & 0x400000,
false);
while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += lonDelta<<(24-subdiv->bits());
pos.ry() += latDelta<<(24-subdiv->bits());
pos.rx() += LS(lonDelta, (24-subdiv->bits()));
if (pos.rx() >= 0x800000 && subdiv->lon() >= 0)
pos.rx() = 0x7fffff;
pos.ry() += LS(latDelta, (24-subdiv->bits()));
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat()));
@ -219,8 +235,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
labelPtr = 0;
if (!_huffmanTable.isNull()) {
pos = QPoint((subdiv->lon()<<8) + ((qint32)lon<<(32-subdiv->bits())),
(subdiv->lat()<<8) + ((qint32)lat<<(32-subdiv->bits())));
pos = QPoint(LS(subdiv->lon(), 8) + LS(lon, 32-subdiv->bits()),
LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
qint32 lonDelta, latDelta;
HuffmanStream stream(*this, hdl, len, _huffmanTable,
@ -229,16 +245,18 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
if (shift) {
if (!stream.readOffset(lonDelta, latDelta))
return false;
pos = QPoint(pos.x() | lonDelta<<(32-subdiv->bits()-shift),
pos.y() | latDelta<<(32-subdiv->bits()-shift));
pos = QPoint(pos.x() | LS(lonDelta, 32-subdiv->bits()-shift),
pos.y() | LS(latDelta, 32-subdiv->bits()-shift));
}
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat()));
while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += lonDelta<<(32-subdiv->bits()-shift);
pos.ry() += latDelta<<(32-subdiv->bits()-shift);
pos.rx() += LS(lonDelta, 32-subdiv->bits()-shift);
if (pos.rx() < 0 && subdiv->lon() >= 0)
pos.rx() = 0x7fffffff;
pos.ry() += LS(latDelta, 32-subdiv->bits()-shift);
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat()));
@ -248,8 +266,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
if (!(stream.atEnd() && stream.flush()))
return false;
} else {
pos = QPoint(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())),
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits())));
pos = QPoint(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + LS(lat, 24-subdiv->bits()));
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat()));
@ -262,8 +280,10 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
DeltaStream stream(*this, hdl, len - 1, bitstreamInfo, false, true);
while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += lonDelta<<(24-subdiv->bits());
pos.ry() += latDelta<<(24-subdiv->bits());
pos.rx() += LS(lonDelta, 24-subdiv->bits());
if (pos.rx() >= 0x800000 && subdiv->lon() >= 0)
pos.rx() = 0x7fffff;
pos.ry() += LS(latDelta, 24-subdiv->bits());
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat()));
@ -294,9 +314,6 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
SegmentType segmentType, LBLFile *lbl, Handle &lblHdl,
QList<IMG::Point> *points) const
{
quint8 type, subtype;
qint16 lon, lat;
quint32 labelPtr;
const SubDiv::Segment &segment = (segmentType == IndexedPoint)
? subdiv->idxPoints() : subdiv->points();
@ -307,6 +324,9 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
while (hdl.pos() < (int)segment.end()) {
IMG::Point point;
quint8 type, subtype;
qint16 lon, lat;
quint32 labelPtr;
if (!(readUInt8(hdl, type) && readUInt24(hdl, labelPtr)
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
@ -317,20 +337,17 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
} else
subtype = 0;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + LS(lat, 24-subdiv->bits()));
point.type = (quint16)type<<8 | subtype;
qint32 lonOffset = lon<<(24-subdiv->bits());
qint32 latOffset = lat<<(24-subdiv->bits());
point.coordinates = Coordinates(toWGS24(subdiv->lon() + lonOffset),
toWGS24(subdiv->lat() + latOffset));
uint hash = qHash(QPair<uint,uint>(qHash(QPair<qint32, qint32>
(subdiv->lon() + lonOffset, subdiv->lat() + latOffset)),
labelPtr & 0x3FFFFF));
point.id = ((quint64)point.type)<<32 | hash;
point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
point.poi = labelPtr & 0x400000;
if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi);
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi,
!(point.type == 0x1400 || point.type == 0x1500
|| point.type == 0x1e00));
points->append(point);
}
@ -341,12 +358,8 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
Handle &lblHdl, QList<IMG::Point> *points) const
{
quint8 type, subtype;
qint16 lon, lat;
quint32 labelPtr;
const SubDiv::Segment &segment = subdiv->extPoints();
if (!segment.isValid())
return true;
if (!seek(hdl, segment.offset()))
@ -354,19 +367,14 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
while (hdl.pos() < (int)segment.end()) {
IMG::Point point;
qint16 lon, lat;
quint8 type, subtype;
quint32 labelPtr = 0;
if (!(readUInt8(hdl, type) && readUInt8(hdl, subtype)
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
return false;
point.type = 0x10000 | (((quint32)type)<<8) | (subtype & 0x1F);
qint32 lonOffset = lon<<(24-subdiv->bits());
qint32 latOffset = lat<<(24-subdiv->bits());
point.coordinates = Coordinates(toWGS24(subdiv->lon() + lonOffset),
toWGS24(subdiv->lat() + latOffset));
labelPtr = 0;
if (subtype & 0x20 && !readUInt24(hdl, labelPtr))
return false;
if (subtype & 0x80 && !skipClassFields(hdl))
@ -374,15 +382,17 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags, Point))
return false;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + LS(lat, 24-subdiv->bits()));
point.type = 0x10000 | (((quint32)type)<<8) | (subtype & 0x1F);
// Discard NT points breaking style draw order logic (and causing huge
// performance drawback)
if (point.type == 0x11400)
continue;
uint hash = qHash(QPair<uint,uint>(qHash(QPair<qint32, qint32>
(subdiv->lon() + lonOffset, subdiv->lat() + latOffset)),
labelPtr & 0x3FFFFF));
point.id = ((quint64)point.type)<<32 | hash;
point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
point.poi = labelPtr & 0x400000;
if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi);
@ -415,7 +425,7 @@ QMap<RGNFile::SegmentType, SubDiv::Segment> RGNFile::segments(Handle &hdl,
quint32 ls = 0;
SegmentType lt = (SegmentType)0;
for (quint16 mask = 0x1; mask <= 0x10; mask <<= 1) {
for (quint8 mask = 0x1; mask <= 0x10; mask <<= 1) {
if (subdiv->objects() & mask) {
if (ls) {
quint16 po;

View File

@ -1,4 +1,5 @@
#include <QImage>
#include <QPainter>
#include "style.h"
@ -79,13 +80,19 @@ void Style::defaultPolygonStyle()
<< TYPE(0x13);
}
static QImage railroad()
{
QImage img(16, 4, QImage::Format_ARGB32_Premultiplied);
img.fill(QColor("#717171"));
QPainter p(&img);
p.setPen(QPen(Qt::white, 2));
p.drawLine(9, 2, 15, 2);
return img;
}
void Style::defaultLineStyle()
{
QVector<qreal> pattern;
pattern << 4 << 4;
QPen rr(QColor("#717171"), 3, Qt::CustomDashLine);
rr.setDashPattern(pattern);
_lines[TYPE(0x01)] = Line(QPen(QColor("#9bd772"), 2, Qt::SolidLine),
QPen(QColor("#72a35a"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x02)] = Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine),
@ -109,13 +116,13 @@ void Style::defaultLineStyle()
QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x0c)] = Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine),
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x14)] = Line(rr, QPen(Qt::white, 3, Qt::SolidLine,
Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x14)] = Line(railroad());
_lines[TYPE(0x16)] = Line(QPen(QColor("#aba083"), 1, Qt::DotLine));
_lines[TYPE(0x18)] = Line(QPen(QColor("#9fc4e1"), 2, Qt::SolidLine));
_lines[TYPE(0x18)].setTextColor(QColor("#9fc4e1"));
//_lines[TYPE(0x1a)] = Line(QPen(QColor("#7697b7"), 1, Qt::DashLine));
_lines[TYPE(0x1b)] = Line(QPen(QColor("#7697b7"), 1, Qt::DashLine));
_lines[TYPE(0x1c)] = Line(QPen(QColor("#505145"), 1, Qt::DashLine));
_lines[TYPE(0x1e)] = Line(QPen(QColor("#505145"), 2, Qt::DashDotLine));
_lines[TYPE(0x1f)] = Line(QPen(QColor("#9fc4e1"), 3, Qt::SolidLine));
_lines[TYPE(0x1f)].setTextColor(QColor("#9fc4e1"));
@ -145,6 +152,17 @@ void Style::defaultLineStyle()
void Style::defaultPointStyle()
{
// Countries
_points[TYPE(0x14)].setTextColor(QColor("#505145"));
_points[TYPE(0x14)].setTextFontSize(Small);
_points[TYPE(0x15)].setTextColor(QColor("#505145"));
_points[TYPE(0x15)].setTextFontSize(Small);
// Regions
_points[TYPE(0x1e)].setTextColor(QColor("#505145"));
_points[TYPE(0x1e)].setTextFontSize(ExtraSmall);
_points[TYPE(0x28)].setTextFontSize(Small);
// Cities
_points[TYPE(0x01)].setTextFontSize(Large);
_points[TYPE(0x02)].setTextFontSize(Large);

View File

@ -16,7 +16,8 @@ public:
None = 1,
Small = 2,
Normal = 3,
Large = 4
Large = 4,
ExtraSmall = 5
};
enum POIClass {

View File

@ -2,7 +2,6 @@
#define SUBFILE_H
#include <QVector>
#include <QDebug>
#include <QFile>
#include "img.h"

View File

@ -3,6 +3,11 @@
#include "trefile.h"
static inline double RB(qint32 val)
{
return (val == -0x800000 || val == 0x800000) ? 180.0 : toWGS24(val);
}
static void demangle(quint8 *data, quint32 size, quint32 key)
{
static const unsigned char shuf[] = {
@ -37,12 +42,13 @@ TREFile::~TREFile()
clear();
}
bool TREFile::init(bool baseMap)
bool TREFile::init()
{
Handle hdl(this);
quint8 locked;
quint16 hdrLen;
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)
&& seek(hdl, _gmpOffset + 0x0D) && readUInt8(hdl, locked)))
return false;
@ -53,7 +59,8 @@ bool TREFile::init(bool baseMap)
&& readInt24(hdl, east) && readInt24(hdl, south) && readInt24(hdl, west)))
return false;
_bounds = RectC(Coordinates(toWGS24(west), toWGS24(north)),
Coordinates(toWGS24(east), toWGS24(south)));
Coordinates(RB(east), toWGS24(south)));
Q_ASSERT(_bounds.left() <= _bounds.right());
// Levels & subdivs info
quint32 levelsOffset, levelsSize, subdivSize;
@ -108,7 +115,7 @@ bool TREFile::init(bool baseMap)
}
}
_isBaseMap = baseMap;
_isBaseMap = false;
return (_firstLevel >= 0);
}
@ -132,8 +139,8 @@ bool TREFile::load(int idx)
for (int j = 0; j < _levels.at(idx).subdivs; j++) {
quint32 oo;
qint32 lon, lat;
quint16 width, height, nextLevel;
qint32 lon, lat, width, height;
quint16 nextLevel;
if (!(readUInt32(hdl, oo) && readInt24(hdl, lon) && readInt24(hdl, lat)
&& readUInt16(hdl, width) && readUInt16(hdl, height)))
@ -149,17 +156,16 @@ bool TREFile::load(int idx)
s->setEnd(offset);
width &= 0x7FFF;
width <<= (24 - _levels.at(idx).bits);
height <<= (24 - _levels.at(idx).bits);
width = LS(width, 24 - _levels.at(idx).bits);
height = LS(height, 24 - _levels.at(idx).bits);
s = new SubDiv(offset, lon, lat, _levels.at(idx).bits, objects);
sl.append(s);
double min[2], max[2];
RectC bounds(Coordinates(toWGS24(lon - width),
toWGS24(lat + height + 1)), Coordinates(toWGS24(lon + width + 1),
toWGS24(lat - height)));
RectC bounds(Coordinates(toWGS24(lon - width), toWGS24(lat + height)),
Coordinates(RB(lon + width), toWGS24(lat - height)));
Q_ASSERT(bounds.left() <= bounds.right());
min[0] = bounds.left();
min[1] = bounds.bottom();
@ -240,15 +246,15 @@ void TREFile::clear()
int TREFile::level(int bits, bool baseMap)
{
int idx = _firstLevel;
if (baseMap) {
if (!_isBaseMap && _levels.at(idx).bits > bits)
if (!_isBaseMap && _levels.first().bits > bits)
return -1;
if (_isBaseMap && bits > _levels.last().bits)
return -1;
}
int idx = _firstLevel;
for (int i = idx + 1; i < _levels.size(); i++) {
if (_levels.at(i).bits > bits)
break;

View File

@ -18,13 +18,16 @@ public:
TREFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset) {}
~TREFile();
bool init(bool baseMap);
bool init();
void markAsBasemap() {_isBaseMap = true;}
void clear();
const RectC &bounds() const {return _bounds;}
QList<SubDiv*> subdivs(const RectC &rect, int bits, bool baseMap);
quint32 shift(quint8 bits) const
{return (bits == _levels.last().bits) ? (_flags >> 0xb) & 7 : 0;}
Range zooms() const
{return Range(_levels.at(_firstLevel).bits, _levels.last().bits);}
private:
struct MapLevel {

View File

@ -82,12 +82,12 @@ SubFile *VectorTile::addFile(const QString &path, SubFile::Type type)
}
}
bool VectorTile::init(bool baseMap)
bool VectorTile::init()
{
if (_gmp && !initGMP())
return false;
if (!(_tre && _tre->init(baseMap) && _rgn))
if (!(_tre && _tre->init() && _rgn))
return false;
return true;

View File

@ -15,10 +15,12 @@ public:
delete _tre; delete _rgn; delete _lbl; delete _net; delete _gmp;
}
bool init(bool baseMap);
bool init();
void markAsBasemap() {_tre->markAsBasemap();}
void clear() {_tre->clear();}
const RectC &bounds() const {return _tre->bounds();}
Range zooms() const {return _tre->zooms();}
SubFile *file(SubFile::Type type);
SubFile *addFile(IMG *img, SubFile::Type type);

View File

@ -79,8 +79,6 @@ private:
};
static const Range zooms(12, 28);
static const QColor shieldColor(Qt::white);
static const QColor shieldBgColor1("#dd3e3e");
static const QColor shieldBgColor2("#379947");
@ -129,6 +127,7 @@ static QFont *font(Style::FontSize size, Style::FontSize defaultSize
static QFont large = pixelSizeFont(16);
static QFont normal = pixelSizeFont(14);
static QFont small = pixelSizeFont(12);
static QFont extraSmall = pixelSizeFont(10);
switch (size) {
case Style::None:
@ -139,6 +138,8 @@ static QFont *font(Style::FontSize size, Style::FontSize defaultSize
return &normal;
case Style::Small:
return &small;
case Style::ExtraSmall:
return &extraSmall;
default:
return font(defaultSize);
}
@ -243,7 +244,7 @@ IMGMap::IMGMap(const QString &fileName, QObject *parent)
return;
}
_zoom = zooms.min();
_zoom = _data->zooms().min();
updateTransform();
_valid = true;
@ -271,8 +272,8 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
if (rect.isValid()) {
RectD pr(rect, _projection, 10);
_zoom = zooms.min();
for (int i = zooms.min() + 1; i <= zooms.max(); i++) {
_zoom = _data->zooms().min();
for (int i = _data->zooms().min() + 1; i <= _data->zooms().max(); i++) {
Transform t(transform(i));
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
if (size.width() < r.width() || size.height() < r.height())
@ -280,7 +281,7 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
_zoom = i;
}
} else
_zoom = zooms.max();
_zoom = _data->zooms().max();
updateTransform();
@ -289,14 +290,14 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
int IMGMap::zoomIn()
{
_zoom = qMin(_zoom + 1, zooms.max());
_zoom = qMin(_zoom + 1, _data->zooms().max());
updateTransform();
return _zoom;
}
int IMGMap::zoomOut()
{
_zoom = qMax(_zoom - 1, zooms.min());
_zoom = qMax(_zoom - 1, _data->zooms().min());
updateTransform();
return _zoom;
}
@ -590,15 +591,21 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
tiles.append(RasterTile(this, ttl, key));
RasterTile &tile = tiles.last();
RectD polyRect(_transform.img2proj(ttl), _transform.img2proj(
QPointF(ttl.x() + TILE_SIZE, ttl.y() + TILE_SIZE)));
_data->polys(polyRect.toRectC(_projection, 4), _zoom,
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
ttl.y() + TILE_SIZE));
polyRect &= bounds().adjusted(0.5, 0.5, -0.5, -0.5);
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
_data->polys(polyRectD.toRectC(_projection, 4), _zoom,
&(tile.polygons()), &(tile.lines()));
RectD pointRect(_transform.img2proj(QPointF(ttl.x() - TEXT_EXTENT,
ttl.y() - TEXT_EXTENT)), _transform.img2proj(QPointF(ttl.x()
+ TILE_SIZE + TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT)));
_data->points(pointRect.toRectC(_projection, 4), _zoom,
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
+ TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
pointRect &= bounds().adjusted(0.5, 0.5, -0.5, -0.5);
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data->points(pointRectD.toRectC(_projection, 4), _zoom,
&(tile.points()));
}
}

View File

@ -17,13 +17,9 @@ bool MapList::loadMap(Map *map, const QString &path)
if (map && map->isValid()) {
_maps.append(map);
return true;
} else if (map) {
_errorPath = path;
_errorString = map->errorString();
return false;
} else {
_errorString = path;
_errorString = "Unknown file format";
_errorPath = path;
_errorString = (map) ? map->errorString() : "Unknown file format";
return false;
}
}
@ -56,7 +52,7 @@ bool MapList::loadFile(const QString &path, bool *terminate)
else if (GMAP::isGMAP(path)) {
if (terminate)
*terminate = true;
map = new IMGMap(path);
map = new IMGMap(path, this);
}
} else if (suffix == "jnx")
map = new JNXMap(path, this);

View File

@ -300,7 +300,7 @@ Map *MapSource::loadMap(const QString &path, QString &errorString)
case WMS:
return new WMSMap(config.name, WMS::Setup(config.url, config.layer,
config.style, config.format, config.crs, config.coordinateSystem,
config.dimensions, config.authorization));
config.dimensions, config.authorization), config.tileSize);
case TMS:
return new OnlineMap(config.name, config.url, config.zooms,
config.bounds, config.tileRatio, config.authorization,

View File

@ -169,6 +169,17 @@ void TileLoader::clearCache()
dir.remove(list.at(i));
_downloader->clearErrors();
QPixmapCache::clear();
}
void TileLoader::setScaledSize(int size)
{
if (_scaledSize == size)
return;
_scaledSize = size;
QPixmapCache::clear();
}
QUrl TileLoader::tileUrl(const Tile &tile) const

View File

@ -16,7 +16,7 @@ public:
void setUrl(const QString &url) {_url = url;}
void setAuthorization(const Authorization &authorization)
{_authorization = authorization;}
void setScaledSize(int size) {_scaledSize = size;}
void setScaledSize(int size);
void setQuadTiles(bool quadTiles) {_quadTiles = quadTiles;}
void loadTilesAsync(QVector<Tile> &list);

View File

@ -1,3 +1,4 @@
#include <cmath>
#include <QFileInfo>
#include <QEventLoop>
#include <QXmlStreamReader>
@ -7,6 +8,14 @@
#include "wms.h"
static inline double hint2denominator(double h)
{
/* Some WMS 1.1.1 servers use a 72dpi resolution by default. Using the usual
90dpi (0.28mm) resolution known from later standards (WMS 1.3, WMTS) does
make them return emty images in the "max" scale level. */
return h / (M_SQRT2 * 0.36e-3);
}
WMS::CTX::CTX(const Setup &setup) : setup(setup), formatSupported(false)
{
QStringList ll = setup.layer().split(',');
@ -28,7 +37,8 @@ void WMS::getMap(QXmlStreamReader &reader, CTX &ctx)
{
while (reader.readNextStartElement()) {
if (reader.name() == "Format") {
if (reader.readElementText() == ctx.setup.format())
QString format(reader.readElementText());
if (format.left(format.indexOf(';')) == ctx.setup.format())
ctx.formatSupported = true;
} else
reader.skipCurrentElement();
@ -97,7 +107,16 @@ void WMS::layer(QXmlStreamReader &reader, CTX &ctx,
CRSs.append(reader.readElementText());
else if (reader.name() == "Style")
styles.append(style(reader));
else if (reader.name() == "MinScaleDenominator") {
else if (reader.name() == "ScaleHint") {
QXmlStreamAttributes attr = reader.attributes();
double minHint = attr.value("min").toString().toDouble();
double maxHint = attr.value("max").toString().toDouble();
if (minHint > 0)
scaleDenominator.setMin(hint2denominator(minHint));
if (maxHint > 0)
scaleDenominator.setMax(hint2denominator(maxHint));
reader.skipCurrentElement();
} else if (reader.name() == "MinScaleDenominator") {
double sd = reader.readElementText().toDouble();
if (sd > 0)
scaleDenominator.setMin(sd);

View File

@ -10,7 +10,6 @@
#define CAPABILITIES_FILE "capabilities.xml"
#define TILE_SIZE 256
#define EPSILON 1e-6
double WMSMap::sd2res(double scaleDenominator) const
@ -25,7 +24,7 @@ QString WMSMap::tileUrl(const QString &version) const
url = QString("%1%2version=%3&request=GetMap&bbox=$bbox"
"&width=%4&height=%5&layers=%6&styles=%7&format=%8&transparent=true")
.arg(_setup.url(), _setup.url().contains('?') ? "&" : "?", version,
QString::number(TILE_SIZE), QString::number(TILE_SIZE), _setup.layer(),
QString::number(_tileSize), QString::number(_tileSize), _setup.layer(),
_setup.style(), _setup.format());
if (version >= "1.3.0")
@ -100,9 +99,9 @@ bool WMSMap::loadWMS()
return true;
}
WMSMap::WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent)
: Map(parent), _name(name), _setup(setup), _tileLoader(0), _zoom(0),
_mapRatio(1.0), _valid(false)
WMSMap::WMSMap(const QString &name, const WMS::Setup &setup, int tileSize,
QObject *parent) : Map(parent), _name(name), _setup(setup), _tileLoader(0),
_zoom(0), _tileSize(tileSize), _mapRatio(1.0), _valid(false)
{
_tileLoader = new TileLoader(tilesDir(), this);
_tileLoader->setAuthorization(_setup.authorization());
@ -180,7 +179,7 @@ Coordinates WMSMap::xy2ll(const QPointF &p)
qreal WMSMap::tileSize() const
{
return (TILE_SIZE / _mapRatio);
return (_tileSize / _mapRatio);
}
void WMSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
@ -194,10 +193,10 @@ void WMSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
tiles.reserve((br.x() - tl.x()) * (br.y() - tl.y()));
for (int i = tl.x(); i < br.x(); i++) {
for (int j = tl.y(); j < br.y(); j++) {
PointD ttl(_transform.img2proj(QPointF(i * TILE_SIZE,
j * TILE_SIZE)));
PointD tbr(_transform.img2proj(QPointF(i * TILE_SIZE + TILE_SIZE,
j * TILE_SIZE + TILE_SIZE)));
PointD ttl(_transform.img2proj(QPointF(i * _tileSize,
j * _tileSize)));
PointD tbr(_transform.img2proj(QPointF(i * _tileSize + _tileSize,
j * _tileSize + _tileSize)));
RectD bbox = (_cs.axisOrder() == CoordinateSystem::YX)
? RectD(PointD(tbr.y(), tbr.x()), PointD(ttl.y(), ttl.x()))
: RectD(ttl, tbr);

View File

@ -14,7 +14,8 @@ class WMSMap : public Map
Q_OBJECT
public:
WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent = 0);
WMSMap(const QString &name, const WMS::Setup &setup, int tileSize,
QObject *parent = 0);
QString name() const {return _name;}
@ -58,6 +59,7 @@ private:
RectC _bbox;
RectD _bounds;
int _zoom;
int _tileSize;
qreal _mapRatio;
bool _valid;

View File

@ -182,7 +182,8 @@ void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
if (s == ctx.setup.style())
ctx.hasStyle = true;
} else if (reader.name() == "Format") {
if (reader.readElementText() == ctx.setup.format())
QString format(reader.readElementText());
if (format.left(format.indexOf(';')) == ctx.setup.format())
ctx.hasFormat = true;
} else
reader.skipCurrentElement();