2018-03-10 09:25:52 +01:00
|
|
|
#include <QIODevice>
|
2018-01-08 23:47:45 +01:00
|
|
|
#include "utm.h"
|
2018-01-20 20:13:56 +01:00
|
|
|
#include "gcs.h"
|
2018-04-05 20:38:23 +02:00
|
|
|
#include "pcs.h"
|
2018-01-08 23:47:45 +01:00
|
|
|
#include "mapfile.h"
|
|
|
|
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
static double parameter(const QString &str, bool *res, double dflt = 0.0)
|
2018-01-08 23:47:45 +01:00
|
|
|
{
|
|
|
|
QString field = str.trimmed();
|
|
|
|
if (field.isEmpty()) {
|
|
|
|
*res = true;
|
2019-03-07 01:08:51 +01:00
|
|
|
return dflt;
|
2018-01-08 23:47:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return field.toDouble(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
int MapFile::parse(QIODevice &device, QList<CalibrationPoint> &points,
|
|
|
|
QString &projection, Projection::Setup &setup, QString &datum)
|
|
|
|
{
|
2019-03-07 01:08:51 +01:00
|
|
|
bool res, utm = false;
|
|
|
|
int ln = 1, zone = 0;
|
|
|
|
|
2018-01-08 23:47:45 +01:00
|
|
|
|
|
|
|
while (!device.atEnd()) {
|
|
|
|
QByteArray line = device.readLine();
|
|
|
|
|
|
|
|
if (ln == 1) {
|
2018-06-02 16:41:27 +02:00
|
|
|
QString fileType(QString::fromUtf8(line).trimmed());
|
|
|
|
if (!fileType.startsWith("OziExplorer Map Data File"))
|
2018-01-08 23:47:45 +01:00
|
|
|
return ln;
|
|
|
|
} else if (ln == 2)
|
|
|
|
_name = line.trimmed();
|
|
|
|
else if (ln == 3)
|
|
|
|
_image = line.trimmed();
|
|
|
|
else if (ln == 5)
|
|
|
|
datum = line.split(',').at(0).trimmed();
|
|
|
|
else {
|
|
|
|
QList<QByteArray> list = line.split(',');
|
|
|
|
QString key(list.at(0).trimmed());
|
|
|
|
|
|
|
|
if (key.startsWith("Point") && list.count() == 17
|
|
|
|
&& !list.at(2).trimmed().isEmpty()) {
|
2019-03-07 01:08:51 +01:00
|
|
|
PointD xy;
|
|
|
|
xy.rx() = list.at(2).trimmed().toInt(&res);
|
2018-01-08 23:47:45 +01:00
|
|
|
if (!res)
|
|
|
|
return ln;
|
2019-03-07 01:08:51 +01:00
|
|
|
xy.ry() = list.at(3).trimmed().toInt(&res);
|
2018-01-08 23:47:45 +01:00
|
|
|
if (!res)
|
|
|
|
return ln;
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
Coordinates c;
|
|
|
|
bool ll = true;
|
2018-01-08 23:47:45 +01:00
|
|
|
int latd = list.at(6).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
|
|
|
ll = false;
|
2018-04-13 21:14:12 +02:00
|
|
|
double latm = list.at(7).trimmed().toDouble(&res);
|
2018-01-08 23:47:45 +01:00
|
|
|
if (!res)
|
|
|
|
ll = false;
|
|
|
|
int lond = list.at(9).trimmed().toInt(&res);
|
|
|
|
if (!res)
|
|
|
|
ll = false;
|
2018-04-13 21:14:12 +02:00
|
|
|
double lonm = list.at(10).trimmed().toDouble(&res);
|
2018-01-08 23:47:45 +01:00
|
|
|
if (!res)
|
|
|
|
ll = false;
|
|
|
|
if (ll && list.at(8).trimmed() == "S") {
|
|
|
|
latd = -latd;
|
|
|
|
latm = -latm;
|
|
|
|
}
|
|
|
|
if (ll && list.at(11).trimmed() == "W") {
|
|
|
|
lond = -lond;
|
|
|
|
lonm = -lonm;
|
|
|
|
}
|
2019-03-07 01:08:51 +01:00
|
|
|
if (ll)
|
|
|
|
c = Coordinates(lond + lonm/60.0, latd + latm/60.0);
|
2018-01-08 23:47:45 +01:00
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
PointD pp;
|
2018-04-13 21:14:12 +02:00
|
|
|
double ppx = list.at(14).trimmed().toDouble(&res);
|
2019-03-07 01:08:51 +01:00
|
|
|
if (res)
|
|
|
|
pp.rx() = ppx;
|
2018-04-13 21:14:12 +02:00
|
|
|
double ppy = list.at(15).trimmed().toDouble(&res);
|
2019-03-07 01:08:51 +01:00
|
|
|
if (res)
|
|
|
|
pp.ry() = ppy;
|
|
|
|
|
|
|
|
if (c.isValid())
|
|
|
|
points.append(CalibrationPoint(xy, c));
|
|
|
|
else if (pp.isValid())
|
|
|
|
points.append(CalibrationPoint(xy, pp));
|
|
|
|
else
|
2018-01-08 23:47:45 +01:00
|
|
|
return ln;
|
2019-03-07 01:08:51 +01:00
|
|
|
|
|
|
|
if (utm && !zone) {
|
|
|
|
zone = list.at(13).trimmed().toInt(&res);
|
|
|
|
if (res) {
|
|
|
|
if (list.at(16).trimmed() == "S")
|
|
|
|
zone = -zone;
|
|
|
|
} else {
|
|
|
|
if (c.isValid())
|
|
|
|
zone = UTM::zone(c);
|
|
|
|
}
|
|
|
|
}
|
2018-01-08 23:47:45 +01:00
|
|
|
} else if (key == "IWH") {
|
|
|
|
if (list.count() < 4)
|
|
|
|
return ln;
|
|
|
|
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);
|
|
|
|
} else if (key == "Map Projection") {
|
|
|
|
if (list.count() < 2)
|
|
|
|
return ln;
|
|
|
|
projection = list.at(1);
|
2019-03-07 01:08:51 +01:00
|
|
|
utm = (projection == "(UTM) Universal Transverse Mercator");
|
2018-01-08 23:47:45 +01:00
|
|
|
} else if (key == "Projection Setup") {
|
|
|
|
if (list.count() < 8)
|
|
|
|
return ln;
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
if (utm && zone)
|
|
|
|
setup = UTM::setup(zone);
|
|
|
|
else {
|
|
|
|
bool r[8];
|
|
|
|
setup = Projection::Setup(
|
|
|
|
parameter(list[1], &r[1]), parameter(list[2], &r[2]),
|
|
|
|
parameter(list[3], &r[3], 1.0), parameter(list[4], &r[4]),
|
|
|
|
parameter(list[5], &r[5]), parameter(list[6], &r[6]),
|
|
|
|
parameter(list[7], &r[7]));
|
|
|
|
|
|
|
|
for (int i = 1; i < 8; i++)
|
|
|
|
if (!r[i])
|
|
|
|
return ln;
|
|
|
|
}
|
2018-01-08 23:47:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ln++;
|
|
|
|
}
|
|
|
|
|
2018-03-21 19:09:37 +01:00
|
|
|
return (ln < 9) ? ln : 0;
|
2018-01-08 23:47:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool MapFile::parseMapFile(QIODevice &device, QList<CalibrationPoint> &points,
|
|
|
|
QString &projection, Projection::Setup &setup, QString &datum)
|
|
|
|
{
|
|
|
|
int el;
|
|
|
|
|
|
|
|
if (!device.open(QIODevice::ReadOnly)) {
|
2018-01-28 22:56:08 +01:00
|
|
|
_errorString = QString("Error opening file: %1")
|
2018-01-08 23:47:45 +01:00
|
|
|
.arg(device.errorString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
if ((el = parse(device, points, projection, setup, datum)))
|
2018-01-28 22:56:08 +01:00
|
|
|
_errorString = QString("Parse error on line %1").arg(el);
|
2018-01-08 23:47:45 +01:00
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
device.close();
|
|
|
|
|
|
|
|
return (!el);
|
2018-01-08 23:47:45 +01:00
|
|
|
}
|
|
|
|
|
2018-01-25 00:19:11 +01:00
|
|
|
const GCS *MapFile::createGCS(const QString &datum)
|
2018-01-08 23:47:45 +01:00
|
|
|
{
|
2018-01-25 00:19:11 +01:00
|
|
|
const GCS *gcs;
|
|
|
|
|
|
|
|
if (!(gcs = GCS::gcs(datum)))
|
2018-01-08 23:47:45 +01:00
|
|
|
_errorString = QString("%1: Unknown datum").arg(datum);
|
|
|
|
|
2018-01-25 00:19:11 +01:00
|
|
|
return gcs;
|
2018-01-08 23:47:45 +01:00
|
|
|
}
|
|
|
|
|
2018-01-25 00:19:11 +01:00
|
|
|
bool MapFile::createProjection(const GCS *gcs, const QString &name,
|
2019-03-07 01:08:51 +01:00
|
|
|
const Projection::Setup &setup)
|
2018-01-08 23:47:45 +01:00
|
|
|
{
|
2018-04-05 20:38:23 +02:00
|
|
|
PCS pcs;
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
if (name == "Latitude/Longitude") {
|
2018-01-25 00:19:11 +01:00
|
|
|
_projection = Projection(gcs);
|
2018-04-05 20:38:23 +02:00
|
|
|
return true;
|
2019-03-07 01:08:51 +01:00
|
|
|
} else if (name == "Mercator")
|
|
|
|
pcs = PCS(gcs, 1024, setup, 9001);
|
|
|
|
else if (name == "Transverse Mercator"
|
|
|
|
|| name == "(UTM) Universal Transverse Mercator")
|
|
|
|
pcs = PCS(gcs, 9807, setup, 9001);
|
|
|
|
else if (name == "Lambert Conformal Conic")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, setup, 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "Albers Equal Area")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9822, setup, 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(A)Lambert Azimuthual Equal Area")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9820, setup, 9001);
|
2019-03-07 01:08:51 +01:00
|
|
|
else if (name == "(NZTM2) New Zealand TM 2000")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9807, Projection::Setup(0, 173.0, 0.9996, 1600000,
|
|
|
|
10000000, NAN, NAN), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(BNG) British National Grid")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9807, Projection::Setup(49, -2, 0.999601, 400000,
|
|
|
|
-100000, NAN, NAN), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(IG) Irish Grid")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9807, Projection::Setup(53.5, -8, 1.000035, 200000,
|
|
|
|
250000, NAN, NAN), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(SG) Swedish Grid")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9807, Projection::Setup(0, 15.808278, 1, 1500000, 0, NAN,
|
|
|
|
NAN), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(I) France Zone I")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, Projection::Setup(49.5, 2.337229, NAN, 600000,
|
|
|
|
1200000, 48.598523, 50.395912), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(II) France Zone II")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, Projection::Setup(46.8, 2.337229, NAN, 600000,
|
|
|
|
2200000, 45.898919, 47.696014), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(III) France Zone III")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, Projection::Setup(44.1, 2.337229, NAN, 600000,
|
|
|
|
3200000, 43.199291, 44.996094), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(IV) France Zone IV")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, Projection::Setup(42.165, 2.337229, NAN, 234.358,
|
|
|
|
4185861.369, 41.560388, 42.767663), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(VICGRID) Victoria Australia")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, Projection::Setup(-37, 145, NAN, 2500000, 4500000,
|
|
|
|
-36, -38), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else if (name == "(VG94) VICGRID94 Victoria Australia")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9802, Projection::Setup(-37, 145, NAN, 2500000, 2500000,
|
|
|
|
-36, -38), 9001);
|
2018-03-19 19:56:31 +01:00
|
|
|
else if (name == "(SUI) Swiss Grid")
|
2018-04-05 20:38:23 +02:00
|
|
|
pcs = PCS(gcs, 9815, Projection::Setup(46.570866, 7.26225, 1.0, 600000,
|
|
|
|
200000, 90.0, 90.0), 9001);
|
2018-01-08 23:47:45 +01:00
|
|
|
else {
|
|
|
|
_errorString = QString("%1: Unknown map projection").arg(name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-05 20:38:23 +02:00
|
|
|
_projection = Projection(&pcs);
|
|
|
|
|
2018-01-08 23:47:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
bool MapFile::computeTransformation(const QList<CalibrationPoint> &points)
|
2018-01-08 23:47:45 +01:00
|
|
|
{
|
|
|
|
QList<ReferencePoint> rp;
|
|
|
|
|
2019-03-07 01:08:51 +01:00
|
|
|
for (int i = 0; i < points.size(); i++)
|
|
|
|
rp.append(points.at(i).rp(_projection));
|
2018-01-08 23:47:45 +01:00
|
|
|
|
2018-03-22 20:00:30 +01:00
|
|
|
_transform = Transform(rp);
|
|
|
|
if (!_transform.isValid()) {
|
|
|
|
_errorString = _transform.errorString();
|
2018-01-08 23:47:45 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-21 19:09:37 +01:00
|
|
|
MapFile::MapFile(QIODevice &file)
|
2018-01-08 23:47:45 +01:00
|
|
|
{
|
|
|
|
QList<CalibrationPoint> points;
|
2018-01-25 00:19:11 +01:00
|
|
|
QString ct, datum;
|
2018-01-08 23:47:45 +01:00
|
|
|
Projection::Setup setup;
|
2018-01-25 00:19:11 +01:00
|
|
|
const GCS *gcs;
|
2018-01-08 23:47:45 +01:00
|
|
|
|
2018-01-25 00:19:11 +01:00
|
|
|
if (!parseMapFile(file, points, ct, setup, datum))
|
2018-03-21 19:09:37 +01:00
|
|
|
return;
|
2018-01-25 00:19:11 +01:00
|
|
|
if (!(gcs = createGCS(datum)))
|
2018-03-21 19:09:37 +01:00
|
|
|
return;
|
2019-03-07 01:08:51 +01:00
|
|
|
if (!createProjection(gcs, ct, setup))
|
2018-03-21 19:09:37 +01:00
|
|
|
return;
|
2018-01-08 23:47:45 +01:00
|
|
|
if (!computeTransformation(points))
|
2018-03-22 20:00:30 +01:00
|
|
|
return;
|
2018-01-08 23:47:45 +01:00
|
|
|
}
|