1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-06 06:43:22 +02:00

Redesigned CRS logic (including CSV files structure)

This commit is contained in:
Martin Tůma 2023-04-13 08:39:33 +02:00
parent aec052edaf
commit e5199f8648
33 changed files with 5938 additions and 4066 deletions

File diff suppressed because it is too large Load Diff

1706
data/csv/projections.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,7 @@ HEADERS += src/common/config.h \
src/map/ENC/style.h \
src/map/IMG/section.h \
src/map/IMG/zoom.h \
src/map/conversion.h \
src/map/encmap.h \
src/map/ENC/iso8211.h \
src/map/gemfmap.h \
@ -335,6 +336,7 @@ SOURCES += src/main.cpp \
src/map/ENC/mapdata.cpp \
src/map/ENC/rastertile.cpp \
src/map/ENC/style.cpp \
src/map/conversion.cpp \
src/map/encmap.cpp \
src/map/ENC/iso8211.cpp \
src/map/gemfmap.cpp \

View File

@ -17,6 +17,7 @@
#include "common/downloader.h"
#include "map/ellipsoid.h"
#include "map/gcs.h"
#include "map/conversion.h"
#include "map/pcs.h"
#include "data/dem.h"
#include "data/waypoint.h"
@ -172,11 +173,21 @@ void App::loadDatums()
void App::loadPCSs()
{
QString projectionsFile(ProgramPaths::projectionsFile());
QString pcsFile(ProgramPaths::pcsFile());
if (!QFileInfo::exists(projectionsFile)) {
qWarning("No projections file found.");
projectionsFile = QString();
}
if (!QFileInfo::exists(pcsFile)) {
qWarning("No PCS file found.");
qWarning("Maps based on a projection different from EPSG:3857 won't work.");
} else
pcsFile = QString();
}
if (!projectionsFile.isNull() && !pcsFile.isNull()) {
Conversion::loadList(projectionsFile);
PCS::loadList(pcsFile);
} else
qWarning("Maps based on a projection different from EPSG:3857 won't work.");
}

View File

@ -2745,7 +2745,8 @@ void GUI::loadOptions()
_mapView->useOpenGL(true);
_mapView->setDevicePixelRatio(devicePixelRatioF(),
_options.hidpiMap ? devicePixelRatioF() : 1.0);
_mapView->setOutputProjection(CRS::projection(_options.outputProjection));
_mapView->setOutputProjection(CRS::projection(4326,
_options.outputProjection));
_mapView->setInputProjection(CRS::projection(_options.inputProjection));
_mapView->setTimeZone(_options.timeZone.zone());
_mapView->setPositionSource(_positionSource);
@ -2859,7 +2860,8 @@ void GUI::updateOptions(const Options &options)
_mapView->setDevicePixelRatio(devicePixelRatioF(),
options.hidpiMap ? devicePixelRatioF() : 1.0);
if (options.outputProjection != _options.outputProjection)
_mapView->setOutputProjection(CRS::projection(options.outputProjection));
_mapView->setOutputProjection(CRS::projection(4326,
options.outputProjection));
if (options.inputProjection != _options.inputProjection)
_mapView->setInputProjection(CRS::projection(options.inputProjection));
if (options.timeZone != _options.timeZone) {

View File

@ -14,6 +14,7 @@
#include <QSysInfo>
#include <QButtonGroup>
#include <QGeoPositionInfoSource>
#include "map/pcs.h"
#include "icons.h"
#include "infolabel.h"
#include "colorbox.h"
@ -52,18 +53,20 @@ void OptionsDialog::automaticPauseDetectionSet(bool set)
QWidget *OptionsDialog::createMapPage()
{
_outputProjection = new ProjectionComboBox();
_outputProjection = new ProjectionComboBox(GCS::WGS84List()
+ Conversion::list());
_outputProjection->setCurrentIndex(_outputProjection->findData(
_options.outputProjection));
_inputProjection = new ProjectionComboBox();
_inputProjection = new ProjectionComboBox(GCS::list() + PCS::list());
_inputProjection->setCurrentIndex(_inputProjection->findData(
_options.inputProjection));
InfoLabel *inInfo = new InfoLabel(tr("Select the proper projection of maps"
" without a projection definition (JNX, KMZ and world file maps)."));
InfoLabel *outInfo = new InfoLabel(tr("Select the desired projection of"
" vector maps (IMG and Mapsforge maps). The projection must be valid for"
" the whole map area."));
InfoLabel *inInfo = new InfoLabel(tr("Select the proper coordinate "
"reference system (CRS) of maps without a CRS definition "
"(JNX, KMZ and World file maps)."));
InfoLabel *outInfo = new InfoLabel(tr("Select the desired projection of "
"vector maps (IMG, Mapsforge and ENC maps). The projection must be valid "
"for the whole map area."));
_hidpi = new QRadioButton(tr("High-resolution"));
_lodpi = new QRadioButton(tr("Standard"));

View File

@ -1,21 +1,16 @@
#include "map/pcs.h"
#include "projectioncombobox.h"
ProjectionComboBox::ProjectionComboBox(QWidget *parent) : QComboBox(parent)
ProjectionComboBox::ProjectionComboBox(const QList<KV<int, QString> > &list,
QWidget *parent) : QComboBox(parent)
{
setSizeAdjustPolicy(AdjustToMinimumContentsLengthWithIcon);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
int last = -1;
QList<KV<int, QString> > projections(GCS::list() + PCS::list());
std::sort(projections.begin(), projections.end());
for (int i = 0; i < projections.size(); i++) {
const KV<int, QString> &proj = projections.at(i);
// There may be duplicit EPSG codes with different names
if (proj.key() == last)
continue;
else
last = proj.key();
QList<KV<int, QString> > projs(list);
std::sort(projs.begin(), projs.end());
for (int i = 0; i < list.size(); i++) {
const KV<int, QString> &proj = projs.at(i);
QString text = QString::number(proj.key()) + " - " + proj.value();
addItem(text, QVariant(proj.key()));
}

View File

@ -2,11 +2,13 @@
#define PROJECTIONCOMBOBOX_H
#include <QComboBox>
#include <QList>
#include "common/kv.h"
class ProjectionComboBox : public QComboBox
{
public:
ProjectionComboBox(QWidget *parent = 0);
ProjectionComboBox(const QList<KV<int, QString> > &list, QWidget *parent = 0);
};
#endif // PROJECTIONCOMBOBOX_H

View File

@ -256,7 +256,7 @@ SETTING(printMovingTime, "printMovingTime", false );
SETTING(printItemCount, "printItemCount", true );
SETTING(separateGraphPage, "separateGraphPage", false );
SETTING(sliderColor, "sliderColor", QColor(Qt::red) );
SETTING(outputProjection, "outputProjection", 3857 );
SETTING(outputProjection, "outputProjection", 3856 );
SETTING(inputProjection, "inputProjection", 4326 );
SETTING(hidpiMap, "HiDPIMap", true );
SETTING(poiPath, "poiPath", "" );

View File

@ -13,7 +13,8 @@
#define STYLE_DIR "style"
#define SYMBOLS_DIR "symbols"
#define ELLIPSOID_FILE "ellipsoids.csv"
#define ELLIPSOIDS_FILE "ellipsoids.csv"
#define PROJECTIONS_FILE "projections.csv"
#define GCS_FILE "gcs.csv"
#define PCS_FILE "pcs.csv"
#define TYP_FILE "style.typ"
@ -131,7 +132,7 @@ QString ProgramPaths::translationsDir()
QString ProgramPaths::ellipsoidsFile()
{
return QDir(csvDir()).filePath(ELLIPSOID_FILE);
return QDir(csvDir()).filePath(ELLIPSOIDS_FILE);
}
QString ProgramPaths::gcsFile()
@ -139,6 +140,11 @@ QString ProgramPaths::gcsFile()
return QDir(csvDir()).filePath(GCS_FILE);
}
QString ProgramPaths::projectionsFile()
{
return QDir(csvDir()).filePath(PROJECTIONS_FILE);
}
QString ProgramPaths::pcsFile()
{
return QDir(csvDir()).filePath(PCS_FILE);

View File

@ -15,6 +15,7 @@ namespace ProgramPaths
QString translationsDir();
QString ellipsoidsFile();
QString gcsFile();
QString projectionsFile();
QString pcsFile();
QString typFile();
QString renderthemeFile();

View File

@ -304,20 +304,20 @@ bool BSBMap::createProjection(const QString &datum, const QString &proj,
if (!proj.compare("MERCATOR", Qt::CaseInsensitive)) {
Projection::Setup setup(0, c.lon(), NAN, 0, 0, NAN, NAN);
pcs = PCS(gcs, 9804, setup, 9001);
pcs = PCS(gcs, Conversion(9804, setup, 9001));
} else if (!proj.compare("TRANSVERSE MERCATOR", Qt::CaseInsensitive)) {
Projection::Setup setup(0, params[1], params[2], 0, 0, NAN, NAN);
pcs = PCS(gcs, 9807, setup, 9001);
pcs = PCS(gcs, Conversion(9807, setup, 9001));
} else if (!proj.compare("UNIVERSAL TRANSVERSE MERCATOR",
Qt::CaseInsensitive)) {
Projection::Setup setup(0, params[0], 0.9996, 500000, 0, NAN, NAN);
pcs = PCS(gcs, 9807, setup, 9001);
pcs = PCS(gcs, Conversion(9807, setup, 9001));
} else if (!proj.compare("LAMBERT CONFORMAL CONIC", Qt::CaseInsensitive)) {
Projection::Setup setup(0, params[0], NAN, 0, 0, params[2], params[3]);
pcs = PCS(gcs, 9802, setup, 9001);
pcs = PCS(gcs, Conversion(9802, setup, 9001));
} else if (!proj.compare("POLYCONIC", Qt::CaseInsensitive)) {
Projection::Setup setup(0, params[0], NAN, 0, 0, NAN, NAN);
pcs = PCS(gcs, 9818, setup, 9001);
pcs = PCS(gcs, Conversion(9818, setup, 9001));
} else {
_errorString = proj + ": Unknown/missing projection";
return false;

203
src/map/conversion.cpp Normal file
View File

@ -0,0 +1,203 @@
#include <QFile>
#include "conversion.h"
static bool parameter(int key, double val, int units, Projection::Setup &setup)
{
switch (key) {
case 8801:
case 8811:
case 8821:
case 8832:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setLatitudeOrigin(au.toDegrees(val));}
return true;
case 8802:
case 8812:
case 8822:
case 8833:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setLongitudeOrigin(au.toDegrees(val));}
return true;
case 8805:
case 8815:
case 8819:
setup.setScale(val);
return true;
case 8806:
case 8816:
case 8826:
{LinearUnits lu(units);
if (lu.isNull())
return false;
setup.setFalseEasting(lu.toMeters(val));}
return true;
case 8807:
case 8817:
case 8827:
{LinearUnits lu(units);
if (lu.isNull())
return false;
setup.setFalseNorthing(lu.toMeters(val));}
return true;
case 8813:
case 8818:
case 8823:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setStandardParallel1(au.toDegrees(val));}
return true;
case 1036:
case 8814:
case 8824:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setStandardParallel2(au.toDegrees(val));}
return true;
default:
return false;
}
}
static int projectionSetup(const QList<QByteArray> &list,
Projection::Setup &setup)
{
bool r1, r2, r3;
for (int i = 5; i < 26; i += 3) {
QString ks = list[i].trimmed();
if (ks.isEmpty())
break;
int key = ks.toInt(&r1);
double val = list[i+1].trimmed().toDouble(&r2);
int un = list[i+2].trimmed().toInt(&r3);
if (!r1 || !r2 || !r3)
return (i - 5)/3 + 1;
if (!parameter(key, val, un, setup))
return (i - 5)/3 + 1;
}
return 0;
}
QMap<int, Conversion::Entry> Conversion::_conversions = defaults();
QMap<int, Conversion::Entry> Conversion::defaults()
{
QMap<int, Conversion::Entry> map;
map.insert(3856, Entry("Popular Visualisation Pseudo-Mercator", 1024,
Projection::Setup(), 9001, 4400));
return map;
}
Conversion Conversion::conversion(int id)
{
QMap<int, Entry>::const_iterator it = _conversions.find(id);
if (it == _conversions.constEnd())
return Conversion();
else {
const Entry &e = it.value();
return Conversion(e.method(), e.setup(), e.units(), e.cs());
}
}
void Conversion::loadList(const QString &path)
{
QFile file(path);
bool res;
int ln = 0, pn;
if (!file.open(QFile::ReadOnly)) {
qWarning("Error opening projections file: %s: %s", qPrintable(path),
qPrintable(file.errorString()));
return;
}
while (!file.atEnd()) {
ln++;
QByteArray line = file.readLine(4096);
QList<QByteArray> list = line.split(',');
if (list.size() != 26) {
qWarning("%s:%d: Format error", qPrintable(path), ln);
continue;
}
QString name(list.at(0).trimmed());
int proj = list.at(1).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid projection code", qPrintable(path), ln);
continue;
}
int units = list.at(2).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid linear units code", qPrintable(path), ln);
continue;
}
int transform = list.at(3).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid coordinate transformation code",
qPrintable(path), ln);
continue;
}
int cs = list.at(4).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid coordinate system code",
qPrintable(path), ln);
continue;
}
if (!LinearUnits(units).isValid()) {
qWarning("%s:%d: Unknown linear units code", qPrintable(path), ln);
continue;
}
if (!Projection::Method(transform).isValid()) {
qWarning("%s:%d: Unknown coordinate transformation code",
qPrintable(path), ln);
continue;
}
if (!CoordinateSystem(cs).isValid()) {
qWarning("%s:%d: Unknown coordinate system code", qPrintable(path),
ln);
continue;
}
Projection::Setup setup;
if ((pn = projectionSetup(list, setup))) {
qWarning("%s: %d: Invalid projection parameter #%d",
qPrintable(path), ln, pn);
continue;
}
_conversions.insert(proj, Entry(name, transform, setup, units, cs));
}
}
QList<KV<int, QString> > Conversion::list()
{
QList<KV<int, QString> > list;
for (QMap<int, Entry>::const_iterator it = _conversions.constBegin();
it != _conversions.constEnd(); ++it)
list.append(KV<int, QString>(it.key(), it.value().name()));
return list;
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Conversion &conversion)
{
dbg.nospace() << "Conversion(" << conversion.method() << ", "
<< conversion.units() << ", " << conversion.setup() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

71
src/map/conversion.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef CONVERSION_H
#define CONVERSION_H
#include "projection.h"
class Conversion {
public:
Conversion() {}
Conversion(const Projection::Method &method,
const Projection::Setup &setup, const LinearUnits &units,
const CoordinateSystem &cs = CoordinateSystem()) : _method(method),
_setup(setup), _units(units), _cs(cs) {}
const Projection::Method &method() const {return _method;}
const Projection::Setup &setup() const {return _setup;}
const LinearUnits &units() const {return _units;}
const CoordinateSystem &cs() const {return _cs;}
bool isNull() const {
return (_units.isNull() && _method.isNull() && _setup.isNull()
&& _cs.isNull());
}
bool isValid() const {
/* We do not check the CoordinateSystem here as it is not always defined
and except of WMTS/WMS it is not needed. The projection setup is
always valid as we do not have any checks for it. */
return (_units.isValid() && _method.isValid());
}
static void loadList(const QString &path);
static Conversion conversion(int id);
static QList<KV<int, QString> > list();
private:
class Entry {
public:
Entry(const QString &name, const Projection::Method &method,
const Projection::Setup &setup, const LinearUnits &units,
const CoordinateSystem &cs = CoordinateSystem())
: _name(name), _method(method), _setup(setup), _units(units),
_cs(cs) {}
const QString &name() const {return _name;}
const Projection::Method &method() const {return _method;}
const Projection::Setup &setup() const {return _setup;}
const LinearUnits &units() const {return _units;}
const CoordinateSystem &cs() const {return _cs;}
private:
QString _name;
Projection::Method _method;
Projection::Setup _setup;
LinearUnits _units;
CoordinateSystem _cs;
};
static QMap<int, Entry> defaults();
Projection::Method _method;
Projection::Setup _setup;
LinearUnits _units;
CoordinateSystem _cs;
static QMap<int, Entry> _conversions;
};
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Conversion &conversion);
#endif // QT_NO_DEBUG
#endif // CONVERSION_H

View File

@ -58,9 +58,24 @@ Projection CRS::projection(int id)
if (!pcs.isNull())
return Projection(pcs);
// Geographic 2D projections
GCS gcs(GCS::gcs(id));
if (!gcs.isNull())
return Projection(gcs);
return Projection();
}
Projection CRS::projection(int gcsId, int projId)
{
Conversion proj(Conversion::conversion(projId));
if (!proj.isNull())
return Projection(PCS(GCS::gcs(gcsId), proj));
// Geographic 2D projections
GCS gcs(GCS::gcs(projId));
if (!gcs.isNull())
return Projection(gcs);
return Projection();
}

View File

@ -7,6 +7,7 @@ namespace CRS
{
Projection projection(const QString &crs);
Projection projection(int id);
Projection projection(int gcsId, int projId);
}
#endif // CRS_H

View File

@ -52,17 +52,17 @@ void Ellipsoid::loadList(const QString &path)
continue;
}
int id = list[1].trimmed().toInt(&res);
int id = list.at(1).trimmed().toInt(&res);
if (!res) {
qWarning("%s: %d: Invalid ellipsoid code", qPrintable(path), ln);
continue;
}
double radius = list[2].trimmed().toDouble(&res);
double radius = list.at(2).trimmed().toDouble(&res);
if (!res) {
qWarning("%s: %d: Invalid radius", qPrintable(path), ln);
continue;
}
double flattening = list[3].trimmed().toDouble(&res);
double flattening = list.at(3).trimmed().toDouble(&res);
if (!res) {
qWarning("%s: %d: Invalid flattening", qPrintable(path), ln);
continue;

View File

@ -227,7 +227,7 @@ void ENCMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
void ENCMap::setOutputProjection(const Projection &projection)
{
if (projection == _projection)
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;

View File

@ -235,11 +235,21 @@ QList<KV<int, QString> > GCS::list()
{
QList<KV<int, QString> > list;
for (int i = 0; i < _gcss.size(); i++)
if (_gcss.at(i).id())
list.append(KV<int, QString>(_gcss.at(i).id(), _gcss.at(i).name()
+ " / Geographic 2D"));
for (int i = 0; i < _gcss.size(); i++) {
const Entry &e = _gcss.at(i);
if (!e.id() || (i && e.id() == list.last().key()))
continue;
list.append(KV<int, QString>(e.id(), e.name() + " / Geographic 2D"));
}
return list;
}
QList<KV<int, QString> > GCS::WGS84List()
{
QList<KV<int, QString> > list;
list.append(KV<int, QString>(4326, "Geographic 2D"));
return list;
}

View File

@ -39,6 +39,7 @@ public:
static void loadList(const QString &path);
static QList<KV<int, QString> > list();
static QList<KV<int, QString> > WGS84List();
private:
class Entry;

View File

@ -370,14 +370,13 @@ bool GeoTIFF::projectedModel(QMap<quint16, Value> &kv)
GCS gcs(geographicCS(kv));
if (gcs.isNull())
return false;
PCS pcs(PCS::pcs(gcs, kv.value(ProjectionGeoKey).SHORT));
if (pcs.isNull()) {
Conversion c(Conversion::conversion(kv.value(ProjectionGeoKey).SHORT));
if (c.isNull()) {
_errorString = QString("%1: unknown projection code")
.arg(kv.value(GeographicTypeGeoKey).SHORT)
.arg(kv.value(ProjectionGeoKey).SHORT);
return false;
}
_projection = Projection(pcs);
_projection = Projection(PCS(gcs, c));
} else {
double lat0, lon0, scale, fe, fn, sp1, sp2;
@ -454,7 +453,7 @@ bool GeoTIFF::projectedModel(QMap<quint16, Value> &kv)
fe = NAN;
Projection::Setup setup(lat0, lon0, scale, fe, fn, sp1, sp2);
_projection = Projection(PCS(gcs, method, setup, lu));
_projection = Projection(PCS(gcs, Conversion(method, setup, lu)));
}
return true;

View File

@ -270,7 +270,7 @@ void IMGMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
void IMGMap::setOutputProjection(const Projection &projection)
{
if (projection == _projection)
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;

View File

@ -269,7 +269,7 @@ void JNXMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
void JNXMap::setInputProjection(const Projection &projection)
{
if (projection == _projection)
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;

View File

@ -471,7 +471,7 @@ void KMZMap::unload()
void KMZMap::setInputProjection(const Projection &projection)
{
if (projection == _projection)
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;

View File

@ -175,51 +175,51 @@ bool MapFile::createProjection(const QString &datum, const QString &name,
_projection = Projection(gcs);
return true;
} else if (name == "Mercator")
pcs = PCS(gcs, 1024, setup, 9001);
pcs = PCS(gcs, Conversion(1024, setup, 9001));
else if (name == "Transverse Mercator"
|| name == "(UTM) Universal Transverse Mercator")
pcs = PCS(gcs, 9807, setup, 9001);
pcs = PCS(gcs, Conversion(9807, setup, 9001));
else if (name == "Lambert Conformal Conic")
pcs = PCS(gcs, 9802, setup, 9001);
pcs = PCS(gcs, Conversion(9802, setup, 9001));
else if (name == "Albers Equal Area")
pcs = PCS(gcs, 9822, setup, 9001);
pcs = PCS(gcs, Conversion(9822, setup, 9001));
else if (name == "(A)Lambert Azimuthual Equal Area")
pcs = PCS(gcs, 9820, setup, 9001);
pcs = PCS(gcs, Conversion(9820, setup, 9001));
else if (name == "Polyconic (American)")
pcs = PCS(gcs, 9818, setup, 9001);
pcs = PCS(gcs, Conversion(9818, setup, 9001));
else if (name == "(NZTM2) New Zealand TM 2000")
pcs = PCS(gcs, 9807, Projection::Setup(0, 173.0, 0.9996, 1600000,
10000000, NAN, NAN), 9001);
pcs = PCS(gcs, Conversion(9807, Projection::Setup(0, 173.0, 0.9996,
1600000, 10000000, NAN, NAN), 9001));
else if (name == "(BNG) British National Grid")
pcs = PCS(gcs, 9807, Projection::Setup(49, -2, 0.999601, 400000,
-100000, NAN, NAN), 9001);
pcs = PCS(gcs, Conversion(9807, Projection::Setup(49, -2, 0.999601,
400000, -100000, NAN, NAN), 9001));
else if (name == "(IG) Irish Grid")
pcs = PCS(gcs, 9807, Projection::Setup(53.5, -8, 1.000035, 200000,
250000, NAN, NAN), 9001);
pcs = PCS(gcs, Conversion(9807, Projection::Setup(53.5, -8, 1.000035,
200000, 250000, NAN, NAN), 9001));
else if (name == "(SG) Swedish Grid")
pcs = PCS(gcs, 9807, Projection::Setup(0, 15.808278, 1, 1500000, 0, NAN,
NAN), 9001);
pcs = PCS(gcs, Conversion(9807, Projection::Setup(0, 15.808278, 1,
1500000, 0, NAN, NAN), 9001));
else if (name == "(I) France Zone I")
pcs = PCS(gcs, 9802, Projection::Setup(49.5, 2.337229, NAN, 600000,
1200000, 48.598523, 50.395912), 9001);
pcs = PCS(gcs, Conversion(9802, Projection::Setup(49.5, 2.337229, NAN,
600000, 1200000, 48.598523, 50.395912), 9001));
else if (name == "(II) France Zone II")
pcs = PCS(gcs, 9802, Projection::Setup(46.8, 2.337229, NAN, 600000,
2200000, 45.898919, 47.696014), 9001);
pcs = PCS(gcs, Conversion(9802, Projection::Setup(46.8, 2.337229, NAN,
600000, 2200000, 45.898919, 47.696014), 9001));
else if (name == "(III) France Zone III")
pcs = PCS(gcs, 9802, Projection::Setup(44.1, 2.337229, NAN, 600000,
3200000, 43.199291, 44.996094), 9001);
pcs = PCS(gcs, Conversion(9802, Projection::Setup(44.1, 2.337229, NAN,
600000, 3200000, 43.199291, 44.996094), 9001));
else if (name == "(IV) France Zone IV")
pcs = PCS(gcs, 9802, Projection::Setup(42.165, 2.337229, NAN, 234.358,
4185861.369, 41.560388, 42.767663), 9001);
pcs = PCS(gcs, Conversion(9802, Projection::Setup(42.165, 2.337229, NAN,
234.358, 4185861.369, 41.560388, 42.767663), 9001));
else if (name == "(VICGRID) Victoria Australia")
pcs = PCS(gcs, 9802, Projection::Setup(-37, 145, NAN, 2500000, 4500000,
-36, -38), 9001);
pcs = PCS(gcs, Conversion(9802, Projection::Setup(-37, 145, NAN,
2500000, 4500000, -36, -38), 9001));
else if (name == "(VG94) VICGRID94 Victoria Australia")
pcs = PCS(gcs, 9802, Projection::Setup(-37, 145, NAN, 2500000, 2500000,
-36, -38), 9001);
pcs = PCS(gcs, Conversion(9802, Projection::Setup(-37, 145, NAN,
2500000, 2500000, -36, -38), 9001));
else if (name == "(SUI) Swiss Grid")
pcs = PCS(gcs, 9815, Projection::Setup(46.570866, 7.26225, 1.0, 600000,
200000, 90.0, 90.0), 9001);
pcs = PCS(gcs, Conversion(9815, Projection::Setup(46.570866, 7.26225,
1.0, 600000, 200000, 90.0, 90.0), 9001));
else {
_errorString = QString("%1: Unknown map projection").arg(name);
return false;

View File

@ -230,7 +230,7 @@ void MapsforgeMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
void MapsforgeMap::setOutputProjection(const Projection &projection)
{
if (projection == _projection)
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;

View File

@ -178,28 +178,28 @@ static Projection createProjection(const GCS &gcs, const QString &name)
if (pl.first() == "Latitude/Longitude")
return Projection(gcs);
else if (pl.first() == "UTM")
pcs = PCS(gcs, 9807, utm2setup(pl), 9001);
pcs = PCS(gcs, Conversion(9807, utm2setup(pl), 9001));
else if (pl.first() == "Mercator")
pcs = PCS(gcs, 1024, Projection::Setup(), 9001);
pcs = PCS(gcs, Conversion(1024, Projection::Setup(), 9001));
else if (pl.first() == "Mercator Ellipsoidal")
pcs = PCS(gcs, 9804, mercator2setup(pl), 9001);
pcs = PCS(gcs, Conversion(9804, mercator2setup(pl), 9001));
else if (pl.first() == "Transverse Mercator")
pcs = PCS(gcs, 9807, tm2setup(pl), 9001);
pcs = PCS(gcs, Conversion(9807, tm2setup(pl), 9001));
else if (pl.first() == "Lambert Conformal Conic")
pcs = PCS(gcs, 9802, lcc2setup(pl), 9001);
pcs = PCS(gcs, Conversion(9802, lcc2setup(pl), 9001));
else if (pl.first() == "(A)Lambert Azimuthual Equal Area")
pcs = PCS(gcs, 9820, laea2setup(pl), 9001);
pcs = PCS(gcs, Conversion(9820, laea2setup(pl), 9001));
else if (pl.first() == "Polyconic (American)")
pcs = PCS(gcs, 9818, polyconic2setup(pl), 9001);
pcs = PCS(gcs, Conversion(9818, polyconic2setup(pl), 9001));
else if (pl.first() == "(IG) Irish Grid")
pcs = PCS(gcs, 9807, Projection::Setup(53.5, -8, 1.000035, 200000,
250000, NAN, NAN), 9001);
pcs = PCS(gcs, Conversion(9807, Projection::Setup(53.5, -8, 1.000035,
200000, 250000, NAN, NAN), 9001));
else if (pl.first() == "(SUI) Swiss Grid")
pcs = PCS(gcs, 9815, Projection::Setup(46.570866, 7.26225, 1.0, 600000,
200000, 90.0, 90.0), 9001);
pcs = PCS(gcs, Conversion(9815, Projection::Setup(46.570866, 7.26225,
1.0, 600000, 200000, 90.0, 90.0), 9001));
else if (pl.first() == "Rijksdriehoeksmeting")
pcs = PCS(gcs, 9809, Projection::Setup(52.1561605555556,
5.38763888888889, 0.9999079, 155000, 463000, NAN, NAN), 9001);
pcs = PCS(gcs, Conversion(9809, Projection::Setup(52.1561605555556,
5.38763888888889, 0.9999079, 155000, 463000, NAN, NAN), 9001));
else
return Projection();

View File

@ -2,145 +2,32 @@
#include "angularunits.h"
#include "pcs.h"
QMap<int, PCS::Entry> PCS::_pcss = defaults();
class PCS::Entry {
public:
Entry(const QString &name, int id, int proj, const PCS &pcs)
: _name(name), _id(id), _proj(proj), _pcs(pcs) {}
const QString &name() const {return _name;}
int id() const {return _id;}
int proj() const {return _proj;}
const PCS &pcs() const {return _pcs;}
private:
QString _name;
int _id, _proj;
PCS _pcs;
};
QList<PCS::Entry> PCS::_pcss = defaults();
QList<PCS::Entry> PCS::defaults()
QMap<int, PCS::Entry> PCS::defaults()
{
QList<PCS::Entry> list;
list.append(PCS::Entry("WGS 84 / Pseudo-Mercator", 3857, 3856,
PCS(GCS::WGS84(), 1024, Projection::Setup(0, 0, NAN, 0, 0, NAN, NAN),
9001, 4499)));
return list;
QMap<int, Entry> map;
map.insert(3857, Entry("WGS 84 / Pseudo-Mercator", 4326, 3856));
return map;
}
static bool parameter(int key, double val, int units, Projection::Setup &setup)
{
switch (key) {
case 8801:
case 8811:
case 8821:
case 8832:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setLatitudeOrigin(au.toDegrees(val));}
return true;
case 8802:
case 8812:
case 8822:
case 8833:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setLongitudeOrigin(au.toDegrees(val));}
return true;
case 8805:
case 8815:
case 8819:
setup.setScale(val);
return true;
case 8806:
case 8816:
case 8826:
{LinearUnits lu(units);
if (lu.isNull())
return false;
setup.setFalseEasting(lu.toMeters(val));}
return true;
case 8807:
case 8817:
case 8827:
{LinearUnits lu(units);
if (lu.isNull())
return false;
setup.setFalseNorthing(lu.toMeters(val));}
return true;
case 8813:
case 8818:
case 8823:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setStandardParallel1(au.toDegrees(val));}
return true;
case 1036:
case 8814:
case 8824:
{AngularUnits au(units);
if (au.isNull())
return false;
setup.setStandardParallel2(au.toDegrees(val));}
return true;
default:
return false;
}
}
static int projectionSetup(const QList<QByteArray> &list,
Projection::Setup &setup)
{
bool r1, r2, r3;
for (int i = 7; i < 28; i += 3) {
QString ks = list[i].trimmed();
if (ks.isEmpty())
break;
int key = ks.toInt(&r1);
double val = list[i+1].trimmed().toDouble(&r2);
int un = list[i+2].trimmed().toInt(&r3);
if (!r1 || !r2 || !r3)
return (i - 7)/3 + 1;
if (!parameter(key, val, un, setup))
return (i - 7)/3 + 1;
}
return 0;
}
PCS PCS::pcs(int id)
{
for (int i = 0; i < _pcss.size(); i++)
if (_pcss.at(i).id() == id)
return _pcss.at(i).pcs();
QMap<int, Entry>::const_iterator it = _pcss.find(id);
return PCS();
}
PCS PCS::pcs(const GCS &gcs, int proj)
{
for (int i = 0; i < _pcss.size(); i++)
if (_pcss.at(i).proj() == proj && _pcss.at(i).pcs().gcs() == gcs)
return _pcss.at(i).pcs();
return PCS();
if (it == _pcss.constEnd())
return PCS();
else {
const Entry &e = it.value();
return PCS(GCS::gcs(e.gcs()), Conversion::conversion(e.proj()));
}
}
void PCS::loadList(const QString &path)
{
QFile file(path);
bool res;
int ln = 0, pn;
int ln = 0;
if (!file.open(QFile::ReadOnly)) {
qWarning("Error opening PCS file: %s: %s", qPrintable(path),
@ -153,7 +40,7 @@ void PCS::loadList(const QString &path)
QByteArray line = file.readLine(4096);
QList<QByteArray> list = line.split(',');
if (list.size() != 28) {
if (list.size() != 4) {
qWarning("%s:%d: Format error", qPrintable(path), ln);
continue;
}
@ -164,7 +51,7 @@ void PCS::loadList(const QString &path)
qWarning("%s:%d: Invalid PCS code", qPrintable(path), ln);
continue;
}
int gcsid = list.at(2).trimmed().toInt(&res);
int gcs = list.at(2).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid GCS code", qPrintable(path), ln);
continue;
@ -174,53 +61,13 @@ void PCS::loadList(const QString &path)
qWarning("%s:%d: Invalid projection code", qPrintable(path), ln);
continue;
}
int units = list.at(4).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid linear units code", qPrintable(path), ln);
continue;
}
int transform = list.at(5).trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid coordinate transformation code",
qPrintable(path), ln);
continue;
}
int cs = list[6].trimmed().toInt(&res);
if (!res) {
qWarning("%s:%d: Invalid coordinate system code",
qPrintable(path), ln);
continue;
}
if (!LinearUnits(units).isValid()) {
qWarning("%s:%d: Unknown linear units code", qPrintable(path), ln);
continue;
}
if (!Projection::Method(transform).isValid()) {
qWarning("%s:%d: Unknown coordinate transformation code",
qPrintable(path), ln);
continue;
}
if (!CoordinateSystem(cs).isValid()) {
qWarning("%s:%d: Unknown coordinate system code", qPrintable(path),
ln);
continue;
}
GCS gcs(GCS::gcs(gcsid));
if (gcs.isNull()) {
if (GCS::gcs(gcs).isNull()) {
qWarning("%s:%d: Unknown GCS code", qPrintable(path), ln);
continue;
}
Projection::Setup setup;
if ((pn = projectionSetup(list, setup))) {
qWarning("%s: %d: Invalid projection parameter #%d",
qPrintable(path), ln, pn);
continue;
}
PCS pcs(gcs, transform, setup, units, cs);
_pcss.append(Entry(name, id, proj, pcs));
_pcss.insert(id, Entry(name, gcs, proj));
}
}
@ -228,8 +75,9 @@ QList<KV<int, QString> > PCS::list()
{
QList<KV<int, QString> > list;
for (int i = 0; i < _pcss.size(); i++)
list.append(KV<int, QString>(_pcss.at(i).id(), _pcss.at(i).name()));
for (QMap<int, Entry>::const_iterator it = _pcss.constBegin();
it != _pcss.constEnd(); ++it)
list.append(KV<int, QString>(it.key(), it.value().name()));
return list;
}
@ -237,8 +85,7 @@ QList<KV<int, QString> > PCS::list()
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const PCS &pcs)
{
dbg.nospace() << "PCS(" << pcs.gcs() << ", " << pcs.method() << ", "
<< pcs.units() << ", " << pcs.setup() << ")";
dbg.nospace() << "PCS(" << pcs.gcs() << ", " << pcs.conversion() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

View File

@ -2,56 +2,51 @@
#define PCS_H
#include <QDebug>
#include <QList>
#include <QMap>
#include "common/kv.h"
#include "gcs.h"
#include "linearunits.h"
#include "coordinatesystem.h"
#include "projection.h"
#include "conversion.h"
class PCS
{
public:
PCS() {}
PCS(const GCS &gcs, const Projection::Method &method,
const Projection::Setup &setup, const LinearUnits &units,
const CoordinateSystem &cs = CoordinateSystem())
: _gcs(gcs), _method(method), _setup(setup), _units(units), _cs(cs) {}
PCS(const GCS &gcs, int proj);
PCS(const GCS &gcs, const Conversion &conversion)
: _gcs(gcs), _conversion(conversion) {}
const GCS &gcs() const {return _gcs;}
const Projection::Method &method() const {return _method;}
const Projection::Setup &setup() const {return _setup;}
const LinearUnits &units() const {return _units;}
const CoordinateSystem &coordinateSystem() const {return _cs;}
const Conversion &conversion() const {return _conversion;}
bool isNull() const {
return (_gcs.isNull() && _units.isNull() && _method.isNull()
&& _setup.isNull() && _cs.isNull());
}
bool isValid() const {
// We do not check the CoordinateSystem here as it is not always defined
// and except of WMTS/WMS it is not needed.
return (_gcs.isValid() && _units.isValid() && _method.isValid());
}
bool isNull() const {return (_gcs.isNull() && _conversion.isNull());}
bool isValid() const {return (_gcs.isValid() && _conversion.isValid());}
static void loadList(const QString &path);
static PCS pcs(int id);
static PCS pcs(const GCS &gcs, int proj);
static QList<KV<int, QString> > list();
private:
class Entry;
class Entry {
public:
Entry(const QString &name, int gcs, int proj)
: _name(name), _gcs(gcs), _proj(proj) {}
static QList<Entry> defaults();
const QString &name() const {return _name;}
int proj() const {return _proj;}
int gcs() const {return _gcs;}
private:
QString _name;
int _gcs, _proj;
};
static QMap<int, Entry> defaults();
GCS _gcs;
Projection::Method _method;
Projection::Setup _setup;
LinearUnits _units;
CoordinateSystem _cs;
Conversion _conversion;
static QList<Entry> _pcss;
static QMap<int, Entry> _pcss;
};
#ifndef QT_NO_DEBUG

View File

@ -537,7 +537,7 @@ void PRJFile::projectedCS(CTX &ctx, PCS *pcs)
optProjectedCS(ctx, &epsg);
compare(ctx, RBRK);
*pcs = (epsg > 0) ? PCS::pcs(epsg) : PCS(gcs, method, setup, lu);
*pcs = (epsg > 0) ? PCS::pcs(epsg) : PCS(gcs, Conversion(method, setup, lu));
}
void PRJFile::axisType(CTX &ctx)

View File

@ -39,13 +39,13 @@ Projection::Method::Method(int id)
}
Projection::Projection(const PCS &pcs)
: _gcs(pcs.gcs()), _ct(0), _units(pcs.units()), _cs(pcs.coordinateSystem()),
_geographic(false)
: _gcs(pcs.gcs()), _ct(0), _units(pcs.conversion().units()),
_cs(pcs.conversion().cs()), _geographic(false)
{
const Ellipsoid &ellipsoid = _gcs.datum().ellipsoid();
const Projection::Setup &setup = pcs.setup();
const Projection::Setup &setup = pcs.conversion().setup();
switch (pcs.method().id()) {
switch (pcs.conversion().method().id()) {
case 1024:
_ct = new WebMercator();
break;

View File

@ -37,7 +37,7 @@ static CalibrationPoint parseCalibrationPoint(const QString &str)
static Projection parseProjection(const QString &str, const GCS &gcs)
{
QStringList fields(str.split(","));
QStringList fields(str.split(','));
if (fields.isEmpty())
return Projection();
bool ret;
@ -55,40 +55,42 @@ static Projection parseProjection(const QString &str, const GCS &gcs)
return Projection();
if (fields.at(3) == "S")
zone = -zone;
return Projection(PCS(gcs, 9807, UTM::setup(zone), 9001));
return Projection(PCS(gcs, Conversion(9807, UTM::setup(zone), 9001)));
case 1: // LatLon
return Projection(gcs);
case 2: // Mercator
return Projection(PCS(gcs, 1024, Projection::Setup(), 9001));
return Projection(PCS(gcs, Conversion(1024, Projection::Setup(),
9001)));
case 3: // Transversal Mercator
if (fields.size() < 7)
return Projection();
return Projection(PCS(gcs, 9807, Projection::Setup(
return Projection(PCS(gcs, Conversion(9807, Projection::Setup(
fields.at(3).toDouble(), fields.at(2).toDouble(),
fields.at(6).toDouble(), fields.at(5).toDouble(),
fields.at(4).toDouble(), NAN, NAN), 9001));
fields.at(4).toDouble(), NAN, NAN), 9001)));
case 4: // Lambert 2SP
if (fields.size() < 8)
return Projection();
return Projection(PCS(gcs, 9802, Projection::Setup(
return Projection(PCS(gcs, Conversion(9802, Projection::Setup(
fields.at(4).toDouble(), fields.at(5).toDouble(), NAN,
fields.at(6).toDouble(), fields.at(7).toDouble(),
fields.at(3).toDouble(), fields.at(2).toDouble()), 9001));
fields.at(3).toDouble(), fields.at(2).toDouble()), 9001)));
case 6: // BGN (British National Grid)
return Projection(PCS(gcs, 9807, Projection::Setup(49, -2, 0.999601,
400000, -100000, NAN, NAN), 9001));
return Projection(PCS(gcs, Conversion(9807, Projection::Setup(49,
-2, 0.999601, 400000, -100000, NAN, NAN), 9001)));
case 12: // France Lambert II etendu
return Projection(PCS(gcs, 9801, Projection::Setup(52, 0,
0.99987742, 600000, 2200000, NAN, NAN), 9001));
return Projection(PCS(gcs, Conversion(9801, Projection::Setup(52, 0,
0.99987742, 600000, 2200000, NAN, NAN), 9001)));
case 14: // Swiss Grid
return Projection(PCS(gcs, 9815, Projection::Setup(46.570866,
7.26225, 1.0, 600000, 200000, 90.0, 90.0), 9001));
return Projection(PCS(gcs, Conversion(9815, Projection::Setup(
46.570866, 7.26225, 1.0, 600000, 200000, 90.0, 90.0), 9001)));
case 108: // Dutch RD grid
return Projection(PCS(gcs, 9809, Projection::Setup(52.15616055555555,
5.38763888888889, 0.9999079, 155000, 463000, NAN, NAN), 9001));
return Projection(PCS(gcs, Conversion(9809, Projection::Setup(
52.15616055555555, 5.38763888888889, 0.9999079, 155000, 463000,
NAN, NAN), 9001)));
case 184: // Swedish Grid
return Projection(PCS(gcs, 9807, Projection::Setup(0, 15.808278, 1,
1500000, 0, NAN, NAN), 9001));
return Projection(PCS(gcs, Conversion(9807, Projection::Setup(0,
15.808278, 1, 1500000, 0, NAN, NAN), 9001)));
default:
return Projection();
}

View File

@ -116,7 +116,7 @@ void WorldFileMap::unload()
void WorldFileMap::setInputProjection(const Projection &projection)
{
if (_hasPRJ || projection == _projection)
if (_hasPRJ || !projection.isValid() || projection == _projection)
return;
_projection = projection;