





















之前做地图控件的时候,暂时只是把基础功能做好,最基础的当然是自动按照经纬度坐标和缩放级别,加载对应的瓦片地图图片文件并绘制,其次是自定义绘制图层,可以在上面绘制折线、多边形、圆形、标注点等,然后这些图形可以支持编辑状态调整位置和范围大小等。近期客户希望能增加测距的功能,之前就已经内置了测距的函数接口,需要用户自己传入两个经纬度坐标,自动计算距离,但是其实用户很多时候,希望直接在地图界面上直接画线测距,所以花费了点时间把这个功能搞定。
测距其实就是在画折线基础上,对每一条线段,计算出对应的距离,每一条线段的距离数值显示在对应线段的结束点的位置,第一个点显示起点,末尾一个点显示所有的总距离,参考了目前市面上的百度地图、高德地图、腾讯地图等,都是按照这个模式进行的。思路有了立即开干,在原有的折线图形类增加distancemode变量,标记当前折线是否开启测距,开启了,则绘制的时候,默认要绘制每个点的手柄,可以是方形也可以是圆形,这个句柄可以参照线条的粗细自动变化。在每个点的位置的右侧绘制文本,自动累加距离,最后一个点显示总距离文本,打完收工超级完美,还可以处于编辑状态,拉伸调整并拖动整体位置,实时测距。

#include "overlaypolyline.h"
#include "overlaybasehelper.h"
#include "maputil.h"
OverlayPolyline::OverlayPolyline(const QVector<PointWorldCoord> &points, int zoom_min, int zoom_max)
: OverlayBase(OverlayType_Polyline, zoom_min, zoom_max)
{
this->m_arrow_size = 15;
this->m_arrow_angle = 30;
this->m_arrow_position = 0.5;
this->m_arrow_center = true;
this->m_arrow_visible = false;
this->m_distance_mode = false;
this->m_pen_width = 3;
this->setPoints(points);
}
OverlayPolyline::~OverlayPolyline()
{
}
QVector<PointWorldCoord> OverlayPolyline::points() const
{
return m_points;
}
void OverlayPolyline::setPoints(const QVector<PointWorldCoord> &points, bool redraw)
{
m_points = points;
//每次更新完坐标立即更新多边形区域
m_polygon.clear();
foreach (PointWorldCoord point, m_points) {
m_polygon.append(point.rawPoint());
}
m_rect = m_polygon.boundingRect();
if (redraw) {
emit requestRedraw();
}
}
void OverlayPolyline::addPoint(const PointWorldCoord &point, bool redraw)
{
m_points << point;
this->setPoints(m_points, redraw);
}
void OverlayPolyline::updatePoint(const PointWorldCoord &point, int index, bool redraw)
{
if (index < points().count()) {
m_points[index] = point;
this->setPoints(m_points, redraw);
}
}
int OverlayPolyline::arrowSize() const
{
return m_arrow_size;
}
void OverlayPolyline::setArrowSize(int size, bool redraw)
{
if (m_arrow_size != size) {
m_arrow_size = size;
if (redraw) {
emit requestRedraw();
}
}
}
int OverlayPolyline::arrowAngle() const
{
return m_arrow_angle;
}
void OverlayPolyline::setArrowAngle(int angle, bool redraw)
{
if (m_arrow_angle != angle) {
m_arrow_angle = angle;
if (redraw) {
emit requestRedraw();
}
}
}
qreal OverlayPolyline::arrowPosition() const
{
return m_arrow_position;
}
void OverlayPolyline::setArrowPosition(qreal position, bool redraw)
{
if (m_arrow_position != position) {
m_arrow_position = position;
if (redraw) {
emit requestRedraw();
}
}
}
bool OverlayPolyline::arrowCenter() const
{
return m_arrow_center;
}
void OverlayPolyline::setArrowCenter(bool center, bool redraw)
{
if (m_arrow_center != center) {
m_arrow_center = center;
if (redraw) {
emit requestRedraw();
}
}
}
bool OverlayPolyline::arrowVisible() const
{
return m_arrow_visible;
}
void OverlayPolyline::setArrowVisible(bool visible, bool redraw)
{
if (m_arrow_visible != visible) {
m_arrow_visible = visible;
if (redraw) {
emit requestRedraw();
}
}
}
bool OverlayPolyline::distanceMode() const
{
return m_distance_mode;
}
void OverlayPolyline::setDistanceMode(bool distance, bool redraw)
{
if (m_distance_mode != distance) {
m_distance_mode = distance;
if (redraw) {
emit requestRedraw();
}
}
}
int OverlayPolyline::handIndex() const
{
return m_hand_index;
}
qreal OverlayPolyline::distance() const
{
return MapUtil::getDistance(points());
}
QRectF OverlayPolyline::closeRect() const
{
return m_close_rect;
}
QRectF OverlayPolyline::toQRectF() const
{
return m_rect;
}
QPolygonF OverlayPolyline::toQPolygonF() const
{
return m_polygon;
}
RectWorldCoord OverlayPolyline::boundingBox(int /*cur_zoom*/) const
{
return RectWorldCoord::fromQRectF(toQRectF());
}
bool OverlayPolyline::touches(const QPointF &mouse_point, int cur_zoom)
{
if (m_points.size() <= 1) {
return false;
}
//优先判断编辑状态下按下的区域
if (this->isEdit()) {
m_hand_index = -1;
for (int i = 0; i < m_hand_rects.size(); ++i) {
if (m_hand_rects.at(i).contains(mouse_point)) {
m_hand_index = i;
return true;
}
}
}
//将点坐标转成路径
QPainterPath path;
path.moveTo(m_points.at(0).rawPoint());
for (int i = 1; i < m_points.size(); ++i) {
path.lineTo(m_points.at(i).rawPoint());
}
//当前缩放级别下/每个像素表示的纬度距离
qreal degress = MapUtil::getDegreesPerPixel(cur_zoom);
qreal width = m_pen_width * 2 * degress;
//放大路径/方便生成轮廓/用于判断点是否在里面
QPainterPathStroker stroker;
stroker.setWidth(width);
QPainterPath strokePath = stroker.createStroke(path);
QList<QPolygonF> polygons = strokePath.toSubpathPolygons();
bool exist = polygons.first().containsPoint(mouse_point, Qt::OddEvenFill);
return exist;
}
void OverlayPolyline::draw(QPainter *painter, const RectWorldCoord &backbuffer_rect, int cur_zoom)
{
//当前多边形区域在缓存区域内则绘制/转换成世界坐标再绘制
if (backbuffer_rect.rawRect().intersects(toQRectF())) {
QPolygonF points;
foreach (PointWorldCoord point, m_points) {
points << coordToPoint(point, cur_zoom);
}
int count = points.size();
m_pen_width = pen().widthF();
painter->setPen(pen());
painter->drawPolyline(points);
//绘制编辑状态
if (this->isEdit() || this->distanceMode()) {
m_hand_rects.clear();
int size = this->pen().width() + 2;
int style = this->distanceMode() ? 1 : 0;
foreach (QPointF point, points) {
m_hand_rects << this->drawHand2(painter, point, cur_zoom, size, style, Qt::black);
}
#if 0
//绘制两点中间点/用于动态增加点
for (int i = 0; i < count - 1; ++i) {
QPointF point = (points.at(i) + points.at(i + 1)) / 2.0;
this->drawHand(painter, point, cur_zoom);
}
#endif
}
//绘制箭头
if (this->arrowVisible()) {
for (int i = 0; i < count - 1; ++i) {
QPolygonF arrow = OverlayBaseHelper::calculateArrowPoints(points.at(i), points.at(i + 1), m_arrow_size, m_arrow_angle, m_arrow_position, m_arrow_center);
painter->drawPolyline(arrow);
}
}
//绘制距离
if (this->distanceMode()) {
int offset = 8;
qreal distance = 0;
for (int i = 0; i < count; ++i) {
QString text;
QColor border = Qt::black;
if (i == 0) {
text = "起点";
} else if (i == count - 1) {
//末尾点突出颜色显示
border = Qt::red;
distance += MapUtil::getDistance(m_points.at(i - 1), m_points.at(i), true);
text = QString("总长: %1 公里 X").arg(distance);
} else {
//计算当前点和上一个点的距离
distance += MapUtil::getDistance(m_points.at(i - 1), m_points.at(i), true);
text = QString("%1 公里").arg(distance);
}
//计算文本的尺寸和区域并绘制
QSize size = this->initText(painter, text) + QSize(offset, 3);
QRectF rect(points.at(i) + QPointF(offset, -offset), size);
OverlayBaseHelper::drawText(painter, rect, text, border, Qt::white, Qt::black);
//计算关闭区域/文字区域右侧部分
if (i == count - 1) {
m_close_rect = rect.adjusted(rect.width() - 15, 0, 0, 0);
//painter->fillRect(m_close_rect, Qt::red);
m_close_rect = MapProjection::pointRectToCoordRect(m_close_rect, cur_zoom);
}
}
}
}
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。