2017-03-18 01:30:31 +01:00
|
|
|
#include <QtGlobal>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QMap>
|
|
|
|
#include <QDir>
|
2017-03-21 01:15:29 +01:00
|
|
|
#include <QBuffer>
|
2017-03-21 19:02:29 +01:00
|
|
|
#include <QImage>
|
2017-03-21 20:51:23 +01:00
|
|
|
#include <QImageReader>
|
2017-03-21 09:01:30 +01:00
|
|
|
#include <QPixmapCache>
|
2017-03-18 01:30:31 +01:00
|
|
|
#include "misc.h"
|
|
|
|
#include "rd.h"
|
|
|
|
#include "wgs84.h"
|
|
|
|
#include "coordinates.h"
|
|
|
|
#include "matrix.h"
|
2017-04-17 18:03:04 +02:00
|
|
|
#include "datum.h"
|
2017-03-29 22:51:32 +02:00
|
|
|
#include "latlon.h"
|
2017-03-29 00:17:47 +02:00
|
|
|
#include "mercator.h"
|
|
|
|
#include "transversemercator.h"
|
2017-04-01 15:58:32 +02:00
|
|
|
#include "utm.h"
|
2017-04-02 22:17:16 +02:00
|
|
|
#include "lambertconic.h"
|
2017-05-04 20:25:47 +02:00
|
|
|
#include "albersequal.h"
|
2017-04-14 22:39:33 +02:00
|
|
|
#include "ozf.h"
|
2017-08-11 00:22:21 +02:00
|
|
|
#include "rectc.h"
|
2017-03-21 09:27:44 +01:00
|
|
|
#include "offlinemap.h"
|
2017-03-18 01:30:31 +01:00
|
|
|
|
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
int OfflineMap::parse(QIODevice &device, QList<ReferencePoint> &points,
|
2017-04-03 20:29:35 +02:00
|
|
|
QString &projection, ProjectionSetup &setup, QString &datum)
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
|
|
|
bool res;
|
|
|
|
int ln = 1;
|
|
|
|
|
|
|
|
while (!device.atEnd()) {
|
|
|
|
QByteArray line = device.readLine();
|
|
|
|
|
|
|
|
if (ln == 1) {
|
2017-03-29 22:51:32 +02:00
|
|
|
if (!line.trimmed().startsWith("OziExplorer Map Data File"))
|
2017-03-18 01:30:31 +01:00
|
|
|
return ln;
|
2017-04-21 21:15:58 +02:00
|
|
|
} else if (ln == 2)
|
|
|
|
_name = line.trimmed();
|
|
|
|
else if (ln == 3)
|
2017-03-18 01:30:31 +01:00
|
|
|
_imgPath = line.trimmed();
|
2017-04-03 20:29:35 +02:00
|
|
|
else if (ln == 5)
|
|
|
|
datum = line.split(',').at(0).trimmed();
|
2017-03-20 22:52:39 +01:00
|
|
|
else {
|
2017-03-18 01:30:31 +01:00
|
|
|
QList<QByteArray> list = line.split(',');
|
2017-03-20 22:52:39 +01:00
|
|
|
QString key(list.at(0).trimmed());
|
2017-04-01 15:58:32 +02:00
|
|
|
bool ll = true; bool pp = true;
|
2017-03-20 22:52:39 +01:00
|
|
|
|
|
|
|
if (key.startsWith("Point") && list.count() == 17
|
|
|
|
&& !list.at(2).trimmed().isEmpty()) {
|
2017-03-18 01:30:31 +01:00
|
|
|
int x = list.at(2).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
|
|
|
return ln;
|
|
|
|
int y = list.at(3).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
|
|
|
return ln;
|
2017-04-01 15:58:32 +02:00
|
|
|
|
2017-03-18 01:30:31 +01:00
|
|
|
int latd = list.at(6).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
2017-04-01 15:58:32 +02:00
|
|
|
ll = false;
|
2017-03-18 01:30:31 +01:00
|
|
|
qreal latm = list.at(7).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
2017-04-01 15:58:32 +02:00
|
|
|
ll = false;
|
2017-03-18 01:30:31 +01:00
|
|
|
int lond = list.at(9).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
2017-04-01 15:58:32 +02:00
|
|
|
ll = false;
|
2017-03-18 01:30:31 +01:00
|
|
|
qreal lonm = list.at(10).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
2017-04-01 15:58:32 +02:00
|
|
|
ll = false;
|
2017-05-06 00:53:39 +02:00
|
|
|
if (ll && list.at(8).trimmed() == "S") {
|
2017-03-18 01:30:31 +01:00
|
|
|
latd = -latd;
|
2017-05-06 00:53:39 +02:00
|
|
|
latm = -latm;
|
|
|
|
}
|
|
|
|
if (ll && list.at(11).trimmed() == "W") {
|
2017-03-18 01:30:31 +01:00
|
|
|
lond = -lond;
|
2017-05-06 00:53:39 +02:00
|
|
|
lonm = -lonm;
|
|
|
|
}
|
2017-04-01 15:58:32 +02:00
|
|
|
|
2017-05-06 00:53:39 +02:00
|
|
|
setup.zone = list.at(13).trimmed().toInt(&res);
|
2017-04-01 15:58:32 +02:00
|
|
|
if (!res)
|
2017-05-06 00:53:39 +02:00
|
|
|
setup.zone = 0;
|
2017-04-01 15:58:32 +02:00
|
|
|
qreal ppx = list.at(14).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
|
|
|
pp = false;
|
|
|
|
qreal ppy = list.at(15).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
|
|
|
pp = false;
|
|
|
|
if (list.at(16).trimmed() == "S")
|
2017-05-06 00:53:39 +02:00
|
|
|
setup.zone = -setup.zone;
|
2017-04-01 15:58:32 +02:00
|
|
|
|
|
|
|
ReferencePoint p;
|
|
|
|
p.xy = QPoint(x, y);
|
|
|
|
if (ll) {
|
|
|
|
p.ll = Coordinates(lond + lonm/60.0, latd + latm/60.0);
|
2017-05-05 09:00:57 +02:00
|
|
|
if (p.ll.isValid())
|
|
|
|
points.append(p);
|
|
|
|
else
|
|
|
|
return ln;
|
2017-04-01 15:58:32 +02:00
|
|
|
} else if (pp) {
|
|
|
|
p.pp = QPointF(ppx, ppy);
|
|
|
|
points.append(p);
|
|
|
|
} else
|
|
|
|
return ln;
|
2017-03-20 22:52:39 +01:00
|
|
|
} else if (key == "IWH") {
|
2017-07-27 19:45:04 +02:00
|
|
|
if (list.count() < 4)
|
|
|
|
return ln;
|
2017-03-18 01:30:31 +01:00
|
|
|
int w = list.at(2).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
|
|
|
return ln;
|
|
|
|
int h = list.at(3).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
|
|
|
return ln;
|
|
|
|
_size = QSize(w, h);
|
2017-03-29 00:17:47 +02:00
|
|
|
} else if (key == "Map Projection") {
|
2017-07-27 19:45:04 +02:00
|
|
|
if (list.count() < 2)
|
|
|
|
return ln;
|
2017-03-29 00:17:47 +02:00
|
|
|
projection = list.at(1);
|
|
|
|
} else if (key == "Projection Setup") {
|
2017-04-01 15:58:32 +02:00
|
|
|
if (list.count() < 8)
|
2017-03-29 00:17:47 +02:00
|
|
|
return ln;
|
2017-04-09 10:26:09 +02:00
|
|
|
setup.latitudeOrigin = list.at(1).trimmed().toFloat(&res);
|
2017-04-02 22:17:16 +02:00
|
|
|
if (!res)
|
2017-04-09 10:26:09 +02:00
|
|
|
setup.latitudeOrigin = 0;
|
|
|
|
setup.longitudeOrigin = list.at(2).trimmed().toFloat(&res);
|
2017-04-01 15:58:32 +02:00
|
|
|
if (!res)
|
2017-04-09 10:26:09 +02:00
|
|
|
setup.longitudeOrigin = 0;
|
2017-04-01 15:58:32 +02:00
|
|
|
setup.scale = list.at(3).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
2017-04-02 22:17:16 +02:00
|
|
|
setup.scale = 1.0;
|
2017-04-01 15:58:32 +02:00
|
|
|
setup.falseEasting = list.at(4).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
|
|
|
setup.falseEasting = 0;
|
|
|
|
setup.falseNorthing = list.at(5).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
|
|
|
setup.falseNorthing = 0;
|
2017-04-02 22:17:16 +02:00
|
|
|
setup.standardParallel1 = list.at(6).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
|
|
|
setup.standardParallel1 = 0;
|
|
|
|
setup.standardParallel2 = list.at(7).trimmed().toFloat(&res);
|
|
|
|
if (!res)
|
|
|
|
setup.standardParallel2 = 0;
|
2017-03-18 01:30:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ln++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
bool OfflineMap::parseMapFile(QIODevice &device, QList<ReferencePoint> &points,
|
|
|
|
QString &projection, ProjectionSetup &setup, QString &datum)
|
|
|
|
{
|
|
|
|
int el;
|
|
|
|
|
|
|
|
if (!device.open(QIODevice::ReadOnly)) {
|
|
|
|
_errorString = QString("Error opening map file: %1")
|
|
|
|
.arg(device.errorString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((el = parse(device, points, projection, setup, datum))) {
|
|
|
|
_errorString = QString("Map file parse error on line %1").arg(el);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-03 20:29:35 +02:00
|
|
|
bool OfflineMap::createProjection(const QString &datum,
|
|
|
|
const QString &projection, const ProjectionSetup &setup,
|
|
|
|
QList<ReferencePoint> &points)
|
2017-03-29 00:17:47 +02:00
|
|
|
{
|
2017-04-01 16:55:46 +02:00
|
|
|
if (points.count() < 2) {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = "Insufficient number of reference points";
|
2017-04-01 16:55:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-17 18:03:04 +02:00
|
|
|
Datum d = Datum::datum(datum);
|
|
|
|
if (d.isNull()) {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = QString("%1: Unknown datum").arg(datum);
|
2017-04-17 18:03:04 +02:00
|
|
|
return false;
|
2017-04-09 10:26:09 +02:00
|
|
|
}
|
|
|
|
|
2017-05-05 09:00:57 +02:00
|
|
|
if (setup.latitudeOrigin < -90.0 || setup.latitudeOrigin > 90.0
|
|
|
|
|| setup.longitudeOrigin < -180.0 || setup.longitudeOrigin > 180.0
|
2017-07-12 21:23:56 +02:00
|
|
|
|| setup.standardParallel1 < -90.0 || setup.standardParallel1 > 90.0
|
|
|
|
|| setup.standardParallel2 < -90.0 || setup.standardParallel2 > 90.0) {
|
2017-05-05 09:00:57 +02:00
|
|
|
_errorString = "Invalid projection setup";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-01 15:58:32 +02:00
|
|
|
if (projection == "Mercator")
|
2017-03-29 00:17:47 +02:00
|
|
|
_projection = new Mercator();
|
2017-04-01 15:58:32 +02:00
|
|
|
else if (projection == "Transverse Mercator")
|
2017-04-17 18:03:04 +02:00
|
|
|
_projection = new TransverseMercator(d.ellipsoid(),
|
2017-05-08 19:53:50 +02:00
|
|
|
setup.latitudeOrigin, setup.longitudeOrigin, setup.scale,
|
|
|
|
setup.falseEasting, setup.falseNorthing);
|
2017-04-01 15:58:32 +02:00
|
|
|
else if (projection == "Latitude/Longitude")
|
2017-03-29 22:51:32 +02:00
|
|
|
_projection = new LatLon();
|
2017-04-02 22:17:16 +02:00
|
|
|
else if (projection == "Lambert Conformal Conic")
|
2017-04-17 18:03:04 +02:00
|
|
|
_projection = new LambertConic(d.ellipsoid(),
|
2017-04-09 10:26:09 +02:00
|
|
|
setup.standardParallel1, setup.standardParallel2,
|
|
|
|
setup.latitudeOrigin, setup.longitudeOrigin, setup.scale,
|
|
|
|
setup.falseEasting, setup.falseNorthing);
|
2017-05-04 20:25:47 +02:00
|
|
|
else if (projection == "Albers Equal Area")
|
|
|
|
_projection = new AlbersEqual(d.ellipsoid(), setup.standardParallel1,
|
|
|
|
setup.standardParallel2, setup.latitudeOrigin, setup.longitudeOrigin,
|
|
|
|
setup.falseEasting, setup.falseNorthing);
|
2017-04-01 16:55:46 +02:00
|
|
|
else if (projection == "(UTM) Universal Transverse Mercator") {
|
|
|
|
if (setup.zone)
|
2017-04-17 18:03:04 +02:00
|
|
|
_projection = new UTM(d.ellipsoid(), setup.zone);
|
2017-04-01 16:55:46 +02:00
|
|
|
else if (!points.first().ll.isNull())
|
2017-04-17 18:03:04 +02:00
|
|
|
_projection = new UTM(d.ellipsoid(), points.first().ll);
|
2017-04-01 16:55:46 +02:00
|
|
|
else {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = "Can not determine UTM zone";
|
2017-04-01 16:55:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-05-06 00:53:39 +02:00
|
|
|
} else if (projection == "(NZTM2) New Zealand TM 2000")
|
2017-05-08 19:53:50 +02:00
|
|
|
_projection = new TransverseMercator(d.ellipsoid(), 0, 173.0, 0.9996,
|
2017-05-06 00:53:39 +02:00
|
|
|
1600000, 10000000);
|
2017-05-08 20:17:14 +02:00
|
|
|
else if (projection == "(BNG) British National Grid")
|
|
|
|
_projection = new TransverseMercator(d.ellipsoid(), 49, -2, 0.999601,
|
|
|
|
400000, -100000);
|
|
|
|
else if (projection == "(IG) Irish Grid")
|
|
|
|
_projection = new TransverseMercator(d.ellipsoid(), 53.5, -8, 1.000035,
|
|
|
|
200000, 250000);
|
|
|
|
else if (projection == "(SG) Swedish Grid")
|
|
|
|
_projection = new TransverseMercator(d.ellipsoid(), 0, 15.808278, 1,
|
|
|
|
1500000, 0);
|
2017-05-06 00:53:39 +02:00
|
|
|
else {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = QString("%1: Unknown map projection").arg(projection);
|
2017-04-01 15:58:32 +02:00
|
|
|
return false;
|
2017-03-29 00:17:47 +02:00
|
|
|
}
|
|
|
|
|
2017-04-17 18:03:04 +02:00
|
|
|
for (int i = 0; i < points.size(); i++) {
|
2017-06-29 19:53:42 +02:00
|
|
|
if (points.at(i).ll.isNull())
|
|
|
|
points[i].ll = d.toWGS84(_projection->xy2ll(points.at(i).pp));
|
|
|
|
else
|
|
|
|
points[i].ll = d.toWGS84(points[i].ll);
|
2017-04-17 18:03:04 +02:00
|
|
|
}
|
2017-04-01 15:58:32 +02:00
|
|
|
|
|
|
|
return true;
|
2017-03-29 00:17:47 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 09:27:44 +01:00
|
|
|
bool OfflineMap::computeTransformation(const QList<ReferencePoint> &points)
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
2017-05-16 12:59:40 +02:00
|
|
|
Q_ASSERT(points.size() >= 2);
|
2017-03-18 01:30:31 +01:00
|
|
|
|
|
|
|
Matrix c(3, 2);
|
|
|
|
c.zeroize();
|
|
|
|
for (size_t j = 0; j < c.w(); j++) {
|
|
|
|
for (size_t k = 0; k < c.h(); k++) {
|
|
|
|
for (int i = 0; i < points.size(); i++) {
|
|
|
|
double f[3], t[2];
|
2017-04-01 15:58:32 +02:00
|
|
|
QPointF p = _projection->ll2xy(points.at(i).ll);
|
2017-03-18 01:30:31 +01:00
|
|
|
|
2017-03-20 22:52:39 +01:00
|
|
|
f[0] = p.x();
|
|
|
|
f[1] = p.y();
|
2017-03-18 01:30:31 +01:00
|
|
|
f[2] = 1.0;
|
2017-04-01 15:58:32 +02:00
|
|
|
t[0] = points.at(i).xy.x();
|
|
|
|
t[1] = points.at(i).xy.y();
|
2017-03-18 01:30:31 +01:00
|
|
|
c.m(k,j) += f[k] * t[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Matrix Q(3, 3);
|
|
|
|
Q.zeroize();
|
|
|
|
for (int qi = 0; qi < points.size(); qi++) {
|
|
|
|
double v[3];
|
2017-04-01 15:58:32 +02:00
|
|
|
QPointF p = _projection->ll2xy(points.at(qi).ll);
|
2017-03-18 01:30:31 +01:00
|
|
|
|
2017-03-20 22:52:39 +01:00
|
|
|
v[0] = p.x();
|
|
|
|
v[1] = p.y();
|
2017-03-18 01:30:31 +01:00
|
|
|
v[2] = 1.0;
|
|
|
|
for (size_t i = 0; i < Q.h(); i++)
|
|
|
|
for (size_t j = 0; j < Q.w(); j++)
|
|
|
|
Q.m(i,j) += v[i] * v[j];
|
|
|
|
}
|
|
|
|
|
|
|
|
Matrix M = Q.augemented(c);
|
2017-03-27 02:41:30 +02:00
|
|
|
if (!M.eliminate()) {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = "Singular transformation matrix";
|
2017-03-18 01:30:31 +01:00
|
|
|
return false;
|
2017-03-27 02:41:30 +02:00
|
|
|
}
|
2017-03-18 01:30:31 +01:00
|
|
|
|
2017-04-01 15:58:32 +02:00
|
|
|
_transform = QTransform(M.m(0,3), M.m(0,4), M.m(1,3), M.m(1,4), M.m(2,3),
|
|
|
|
M.m(2,4));
|
2017-04-04 22:03:42 +02:00
|
|
|
_inverted = _transform.inverted();
|
2017-03-18 01:30:31 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-21 09:27:44 +01:00
|
|
|
bool OfflineMap::computeResolution(QList<ReferencePoint> &points)
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
2017-03-27 02:41:30 +02:00
|
|
|
Q_ASSERT(points.count() >= 2);
|
2017-03-18 01:30:31 +01:00
|
|
|
|
|
|
|
int maxLon = 0, minLon = 0, maxLat = 0, minLat = 0;
|
|
|
|
qreal dLon, pLon, dLat, pLat;
|
|
|
|
|
|
|
|
for (int i = 1; i < points.size(); i++) {
|
2017-04-01 15:58:32 +02:00
|
|
|
if (points.at(i).ll.lon() < points.at(minLon).ll.lon())
|
2017-03-18 01:30:31 +01:00
|
|
|
minLon = i;
|
2017-04-01 15:58:32 +02:00
|
|
|
if (points.at(i).ll.lon() > points.at(maxLon).ll.lon())
|
2017-03-18 01:30:31 +01:00
|
|
|
maxLon = i;
|
2017-04-01 15:58:32 +02:00
|
|
|
if (points.at(i).ll.lat() < points.at(minLat).ll.lon())
|
2017-03-18 01:30:31 +01:00
|
|
|
minLat = i;
|
2017-04-01 15:58:32 +02:00
|
|
|
if (points.at(i).ll.lat() > points.at(maxLat).ll.lon())
|
2017-03-18 01:30:31 +01:00
|
|
|
maxLat = i;
|
|
|
|
}
|
|
|
|
|
2017-04-01 15:58:32 +02:00
|
|
|
dLon = points.at(minLon).ll.distanceTo(points.at(maxLon).ll);
|
|
|
|
pLon = QLineF(points.at(minLon).xy, points.at(maxLon).xy).length();
|
|
|
|
dLat = points.at(minLat).ll.distanceTo(points.at(maxLat).ll);
|
|
|
|
pLat = QLineF(points.at(minLat).xy, points.at(maxLat).xy).length();
|
2017-03-18 01:30:31 +01:00
|
|
|
|
|
|
|
_resolution = (dLon/pLon + dLat/pLat) / 2.0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-21 19:02:29 +01:00
|
|
|
bool OfflineMap::getImageInfo(const QString &path)
|
2017-03-20 10:05:07 +01:00
|
|
|
{
|
2017-03-21 19:02:29 +01:00
|
|
|
QFileInfo ii(_imgPath);
|
2017-04-13 20:00:27 +02:00
|
|
|
|
2017-03-21 19:02:29 +01:00
|
|
|
if (ii.isRelative())
|
2017-04-13 20:00:27 +02:00
|
|
|
ii.setFile(path + "/" + _imgPath);
|
|
|
|
|
|
|
|
if (!ii.exists()) {
|
|
|
|
int last = _imgPath.lastIndexOf('\\');
|
|
|
|
if (last >= 0 && last < _imgPath.length() - 1) {
|
|
|
|
QStringRef fn(&_imgPath, last + 1, _imgPath.length() - last - 1);
|
|
|
|
ii.setFile(path + "/" + fn.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ii.exists())
|
|
|
|
_imgPath = ii.absoluteFilePath();
|
|
|
|
else {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = QString("%1: No such image file").arg(_imgPath);
|
2017-04-13 20:00:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-03-21 19:02:29 +01:00
|
|
|
|
2017-07-29 22:19:03 +02:00
|
|
|
if (OZF::isOZF(_imgPath)) {
|
2017-08-11 00:22:21 +02:00
|
|
|
if (!_ozf.load(_imgPath)) {
|
|
|
|
_errorString = QString("%1: Error loading OZF file")
|
|
|
|
.arg(QFileInfo(_imgPath).fileName());
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-14 22:39:33 +02:00
|
|
|
} else {
|
|
|
|
QImageReader img(_imgPath);
|
|
|
|
_size = img.size();
|
2017-08-11 00:22:21 +02:00
|
|
|
if (!_size.isValid()) {
|
|
|
|
_errorString = QString("%1: Error reading map image")
|
|
|
|
.arg(QFileInfo(_imgPath).fileName());
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-20 10:05:07 +01:00
|
|
|
}
|
|
|
|
|
2017-03-21 01:15:29 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-27 10:31:41 +02:00
|
|
|
bool OfflineMap::getTileInfo(const QStringList &tiles, const QString &path)
|
2017-03-21 01:15:29 +01:00
|
|
|
{
|
2017-03-21 19:02:29 +01:00
|
|
|
QRegExp rx("_[0-9]+_[0-9]+\\.");
|
|
|
|
for (int i = 0; i < tiles.size(); i++) {
|
|
|
|
if (tiles.at(i).contains(rx)) {
|
|
|
|
_tileName = QString(tiles.at(i)).replace(rx, "_%1_%2.");
|
|
|
|
|
2017-03-27 02:41:30 +02:00
|
|
|
if (path.isNull()) {
|
2017-03-21 19:02:29 +01:00
|
|
|
QByteArray ba = _tar.file(tiles.at(i));
|
2017-03-21 20:51:23 +01:00
|
|
|
QBuffer buffer(&ba);
|
|
|
|
_tileSize = QImageReader(&buffer).size();
|
2017-03-21 19:02:29 +01:00
|
|
|
} else {
|
|
|
|
_tileName = path + "/" + _tileName;
|
2017-03-21 20:51:23 +01:00
|
|
|
_tileSize = QImageReader(path + "/" + tiles.at(i)).size();
|
2017-03-21 19:02:29 +01:00
|
|
|
}
|
2017-03-21 20:51:23 +01:00
|
|
|
if (!_tileSize.isValid()) {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = QString("Error retrieving tile size: "
|
|
|
|
"%1: Invalid image").arg(QFileInfo(tiles.at(i)).fileName());
|
2017-03-21 19:02:29 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-20 10:05:07 +01:00
|
|
|
}
|
2017-03-21 01:15:29 +01:00
|
|
|
|
2017-04-23 13:37:17 +02:00
|
|
|
_errorString = "Invalid/missing tile set";
|
2017-03-21 19:02:29 +01:00
|
|
|
return false;
|
2017-03-20 10:05:07 +01:00
|
|
|
}
|
|
|
|
|
2017-03-27 10:31:41 +02:00
|
|
|
bool OfflineMap::totalSizeSet()
|
|
|
|
{
|
|
|
|
if (!_size.isValid()) {
|
2017-04-21 21:15:58 +02:00
|
|
|
_errorString = "Missing total image size (IWH)";
|
2017-03-27 10:31:41 +02:00
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
OfflineMap::OfflineMap(const QString &fileName, QObject *parent)
|
|
|
|
: Map(parent)
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
|
|
|
QList<ReferencePoint> points;
|
2017-04-03 20:29:35 +02:00
|
|
|
QString proj, datum;
|
2017-04-01 15:58:32 +02:00
|
|
|
ProjectionSetup setup;
|
2017-04-21 21:15:58 +02:00
|
|
|
QFileInfo fi(fileName);
|
2017-04-23 12:26:01 +02:00
|
|
|
QString suffix = fi.suffix().toLower();
|
2017-03-29 00:17:47 +02:00
|
|
|
|
2017-03-18 01:30:31 +01:00
|
|
|
|
|
|
|
_valid = false;
|
2017-03-29 00:17:47 +02:00
|
|
|
_img = 0;
|
|
|
|
_projection = 0;
|
2017-07-12 22:07:18 +02:00
|
|
|
_resolution = 0.0;
|
2017-08-11 00:22:21 +02:00
|
|
|
_zoom = 0;
|
|
|
|
_scale = QPointF(1.0, 1.0);
|
2017-03-18 01:30:31 +01:00
|
|
|
|
2017-04-23 12:26:01 +02:00
|
|
|
if (suffix == "tar") {
|
2017-04-21 21:15:58 +02:00
|
|
|
if (!_tar.load(fileName)) {
|
|
|
|
_errorString = "Error reading tar file";
|
|
|
|
return;
|
2017-03-21 01:15:29 +01:00
|
|
|
}
|
2017-04-21 21:15:58 +02:00
|
|
|
|
|
|
|
QString mapFileName = fi.completeBaseName() + ".map";
|
|
|
|
QByteArray ba = _tar.file(mapFileName);
|
|
|
|
if (ba.isNull()) {
|
|
|
|
_errorString = "Map file not found";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QBuffer mapFile(&ba);
|
|
|
|
if (!parseMapFile(mapFile, points, proj, setup, datum))
|
|
|
|
return;
|
2017-04-23 12:26:01 +02:00
|
|
|
} else if (suffix =="map") {
|
2017-04-21 21:15:58 +02:00
|
|
|
QFile mapFile(fileName);
|
|
|
|
if (!parseMapFile(mapFile, points, proj, setup, datum))
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
_errorString = "Not a map file";
|
2017-03-18 01:30:31 +01:00
|
|
|
return;
|
2017-04-21 21:15:58 +02:00
|
|
|
}
|
2017-03-18 01:30:31 +01:00
|
|
|
|
2017-04-03 20:29:35 +02:00
|
|
|
if (!createProjection(datum, proj, setup, points))
|
2017-03-29 00:17:47 +02:00
|
|
|
return;
|
2017-03-27 02:41:30 +02:00
|
|
|
if (!computeTransformation(points))
|
2017-03-18 01:30:31 +01:00
|
|
|
return;
|
|
|
|
computeResolution(points);
|
|
|
|
|
2017-03-21 01:15:29 +01:00
|
|
|
if (_tar.isOpen()) {
|
2017-03-27 10:31:41 +02:00
|
|
|
if (!totalSizeSet())
|
|
|
|
return;
|
2017-03-21 19:02:29 +01:00
|
|
|
if (!getTileInfo(_tar.files()))
|
2017-03-20 10:05:07 +01:00
|
|
|
return;
|
2017-04-21 21:15:58 +02:00
|
|
|
_imgPath = QString();
|
2017-03-21 01:15:29 +01:00
|
|
|
} else {
|
2017-04-21 21:15:58 +02:00
|
|
|
QDir set(fi.absolutePath() + "/" + "set");
|
2017-03-21 01:15:29 +01:00
|
|
|
if (set.exists()) {
|
2017-03-27 10:31:41 +02:00
|
|
|
if (!totalSizeSet())
|
|
|
|
return;
|
2017-04-21 21:15:58 +02:00
|
|
|
if (!getTileInfo(set.entryList(), set.absolutePath()))
|
2017-03-21 01:15:29 +01:00
|
|
|
return;
|
2017-03-27 02:41:30 +02:00
|
|
|
_imgPath = QString();
|
2017-03-21 01:15:29 +01:00
|
|
|
} else {
|
2017-04-21 21:15:58 +02:00
|
|
|
if (!getImageInfo(fi.absolutePath()))
|
2017-03-21 01:15:29 +01:00
|
|
|
return;
|
2017-03-20 10:05:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-18 01:30:31 +01:00
|
|
|
_valid = true;
|
|
|
|
}
|
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent)
|
2017-03-27 02:41:30 +02:00
|
|
|
: Map(parent)
|
|
|
|
{
|
|
|
|
QList<ReferencePoint> points;
|
2017-04-03 20:29:35 +02:00
|
|
|
QString proj, datum;
|
2017-04-01 15:58:32 +02:00
|
|
|
ProjectionSetup setup;
|
2017-04-21 21:15:58 +02:00
|
|
|
QFileInfo fi(fileName);
|
2017-03-27 02:41:30 +02:00
|
|
|
|
|
|
|
_valid = false;
|
2017-03-29 00:17:47 +02:00
|
|
|
_img = 0;
|
|
|
|
_projection = 0;
|
2017-07-12 22:07:18 +02:00
|
|
|
_resolution = 0.0;
|
2017-08-11 00:22:21 +02:00
|
|
|
_zoom = 0;
|
|
|
|
_scale = QPointF(1.0, 1.0);
|
2017-03-27 02:41:30 +02:00
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
QFileInfo map(fi.absolutePath());
|
|
|
|
QFileInfo layer(map.absolutePath());
|
|
|
|
QString mapFile = layer.fileName() + "/" + map.fileName() + "/"
|
|
|
|
+ fi.fileName();
|
|
|
|
QByteArray ba = tar.file(mapFile);
|
|
|
|
if (ba.isNull()) {
|
|
|
|
_errorString = "Map file not found";
|
2017-03-27 02:41:30 +02:00
|
|
|
return;
|
2017-04-21 21:15:58 +02:00
|
|
|
}
|
|
|
|
QBuffer buffer(&ba);
|
2017-03-29 00:17:47 +02:00
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
if (!parseMapFile(buffer, points, proj, setup, datum))
|
|
|
|
return;
|
2017-04-03 20:29:35 +02:00
|
|
|
if (!createProjection(datum, proj, setup, points))
|
2017-03-29 00:17:47 +02:00
|
|
|
return;
|
2017-03-27 10:31:41 +02:00
|
|
|
if (!totalSizeSet())
|
|
|
|
return;
|
2017-03-27 02:41:30 +02:00
|
|
|
if (!computeTransformation(points))
|
|
|
|
return;
|
|
|
|
computeResolution(points);
|
|
|
|
|
|
|
|
|
2017-04-21 21:15:58 +02:00
|
|
|
_tarPath = fi.absolutePath() + "/" + fi.completeBaseName() + ".tar";
|
2017-03-27 02:41:30 +02:00
|
|
|
_imgPath = QString();
|
|
|
|
_valid = true;
|
|
|
|
}
|
|
|
|
|
2017-03-29 00:17:47 +02:00
|
|
|
OfflineMap::~OfflineMap()
|
|
|
|
{
|
|
|
|
if (_img)
|
|
|
|
delete _img;
|
|
|
|
if (_projection)
|
|
|
|
delete _projection;
|
|
|
|
}
|
|
|
|
|
2017-03-21 09:27:44 +01:00
|
|
|
void OfflineMap::load()
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
2017-03-27 02:41:30 +02:00
|
|
|
if (!_tarPath.isNull() && !_tileSize.isValid()) {
|
|
|
|
if (!_tar.load(_tarPath)) {
|
2017-04-21 21:15:58 +02:00
|
|
|
qWarning("%s: error loading tar file", qPrintable(_tarPath));
|
2017-03-27 02:41:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-04-23 13:37:17 +02:00
|
|
|
if (!getTileInfo(_tar.files()))
|
|
|
|
qWarning("%s: %s", qPrintable(_tarPath), qPrintable(_errorString));
|
2017-03-20 10:05:07 +01:00
|
|
|
return;
|
2017-03-27 02:41:30 +02:00
|
|
|
}
|
2017-03-18 01:30:31 +01:00
|
|
|
|
2017-04-14 22:39:33 +02:00
|
|
|
if (!_img && !_imgPath.isNull() && !_ozf.isOpen()) {
|
2017-03-27 02:41:30 +02:00
|
|
|
_img = new QImage(_imgPath);
|
|
|
|
if (_img->isNull())
|
|
|
|
qWarning("%s: error loading map image", qPrintable(_imgPath));
|
|
|
|
}
|
2017-03-18 01:30:31 +01:00
|
|
|
}
|
|
|
|
|
2017-03-21 09:27:44 +01:00
|
|
|
void OfflineMap::unload()
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
2017-03-27 02:41:30 +02:00
|
|
|
if (_img) {
|
2017-03-20 10:05:07 +01:00
|
|
|
delete _img;
|
2017-03-27 02:41:30 +02:00
|
|
|
_img = 0;
|
|
|
|
}
|
2017-03-18 01:30:31 +01:00
|
|
|
}
|
|
|
|
|
2017-04-15 08:59:31 +02:00
|
|
|
void OfflineMap::drawTiled(QPainter *painter, const QRectF &rect)
|
2017-03-18 01:30:31 +01:00
|
|
|
{
|
2017-04-15 08:59:31 +02:00
|
|
|
QPoint tl = QPoint((int)floor(rect.left() / (qreal)_tileSize.width())
|
|
|
|
* _tileSize.width(), (int)floor(rect.top() / _tileSize.height())
|
|
|
|
* _tileSize.height());
|
|
|
|
|
|
|
|
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
|
|
|
|
for (int i = 0; i < ceil(s.width() / _tileSize.width()); i++) {
|
|
|
|
for (int j = 0; j < ceil(s.height() / _tileSize.height()); j++) {
|
|
|
|
int x = tl.x() + i * _tileSize.width();
|
|
|
|
int y = tl.y() + j * _tileSize.height();
|
|
|
|
|
2017-08-11 00:22:21 +02:00
|
|
|
if (!QRectF(QPointF(x, y), _tileSize).intersects(bounds())) {
|
2017-04-15 08:59:31 +02:00
|
|
|
painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white);
|
|
|
|
continue;
|
2017-03-20 10:05:07 +01:00
|
|
|
}
|
2017-04-15 08:59:31 +02:00
|
|
|
|
|
|
|
QString tileName(_tileName.arg(QString::number(x),
|
|
|
|
QString::number(y)));
|
|
|
|
QPixmap pixmap;
|
|
|
|
|
|
|
|
if (_tar.isOpen()) {
|
|
|
|
QString key = _tar.fileName() + "/" + tileName;
|
2017-04-14 22:39:33 +02:00
|
|
|
if (!QPixmapCache::find(key, &pixmap)) {
|
2017-04-15 08:59:31 +02:00
|
|
|
QByteArray ba = _tar.file(tileName);
|
|
|
|
pixmap = QPixmap::fromImage(QImage::fromData(ba));
|
2017-04-14 22:39:33 +02:00
|
|
|
if (!pixmap.isNull())
|
|
|
|
QPixmapCache::insert(key, pixmap);
|
|
|
|
}
|
2017-04-15 08:59:31 +02:00
|
|
|
} else
|
|
|
|
pixmap = QPixmap(tileName);
|
|
|
|
|
|
|
|
if (pixmap.isNull()) {
|
|
|
|
qWarning("%s: error loading tile image", qPrintable(
|
|
|
|
_tileName.arg(QString::number(x), QString::number(y))));
|
|
|
|
painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white);
|
|
|
|
} else
|
2017-04-14 22:39:33 +02:00
|
|
|
painter->drawPixmap(QPoint(x, y), pixmap);
|
|
|
|
}
|
2017-04-15 08:59:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OfflineMap::drawOZF(QPainter *painter, const QRectF &rect)
|
|
|
|
{
|
|
|
|
QPoint tl = QPoint((int)floor(rect.left() / _ozf.tileSize().width())
|
|
|
|
* _ozf.tileSize().width(), (int)floor(rect.top()
|
|
|
|
/ _ozf.tileSize().height()) * _ozf.tileSize().height());
|
|
|
|
|
|
|
|
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
|
|
|
|
for (int i = 0; i < ceil(s.width() / _ozf.tileSize().width()); i++) {
|
|
|
|
for (int j = 0; j < ceil(s.height() / _ozf.tileSize().height()); j++) {
|
|
|
|
int x = tl.x() + i * _ozf.tileSize().width();
|
|
|
|
int y = tl.y() + j * _ozf.tileSize().height();
|
|
|
|
|
|
|
|
if (!QRectF(QPointF(x, y), _ozf.tileSize()).intersects(bounds())) {
|
2017-08-11 00:22:21 +02:00
|
|
|
painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()),
|
|
|
|
Qt::white);
|
2017-04-15 08:59:31 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPixmap pixmap;
|
2017-08-11 00:22:21 +02:00
|
|
|
QString key = _ozf.fileName() + "/" + QString::number(_zoom) + "_"
|
|
|
|
+ QString::number(x) + "_" + QString::number(y);
|
2017-04-15 08:59:31 +02:00
|
|
|
if (!QPixmapCache::find(key, &pixmap)) {
|
2017-08-11 00:22:21 +02:00
|
|
|
pixmap = _ozf.tile(_zoom, x, y);
|
2017-04-15 08:59:31 +02:00
|
|
|
if (!pixmap.isNull())
|
|
|
|
QPixmapCache::insert(key, pixmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pixmap.isNull()) {
|
|
|
|
qWarning("%s: error loading tile image", qPrintable(key));
|
2017-08-11 00:22:21 +02:00
|
|
|
painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()),
|
|
|
|
Qt::white);
|
2017-04-15 08:59:31 +02:00
|
|
|
} else
|
|
|
|
painter->drawPixmap(QPoint(x, y), pixmap);
|
2017-03-20 10:05:07 +01:00
|
|
|
}
|
|
|
|
}
|
2017-03-18 01:30:31 +01:00
|
|
|
}
|
2017-04-15 08:59:31 +02:00
|
|
|
|
|
|
|
void OfflineMap::drawImage(QPainter *painter, const QRectF &rect)
|
|
|
|
{
|
|
|
|
if (!_img || _img->isNull())
|
|
|
|
painter->fillRect(rect, Qt::white);
|
|
|
|
else {
|
2017-08-09 20:25:02 +02:00
|
|
|
QRect r(rect.toRect());
|
2017-08-09 20:29:49 +02:00
|
|
|
painter->drawImage(r.left(), r.top(), *_img, r.left(), r.top(),
|
2017-08-09 17:58:37 +02:00
|
|
|
r.width(), r.height());
|
2017-04-15 08:59:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OfflineMap::draw(QPainter *painter, const QRectF &rect)
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen())
|
|
|
|
drawOZF(painter, rect);
|
|
|
|
else if (_tileSize.isValid())
|
|
|
|
drawTiled(painter, rect);
|
|
|
|
else
|
|
|
|
drawImage(painter, rect);
|
|
|
|
}
|
2017-08-11 00:22:21 +02:00
|
|
|
|
|
|
|
QPointF OfflineMap::ll2xy(const Coordinates &c)
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen()) {
|
|
|
|
QPointF p(_transform.map(_projection->ll2xy(c)));
|
|
|
|
return QPointF(p.x() * _scale.x(), p.y() * _scale.y());
|
|
|
|
} else
|
|
|
|
return _transform.map(_projection->ll2xy(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
Coordinates OfflineMap::xy2ll(const QPointF &p)
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen()) {
|
|
|
|
return _projection->xy2ll(_inverted.map(QPointF(p.x() / _scale.x(),
|
|
|
|
p.y() / _scale.y())));
|
|
|
|
} else
|
|
|
|
return _projection->xy2ll(_inverted.map(p));
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF OfflineMap::bounds() const
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen())
|
|
|
|
return QRectF(QPointF(0, 0), _ozf.size(_zoom));
|
|
|
|
else
|
|
|
|
return QRectF(QPointF(0, 0), _size);
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal OfflineMap::resolution(const QPointF &p) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(p);
|
|
|
|
|
|
|
|
if (_ozf.isOpen())
|
|
|
|
return _resolution / ((_scale.x() + _scale.y()) / 2.0);
|
|
|
|
else
|
|
|
|
return _resolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal OfflineMap::zoomFit(const QSize &size, const RectC &br)
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen()) {
|
|
|
|
if (!br.isValid())
|
|
|
|
rescale(0);
|
|
|
|
else {
|
|
|
|
QRect sbr(QRectF(_transform.map(_projection->ll2xy(br.topLeft())),
|
|
|
|
_transform.map(_projection->ll2xy(br.bottomRight())))
|
|
|
|
.toRect().normalized());
|
|
|
|
|
|
|
|
for (int i = 0; i < _ozf.zooms(); i++) {
|
|
|
|
rescale(i);
|
|
|
|
if (sbr.size().width() * _scale.x() <= size.width()
|
|
|
|
&& sbr.size().height() * _scale.y() <= size.height())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal OfflineMap::zoomFit(qreal resolution, const Coordinates &c)
|
|
|
|
{
|
|
|
|
Q_UNUSED(c);
|
|
|
|
|
|
|
|
if (_ozf.isOpen()) {
|
|
|
|
for (int i = 0; i < _ozf.zooms(); i++) {
|
|
|
|
rescale(i);
|
|
|
|
qreal sr = _resolution / ((_scale.x() + _scale.y()) / 2.0);
|
|
|
|
if (sr >= resolution)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal OfflineMap::zoomIn()
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen())
|
|
|
|
rescale(qMax(_zoom - 1, 0));
|
|
|
|
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal OfflineMap::zoomOut()
|
|
|
|
{
|
|
|
|
if (_ozf.isOpen())
|
|
|
|
rescale(qMin(_zoom + 1, _ozf.zooms() - 1));
|
|
|
|
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OfflineMap::rescale(int zoom)
|
|
|
|
{
|
|
|
|
_zoom = zoom;
|
|
|
|
_scale = QPointF(
|
|
|
|
(qreal)_ozf.size(_zoom).width() / (qreal)_ozf.size(0).width(),
|
|
|
|
(qreal)_ozf.size(_zoom).height() / (qreal)_ozf.size(0).height());
|
|
|
|
}
|