























之前已经花了巨大的精力,把纯qwidget采用painter绘制的电子地图控件写完,已经支持了多级图层叠加,最上层一般是图形绘制层,可以绘制折线、多边形、矩形、圆形等覆盖物,底图一般是瓦片电子地图,可以是天地图高德地图等,前几年就已经有不少人问过是否支持海图,近期有空刚好可以把这块研究起来,核心关键就是要拿到海图的瓦片地址,最好是在线的,类似天地图这样的,最好是免费的,百度地图吃相就很难看,现在估计绝大部分人都转移到天地图上面来了,不仅标准的gps坐标系,而且卫星图还支持到18级,既免费又真香,瓦片下载下来后,还可以离线使用。
为了支持在线海图的瓦片,之前的电子地图控件做了一些改动,比如需要支持经纬度投影,之前默认按照默卡托投影来的,而不少厂商提供的是经纬度投影,现在需要改成每个控件对象都能设置投影系类型。经过测试发现,还需要支持512像素的瓦片,之前默认按照256像素的瓦片实现的,因为市面上绝大部分的都是256像素的,而目前找的海图的瓦片很可能是512像素的。经过不断的重组和调整重构,目前已经能够支持默认在天地图上叠加海图,当然也支持任意地图作为底图,而且支持任意多个底图,比如最底层是矢量底图、然后是海图、最上层是矢量标记图,也可以叫路网图,这样才是最完整的叠加顺序。



#include "mapadapter.h"
#include "mapprojection.h"
#include "tilehelper.h"
MapAdapter::MapAdapter(int min_zoom, int max_zoom, QObject *parent) : QObject(parent),
m_min_zoom(min_zoom),
m_max_zoom(max_zoom)
{
}
MapAdapter::~MapAdapter()
{
}
QString MapAdapter::url() const
{
return m_url;
}
void MapAdapter::setUrl(const QString &url)
{
m_url = url;
m_ids.clear();
this->replaceKey(m_url, true);
this->replaceId(m_url, m_ids);
}
QStringList MapAdapter::ids() const
{
return m_ids;
}
void MapAdapter::setIds(const QStringList &ids)
{
m_ids = ids;
}
bool MapAdapter::isTileValid(int x, int y, int z) const
{
int zoom = z;
bool success = false;
if (m_min_zoom <= m_max_zoom && (zoom < m_min_zoom || zoom > m_max_zoom)) {
} else if (m_min_zoom > m_max_zoom && (zoom < m_max_zoom || zoom > m_min_zoom)) {
} else if (x >= 0 && x < MapProjection::tileX(z) && y >= 0 && y < MapProjection::tileY(z)) {
success = true;
}
return success;
}
QUrl MapAdapter::tileQuery(int x, int y, int z) const
{
QString url = m_url;
//替换特殊地址
this->replaceUrl(url, z);
//替换地图秘钥
this->replaceKey(url, false);
//替换主机编号
int size = m_ids.size();
if (size > 0 && url.contains("{n}")) {
url.replace("{n}", m_ids.at(rand() % size));
}
//反转TMS坐标系
if (url.contains("{-y}")) {
y = (1 << z) - 1 - y;
url.replace("{-y}", "{y}");
}
//替换微软地图
if (url.contains("{x}{y}{z}")) {
QString xyz = this->xyzToQuadKey(x, y, z);
url.replace("{x}{y}{z}", xyz);
}
//替换腾讯地图
//这种地址需要替换xy http://p0.map.gtimg.com/sateTiles/z/x/y/x_y.png
//这种地址只需要替换y http://rt0.map.gtimg.com/tile?styleid=1&z=10&x=861&y=417
if (url.contains("map.gtimg.com")) {
y = (1 << z) - 1 - y;
if (url.contains("{x}_{y}")) {
url.replace("{x/16}", QString::number(x / 16));
url.replace("{y/16}", QString::number(y / 16));
}
}
//替换瓦片地址
url.replace("{x}", QString::number(x));
url.replace("{y}", QString::number(y));
url.replace("{z}", QString::number(z));
return QUrl(url);
}
void MapAdapter::replaceUrl(QString &url, int zoom)
{
if (url.contains("onechart.cn")) {
if (zoom <= 10) {
url.replace("WMTS_1_18", "WMTS_globe_1_10");
} else {
url.replace("WMTS_globe_1_10", "WMTS_1_18");
}
}
}
void MapAdapter::replaceKey(QString &url, bool replace)
{
//如果只有一个秘钥则直接替换掉占位符
if (url.contains("{key}")) {
QStringList keys;
if (url.contains("tianditu.gov.cn")) {
keys = TileHelper::tianKeys;
}
int size = keys.size();
if (size > 0) {
QString key = keys.at(rand() % size);
if (!replace || size == 1) {
url.replace("{key}", key);
}
}
}
}
void MapAdapter::replaceId(QString &url, QStringList &ids)
{
//如果带了指定主机编号的范围/先取出来/{n-0-5-n}/处理后恢复占位符
int index1 = url.indexOf("{n-");
int index2 = url.indexOf("-n}");
if (index1 > 0 && index2 > 0) {
QString range = url.mid(index1 + 3, index2 - index1 - 3);
if (range.contains('-')) {
QChar min = range.at(0);
QChar max = range.at(2);
for (QChar c = min; c <= max; c = QChar(c.unicode() + 1)) {
ids << QString(c);
}
url.replace(QString("{n-%1-n}").arg(range), "{n}");
}
}
}
QString MapAdapter::xyzToQuadKey(int x, int y, int z)
{
QString quadKey;
for (int i = z; i > 0; i--) {
char digit = '0';
int mask = 1 << (i - 1);
if ((x & mask) != 0) {
if ((y & mask) != 0) {
digit = '3';
} else {
digit = '1';
}
} else {
if ((y & mask) != 0) {
digit = '2';
} else {
digit = '0';
}
}
quadKey.append(digit);
}
return quadKey;
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。