地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

513 lines
17 KiB

/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @author Gus Grubba <mavlink@grubba.com>
* Original work: The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
*/
#include "QGCMapEngine.h"
#include <QRegExp>
#include <QNetworkReply>
#include <QEventLoop>
#include <QTimer>
#include <QString>
#include <QByteArray>
//-----------------------------------------------------------------------------
UrlFactory::UrlFactory()
: _timeout(5 * 1000)
, _googleVersionRetrieved(false)
, _googleReply(NULL)
{
QStringList langs = QLocale::system().uiLanguages();
if (langs.length() > 0) {
_language = langs[0];
}
// Google version strings
_versionGoogleMap = "m@336";
_versionGoogleSatellite = "194";
_versionGoogleLabels = "h@336";
_versionGoogleTerrain = "t@132,r@336";
_secGoogleWord = "Galileo";
// BingMaps
_versionBingMaps = "563";
}
//-----------------------------------------------------------------------------
UrlFactory::~UrlFactory()
{
if(_googleReply)
_googleReply->deleteLater();
}
//-----------------------------------------------------------------------------
QString
UrlFactory::getImageFormat(MapType type, const QByteArray& image)
{
QString format;
if(image.size() > 2)
{
if((char)image[0] == (char)0xff && (char)image[1] == (char)0xd8)
format = "jpg";
else if((char)image[0] == (char)0x89 && (char)image[1] == (char)0x50)
format = "png";
else {
switch (type) {
case GoogleMap:
case GoogleLabels:
case GoogleTerrain:
case GoogleHybrid:
case BingMap:
case OpenStreetMap:
format = "png";
break;
case MapQuestMap:
case MapQuestSat:
case MapBoxStreets:
case MapBoxLight:
case MapBoxDark:
case MapBoxSatellite:
case MapBoxHybrid:
case MapBoxWheatPaste:
case MapBoxStreetsBasic:
case MapBoxComic:
case MapBoxOutdoors:
case MapBoxRunBikeHike:
case MapBoxPencil:
case MapBoxPirates:
case MapBoxEmerald:
case MapBoxHighContrast:
case GoogleSatellite:
case BingSatellite:
case BingHybrid:
format = "jpg";
break;
default:
qWarning("UrlFactory::getImageFormat() Unknown map id %d", type);
break;
}
}
}
return format;
}
//-----------------------------------------------------------------------------
QNetworkRequest
UrlFactory::getTileURL(MapType type, int x, int y, int zoom, QNetworkAccessManager* networkManager)
{
//-- Build URL
QNetworkRequest request;
QString url = _getURL(type, x, y, zoom, networkManager);
if(url.isEmpty())
return request;
request.setUrl(QUrl(url));
request.setRawHeader("Accept", "*/*");
request.setRawHeader("User-Agent", _userAgent);
switch (type) {
case GoogleMap:
case GoogleSatellite:
case GoogleLabels:
case GoogleTerrain:
case GoogleHybrid:
request.setRawHeader("Referrer", "http://maps.google.com/");
break;
case BingHybrid:
case BingMap:
case BingSatellite:
request.setRawHeader("Referrer", "http://www.bing.com/maps/");
break;
/*
case OpenStreetMapSurfer:
case OpenStreetMapSurferTerrain:
request.setRawHeader("Referrer", "http://www.mapsurfer.net/");
break;
case OpenStreetMap:
case OpenStreetOsm:
request.setRawHeader("Referrer", "https://www.openstreetmap.org/");
break;
*/
default:
break;
}
return request;
}
//-----------------------------------------------------------------------------
void
UrlFactory::_getSecGoogleWords(int x, int y, QString &sec1, QString &sec2)
{
sec1 = ""; // after &x=...
sec2 = ""; // after &zoom=...
int seclen = ((x * 3) + y) % 8;
sec2 = _secGoogleWord.left(seclen);
if (y >= 10000 && y < 100000) {
sec1 = "&s=";
}
}
//-----------------------------------------------------------------------------
QString
UrlFactory::_getURL(MapType type, int x, int y, int zoom, QNetworkAccessManager* networkManager)
{
switch (type) {
case GoogleMap:
{
// http://mt1.google.com/vt/lyrs=m
QString server = "mt";
QString request = "vt";
QString sec1 = ""; // after &x=...
QString sec2 = ""; // after &zoom=...
_getSecGoogleWords(x, y, sec1, sec2);
_tryCorrectGoogleVersions(networkManager);
return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleMap).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2);
}
break;
case GoogleSatellite:
{
// http://mt1.google.com/vt/lyrs=s
QString server = "khm";
QString request = "kh";
QString sec1 = ""; // after &x=...
QString sec2 = ""; // after &zoom=...
_getSecGoogleWords(x, y, sec1, sec2);
_tryCorrectGoogleVersions(networkManager);
return QString("http://%1%2.google.com/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleSatellite).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2);
}
break;
case GoogleLabels:
{
QString server = "mts";
QString request = "vt";
QString sec1 = ""; // after &x=...
QString sec2 = ""; // after &zoom=...
_getSecGoogleWords(x, y, sec1, sec2);
_tryCorrectGoogleVersions(networkManager);
return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleLabels).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2);
}
break;
case GoogleTerrain:
{
QString server = "mt";
QString request = "vt";
QString sec1 = ""; // after &x=...
QString sec2 = ""; // after &zoom=...
_getSecGoogleWords(x, y, sec1, sec2);
_tryCorrectGoogleVersions(networkManager);
return QString("http://%1%2.google.com/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleTerrain).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2);
}
break;
/*
case OpenStreetMap:
{
char letter = "abc"[_getServerNum(x, y, 3)];
return QString("https://%1.tile.openstreetmap.org/%2/%3/%4.png").arg(letter).arg(zoom).arg(x).arg(y);
}
break;
case OpenStreetOsm:
{
char letter = "abc"[_getServerNum(x, y, 3)];
return QString("http://%1.tah.openstreetmap.org/Tiles/tile/%2/%3/%4.png").arg(letter).arg(zoom).arg(x).arg(y);
}
break;
case OpenStreetMapSurfer:
{
// http://tiles1.mapsurfer.net/tms_r.ashx?x=37378&y=20826&z=16
return QString("http://tiles1.mapsurfer.net/tms_r.ashx?x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
}
break;
case OpenStreetMapSurferTerrain:
{
// http://tiles2.mapsurfer.net/tms_t.ashx?x=9346&y=5209&z=14
return QString("http://tiles2.mapsurfer.net/tms_t.ashx?x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
}
break;
*/
case BingMap:
{
QString key = _tileXYToQuadKey(x, y, zoom);
return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/r%2.png?g=%3&mkt=%4").arg(_getServerNum(x, y, 4)).arg(key).arg(_versionBingMaps).arg(_language);
}
break;
case BingSatellite:
{
QString key = _tileXYToQuadKey(x, y, zoom);
return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/a%2.jpeg?g=%3&mkt=%4").arg(_getServerNum(x, y, 4)).arg(key).arg(_versionBingMaps).arg(_language);
}
break;
case BingHybrid:
{
QString key = _tileXYToQuadKey(x, y, zoom);
return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/h%2.jpeg?g=%3&mkt=%4").arg(_getServerNum(x, y, 4)).arg(key).arg(_versionBingMaps).arg(_language);
}
case MapQuestMap:
{
char letter = "1234"[_getServerNum(x, y, 4)];
return QString("http://otile%1.mqcdn.com/tiles/1.0.0/map/%2/%3/%4.jpg").arg(letter).arg(zoom).arg(x).arg(y);
}
break;
case MapQuestSat:
{
char letter = "1234"[_getServerNum(x, y, 4)];
return QString("http://otile%1.mqcdn.com/tiles/1.0.0/sat/%2/%3/%4.jpg").arg(letter).arg(zoom).arg(x).arg(y);
}
break;
case MapBoxStreets:
case MapBoxLight:
case MapBoxDark:
case MapBoxSatellite:
case MapBoxHybrid:
case MapBoxWheatPaste:
case MapBoxStreetsBasic:
case MapBoxComic:
case MapBoxOutdoors:
case MapBoxRunBikeHike:
case MapBoxPencil:
case MapBoxPirates:
case MapBoxEmerald:
case MapBoxHighContrast:
{
QString mapBoxToken = getQGCMapEngine()->getMapBoxToken();
if(!mapBoxToken.isEmpty()) {
QString server = "https://api.mapbox.com/v4/";
switch(type) {
case MapBoxStreets:
server += "mapbox.streets";
break;
case MapBoxLight:
server += "mapbox.light";
break;
case MapBoxDark:
server += "mapbox.dark";
break;
case MapBoxSatellite:
server += "mapbox.satellite";
break;
case MapBoxHybrid:
server += "mapbox.streets-satellite";
break;
case MapBoxWheatPaste:
server += "mapbox.wheatpaste";
break;
case MapBoxStreetsBasic:
server += "mapbox.streets-basic";
break;
case MapBoxComic:
server += "mapbox.comic";
break;
case MapBoxOutdoors:
server += "mapbox.outdoors";
break;
case MapBoxRunBikeHike:
server += "mapbox.run-bike-hike";
break;
case MapBoxPencil:
server += "mapbox.pencil";
break;
case MapBoxPirates:
server += "mapbox.pirates";
break;
case MapBoxEmerald:
server += "mapbox.emerald";
break;
case MapBoxHighContrast:
server += "mapbox.high-contrast";
break;
default:
return QString::null;
}
server += QString("/%1/%2/%3.jpg80?access_token=%4").arg(zoom).arg(x).arg(y).arg(mapBoxToken);
return server;
}
}
break;
default:
qWarning("Unknown map id %d\n", type);
break;
}
return QString::null;
}
//-----------------------------------------------------------------------------
QString
UrlFactory::_tileXYToQuadKey(int tileX, int tileY, int levelOfDetail)
{
QString quadKey;
for (int i = levelOfDetail; i > 0; i--) {
char digit = '0';
int mask = 1 << (i - 1);
if ((tileX & mask) != 0) {
digit++;
}
if ((tileY & mask) != 0) {
digit++;
digit++;
}
quadKey.append(digit);
}
return quadKey;
}
//-----------------------------------------------------------------------------
int
UrlFactory::_getServerNum(int x, int y, int max)
{
return (x + 2 * y) % max;
}
//-----------------------------------------------------------------------------
void
UrlFactory::_networkReplyError(QNetworkReply::NetworkError error)
{
qWarning() << "Could not connect to google maps. Error:" << error;
if(_googleReply)
{
_googleReply->deleteLater();
_googleReply = NULL;
}
}
//-----------------------------------------------------------------------------
void
UrlFactory::_replyDestroyed()
{
_googleReply = NULL;
}
//-----------------------------------------------------------------------------
void
UrlFactory::_googleVersionCompleted()
{
if (!_googleReply || (_googleReply->error() != QNetworkReply::NoError)) {
qDebug() << "Error collecting Google maps version info";
return;
}
QString html = QString(_googleReply->readAll());
QRegExp reg("\"*http://mt0.google.com/vt/lyrs=m@(\\d*)",Qt::CaseInsensitive);
if (reg.indexIn(html) != -1) {
QStringList gc = reg.capturedTexts();
_versionGoogleMap = QString("m@%1").arg(gc[1]);
}
reg = QRegExp("\"*http://mt0.google.com/vt/lyrs=h@(\\d*)",Qt::CaseInsensitive);
if (reg.indexIn(html) != -1) {
QStringList gc = reg.capturedTexts();
_versionGoogleLabels = QString("h@%1").arg(gc[1]);
}
reg = QRegExp("\"*http://khm\\D?\\d.google.com/kh/v=(\\d*)",Qt::CaseInsensitive);
if (reg.indexIn(html) != -1) {
QStringList gc = reg.capturedTexts();
_versionGoogleSatellite = gc[1];
}
reg = QRegExp("\"*http://mt0.google.com/vt/lyrs=t@(\\d*),r@(\\d*)",Qt::CaseInsensitive);
if (reg.indexIn(html) != -1) {
QStringList gc = reg.capturedTexts();
_versionGoogleTerrain = QString("t@%1,r@%2").arg(gc[1]).arg(gc[2]);
}
_googleReply->deleteLater();
_googleReply = NULL;
}
//-----------------------------------------------------------------------------
void
UrlFactory::_tryCorrectGoogleVersions(QNetworkAccessManager* networkManager)
{
QMutexLocker locker(&_googleVersionMutex);
if (_googleVersionRetrieved) {
return;
}
_googleVersionRetrieved = true;
if(networkManager)
{
QNetworkRequest qheader;
QNetworkProxy proxy = networkManager->proxy();
QNetworkProxy tProxy;
tProxy.setType(QNetworkProxy::NoProxy);
networkManager->setProxy(tProxy);
QString url = "http://maps.google.com/maps";
qheader.setUrl(QUrl(url));
QByteArray ua;
ua.append(getQGCMapEngine()->userAgent());
qheader.setRawHeader("User-Agent", ua);
_googleReply = networkManager->get(qheader);
connect(_googleReply, &QNetworkReply::finished, this, &UrlFactory::_googleVersionCompleted);
connect(_googleReply, &QNetworkReply::destroyed, this, &UrlFactory::_replyDestroyed);
connect(_googleReply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
this, &UrlFactory::_networkReplyError);
networkManager->setProxy(proxy);
}
}
#define AVERAGE_GOOGLE_STREET_MAP 4913
#define AVERAGE_GOOGLE_TERRAIN_MAP 19391
#define AVERAGE_BING_STREET_MAP 1297
#define AVERAGE_BING_SAT_MAP 19597
#define AVERAGE_GOOGLE_SAT_MAP 56887
#define AVERAGE_MAPBOX_SAT_MAP 15739
#define AVERAGE_MAPBOX_STREET_MAP 5648
#define AVERAGE_TILE_SIZE 13652
//-----------------------------------------------------------------------------
quint32
UrlFactory::averageSizeForType(MapType type)
{
switch (type) {
case GoogleMap:
return AVERAGE_GOOGLE_STREET_MAP;
case BingMap:
return AVERAGE_BING_STREET_MAP;
case GoogleSatellite:
return AVERAGE_GOOGLE_SAT_MAP;
case MapBoxSatellite:
return AVERAGE_MAPBOX_SAT_MAP;
case BingHybrid:
case BingSatellite:
return AVERAGE_BING_SAT_MAP;
case GoogleTerrain:
return AVERAGE_GOOGLE_TERRAIN_MAP;
case MapBoxStreets:
case MapBoxStreetsBasic:
case MapBoxRunBikeHike:
return AVERAGE_MAPBOX_STREET_MAP;
case GoogleLabels:
case MapBoxDark:
case MapBoxLight:
case MapBoxOutdoors:
case MapBoxPencil:
case OpenStreetMap:
case GoogleHybrid:
case MapBoxComic:
case MapBoxEmerald:
case MapBoxHighContrast:
case MapBoxHybrid:
case MapBoxPirates:
case MapBoxWheatPaste:
default:
break;
}
return AVERAGE_TILE_SIZE;
}