一、问题
QGC的地图在国内某些地方,只用bing可用,其他都不能使用。而且bing卫星图也没有道路和标签信息,还死慢死慢的。据说bing其实就没有国内的卫星地图版权,在网页上查查看bing地图,时没有卫星图的,只有切换到en-US上才有卫星图。
二、解决
国内的地图百度、高德、腾讯、搜狗、天地图,有标签、速度快,看上去不错,但只是看上去。两个问题(其实是3个):地图更新太慢、最大到18级就没有了效果不好。
谷歌地图本来在QGC上就有,奈何QGC上集成的是www.google.com/maps
所以上不了,但是谷歌在国内是有地图服务许可的,卫星图效果非常好,可以支持到24级(再大没有试),远大于QGC默认编译的20级,速度也非常快。对比了一下Google地图至少在我上班这个地方卫星图是最新的。
三、步骤
1. 找瓦片(title/切片)
- 百度 谷歌地图(http://www.google.cn/maps)
- 现在谷歌地图国内好像也没有服务了,仅供参考
菜单选卫星图像
-
浏览器 F12 看网络
之前的URL都失效了,这里特别感谢CSDN上CX0660同学,提供了URL
找到卫星切片地址(西安钟楼)
http://mt2.google.cn/vt/lyrs=s@847&gl=cn&x=52601&y=26122&z=16
解释下参数
- x/y:web墨卡托横纵坐标
- z 就是地图的层级 zoom
- lyrs 可以去以下几个值,lyrs=s@847写成lyrs=s一样ok
m:路线图
t:地形图
p:带标签的地形图
s:卫星图
y:带标签的卫星图
h:标签层(路名、地名等)
找到无偏移瓦片URL
把参数lyrs替换一下发现都能用(xyz,就不要手动改了,不知道切片规则的话就是404)
坑点:国内地图都是带偏移的,谷歌为了拿到地图服务许可,也必须入乡随俗,本来当个地图看没问题,但真要用到飞行上就偏得太远,你用这个瓦片地址到QGC上定位就知道了。而且这种“火星坐标”最大得问题是每个地方偏移量还不一样,需要专门做数据库,还要改大量代码,不值当。那么google.cn有没有无偏移得地图呢?还真有,上面url去掉gl=cn这个参数就ok,手贱试出来的,注意只对卫星图有效,也就是lyrs=s有效,另外lyrs=y在卫星图上无偏移,标签道路上有偏移,这个也算比较理想的地图了吧,你可以自己改参数试试。-
能用的url
- 卫星无偏移:
http://mt2.google.cn/vt/lyrs=s&x=52601&y=26122&z=16
- 卫星混合,标签偏移:
http://mt2.google.cn/vt/lyrs=y&x=52601&y=26122&z=16
- 卫星混合偏移:
http://mt2.google.cn/vt/lyrs=y&gl=cn&x=52601&y=26122&z=16
- 地形带标签(反正大于15层也不显示等高线了,偏移看不出来):
http://mt2.google.cn/vt/lyrs=p&gl=cn&x=6051&y=2997&z=13
- 街道偏移(当个地图也可以):
http://mt2.google.cn/vt/lyrs=m&gl=cn&x=52601&y=26122&z=16
- 卫星无偏移:
-
其他地图瓦片
ArcGis(esri)已经继承到QGC中,因为没有Token,所以不显示,但是下面两个瓦片不需要token,支持到19级,需要把判断esri token的地方注释掉
- ArcGis街道
http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile//16/26122/52601
- ArcGis卫星
http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/16/26122/52601
- ArcGis街道
2. 更新20200412,基于4.0.5修改源码
QGC 4.0 以后修改就方便很多了
2.1 首先当然是下源码
#下载
git clone https://github.com/mavlink/qgroundcontrol.git --recursive
#查看tag
git tag
#查看分支
git branch
#以v4.0.5稳定版为蓝本创建分支,随便取个名字叫cunstom405
git checkout -b cunstom405 v4.0.5
2.2 尝试编译
要搭建编译环境,打开QT Creator,把工程编译一遍,这一步过不去后面的就呵呵了,这也是一个比较坑爹的地方,QGC官方给的说明不太对,请参考下面的文档:
-
工程目录/.travis.yml
对应qt版本和安卓ndk版本在第二个文档内是肯定正确的,github使用Travis-CI自动编译
2.3 添加Google.cn地图 ,可参照此办法添加其它地图
-
创建头文件
src/QtLocationPlugin/GoogleCnMapProvider.h
,添加google.cn定义这里就不写CPP文件了,就这么几行代码,哈哈,野路子走惯了
#ifndef GOOGLECNMAPPROVIDER_H
#define GOOGLECNMAPPROVIDER_H
#include "MapProvider.h"
#include <QNetworkReply>
#include <QMutex>
static const quint32 AVERAGE_GOOGLE_CN_SAT_MAP = 56887;
static const quint32 AVERAGE_GOOGLE_CN_TERRAIN_MAP = 19391;
class GoogleCNSatelliteMapProvider : public MapProvider {
Q_OBJECT
public:
GoogleCNSatelliteMapProvider(QObject* parent = nullptr)
: MapProvider(QStringLiteral("http://www.google.cn/maps"), QStringLiteral("jpg"),
AVERAGE_GOOGLE_CN_SAT_MAP, QGeoMapType::SatelliteMapDay, parent) {}
protected:
QString _getURL(const int x, const int y, const int zoom, QNetworkAccessManager* networkManager) override{
Q_UNUSED(networkManager)
return QString("http://mt2.google.cn/vt/lyrs=s&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
}
};
class GoogleCNHybridMapProvider : public MapProvider {
Q_OBJECT
public:
GoogleCNHybridMapProvider(QObject* parent = nullptr)
: MapProvider(QStringLiteral("http://www.google.cn/maps"), QStringLiteral("jpg"),
AVERAGE_GOOGLE_CN_TERRAIN_MAP, QGeoMapType::SatelliteMapDay, parent) {}
protected:
QString _getURL(const int x, const int y, const int zoom, QNetworkAccessManager* networkManager) override{
Q_UNUSED(networkManager)
return QString("http://mt2.google.cn/vt/lyrs=y&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
}
};
class GoogleCNTerrainMapProvider : public MapProvider {
Q_OBJECT
public:
GoogleCNTerrainMapProvider(QObject* parent = nullptr)
: MapProvider(QStringLiteral("http://www.google.cn/maps"), QStringLiteral("png"),
AVERAGE_GOOGLE_CN_TERRAIN_MAP, QGeoMapType::TerrainMap, parent) {}
protected:
QString _getURL(const int x, const int y, const int zoom, QNetworkAccessManager* networkManager) override{
Q_UNUSED(networkManager)
return QString("http://mt2.google.cn/vt/lyrs=p&gl=cn&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
}
};
#endif // GOOGLECNMAPPROVIDER_H
- 将定义好的地图添加到地图列表中去
src/QtLocationPlugin/QGCMapUrlEngine.cpp
...
#include "GoogleCnMapProvider.h"
...
UrlFactory::UrlFactory() : _timeout(5 * 1000) {
...
//位置随便加,APP中使用QHash没有排序
//第一个空格前是供应商名称,而后是地图类型
_providersTable["Google.cn 卫星"] = new GoogleCNSatelliteMapProvider(this);
_providersTable["Google.cn 混合 标签偏移"] = new GoogleCNHybridMapProvider(this);
_providersTable["Google.cn 地形"] = new GoogleCNTerrainMapProvider(this);
...
}
...
不像之前地图列表是没有顺序的,如果想要排序,自行修改,这里就不改了好像也没啥影响
// src/QtLocationPlugin/QGCMapEngine.cpp
// 可修改地图供应商的排序,这里未修改
QStringList
QGCMapEngine::getMapNameList()
{
return QStringList(getQGCMapEngine()->urlFactory()->getProviderTable().keys());
}
// src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc
// 可修改地图供应商的类型排序,这里未修改
QStringList
QGCMapEngineManager::mapTypeList(QString provider)
{
// Extract type name from MapName ( format : "Provider Type")
QStringList mapList = getQGCMapEngine()->getMapNameList();
mapList = mapList.filter(QRegularExpression(provider));
mapList.replaceInStrings(QRegExp("^([^\\ ]*) (.*)$"),"\\2");
mapList.removeDuplicates();
return mapList;
}
2.4 编译、测试、收工
可以按照设计正常显示地图,本来还想加OpenStreet,在浏览器上没问题,但在QGC上死活不出来,应该是OpenStreet没有许可只能在浏览器上使用。
以下4.0之前的修改方式,仅供参考------------------------------------------------------------
2. 改源码,基于3.5.3修改
2.1 克隆源码
#下载
git clone https://github.com/mavlink/qgroundcontrol.git --recursive
#查看tag
git tag
#查看分支
git branch
#以v3.5.3稳定版为蓝本创建分支,随便取个名字叫cunstom353
git checkout -b cunstom353 v3.5.3
2.2 修改
-
/src/Settings/FlightMap.SettingsGroup.json
用于显示
[
{
"name": "mapProvider",
"shortDescription": "Currently selected map provider for flight maps",
"type": "uint32",
"enumStrings": "Google.cn,Esri",
"enumValues": "0,1",
"defaultValue": 0
},
{
"name": "mapType",
"shortDescription": "Currently selected map type for flight maps,* means can't use to feight",
"type": "uint32",
"enumStrings": "卫星,卫星混合(仅标签偏移),*卫星混合(全偏移),街道,*街道(偏移),地形,*地形(偏移)",
"enumValues": "0,1,2,3,4,5,6",
"defaultValue": 0
}
]
-
/src/Settings/FlightMapSettings.h
定义地图提供商和地图类型
...
// 必须和上json的数据个数顺序对应
typedef enum {
mapProviderGoogle,
mapProviderEsri
} MapProvider_t;
// 必须和上json的数据个数顺序对应
typedef enum {
mapTypeSatellite,
mapTypeSatelliteMix,
maptypeSatelliteMixOffset,
mapTypeStreet,
mapTypeStreetOffset,
mapTypeTerrain,
mapTypeTerrainOffset
} MapType_t;
...
/src/Settings/FlightMapSettings.cc
/* 33:这两处注释掉 不然esri地图显示不出来
if(qgcApp()->toolbox()->settingsManager()->appSettings()->mapboxToken()->rawValue().toString().isEmpty()) {
_excludeProvider(mapProviderMapbox);
}
if(qgcApp()->toolbox()->settingsManager()->appSettings()->esriToken()->rawValue().toString().isEmpty()) {
_excludeProvider(mapProviderEsri);
}
*/
//80:地图提供商没有的地图类型
void FlightMapSettings::_newMapProvider(QVariant value)
{
FactMetaData* metaData = _nameToMetaDataMap[mapTypeName];
QStringList enumStrings = _savedMapTypeStrings;
QVariantList enumValues = _savedMapTypeValues;
switch (value.toInt()) {
case mapProviderGoogle:
_removeEnumValue(mapTypeStreet,enumStrings,enumValues);
_removeEnumValue(mapTypeStreetOffset,enumStrings,enumValues);
_removeEnumValue(mapTypeTerrain,enumStrings,enumValues);
break;
case mapProviderEsri:
_removeEnumValue(mapTypeSatelliteMix,enumStrings,enumValues);
_removeEnumValue(maptypeSatelliteMixOffset,enumStrings,enumValues);
_removeEnumValue(mapTypeStreetOffset,enumStrings,enumValues);
_removeEnumValue(mapTypeTerrain,enumStrings,enumValues);
_removeEnumValue(mapTypeTerrainOffset,enumStrings,enumValues);
break;
...
-
/src/QtLocationPlugin/QGCMapUrlEngine.h
原来没有的地图类型加到URL库中
...
//41:卫星混合(全偏移)
GoogleStatelliteWithLable = 21,
//卫星混合(标签偏移)
GoogleStatelliteWithLableNofix = 22,
...
-
/src/QtLocationPlugin/QGCMapUrlEngine.cpp
定义瓦片URL
...
//230
case GoogleMap:
return QString("http://www.google.cn/maps/vt?lyrs=m&gl=cn&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
case GoogleSatellite:
return QString("http://www.google.cn/maps/vt?lyrs=s&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
case GoogleLabels:
return QString("http://www.google.cn/maps/vt?lyrs=h&gl=cn&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
case GoogleTerrain:
return QString("http://www.google.cn/maps/vt?lyrs=p&gl=cn&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
case GoogleStatelliteWithLable:
//卫星混合(全偏移)
return QString("http://www.google.cn/maps/vt?lyrs=y&gl=cn&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
case GoogleStatelliteWithLableNofix:
//卫星混合(标签偏移)
return QString("http://www.google.cn/maps/vt?lyrs=y&x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom);
...
case EsriWorldStreet:
return QString("http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/%1/%2/%3").arg(zoom).arg(y).arg(x);
case EsriWorldSatellite:
return QString("http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/%1/%2/%3").arg(zoom).arg(y).arg(x);
...
-
/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp
将前台地图类型和后台URL连接,第一个字符串是 供应商名+一个空格+地图类型,第二个字符串是简介,随便写
//108:卫星(无偏移)
mapTypes << QGCGEOMAPTYPE(QGeoMapType::SatelliteMapDay, "Google.cn 卫星", "Google.cn 卫星", false, false, UrlFactory::GoogleSatellite);
mapTypes << QGCGEOMAPTYPE(QGeoMapType::TerrainMap, "Google.cn *地形(偏移)", "Google.cn *地形(偏移)", false, false, UrlFactory::GoogleTerrain);
//卫星混合(全偏移)
mapTypes << QGCGEOMAPTYPE(QGeoMapType::SatelliteMapDay, "Google.cn *卫星混合(全偏移)", "Google.cn *卫星混合(全偏移)",false, false, UrlFactory::GoogleStatelliteWithLable);
//卫星混合(标签偏移)
mapTypes << QGCGEOMAPTYPE(QGeoMapType::SatelliteMapDay, "Google.cn 卫星混合(仅标签偏移)","Google.cn 卫星混合(仅标签偏移)", false, false, UrlFactory::GoogleStatelliteWithLableNofix);
// Esri
mapTypes << QGCGEOMAPTYPE(QGeoMapType::StreetMap, "Esri 街道", "ArcGIS Online World 街道", true, false, UrlFactory::EsriWorldStreet);
mapTypes << QGCGEOMAPTYPE(QGeoMapType::SatelliteMapDay, "Esri 卫星", "ArcGIS Online World 卫星", true, false, UrlFactory::EsriWorldSatellite);
...
-
/src/QtLocationPlugin/QGCMapEngine.cpp
修改离线地图
//48:字符串要求同上节
stQGeoTileCacheQGCMapTypes kMapTypes[] = {
{"Google.cn 卫星", UrlFactory::GoogleSatellite},
{"Google.cn 卫星混合(仅标签偏移)", UrlFactory::GoogleStatelliteWithLableNofix},
{"Google.cn *卫星混合(全偏移)", UrlFactory::GoogleStatelliteWithLable},
{"Google.cn *地形(偏移)", UrlFactory::GoogleTerrain},
{"Esri 卫星", UrlFactory::EsriWorldSatellite},
{"Esri 街道", UrlFactory::EsriWorldStreet},
};
//380:注释掉下面这两处,好像不注释也没问题
QStringList
QGCMapEngine::getMapNameList()
{
QStringList mapList;
for(size_t i = 0; i < NUM_MAPS; i++) {
mapList << kMapTypes[i].name;
}
/*
if(!qgcApp()->toolbox()->settingsManager()->appSettings()->mapboxToken()->rawValue().toString().isEmpty()) {
for(size_t i = 0; i < NUM_MAPBOXMAPS; i++) {
mapList << kMapboxTypes[i].name;
}
}
if(!qgcApp()->toolbox()->settingsManager()->appSettings()->esriToken()->rawValue().toString().isEmpty()) {
for(size_t i = 0; i < NUM_ESRIMAPS; i++) {
mapList << kEsriTypes[i].name;
}
}*/
return mapList;
}