有注解的地图程序

Cityscape应用程序显示了城市中的建筑物、街区和公园的虚拟地图,最为重要的那些建筑会用它们的名字加以标注。它可以让用户通过鼠标和键盘来移动和缩放地图。首先介绍Cityscape类,它提供了应用程序的主窗口。

Cityscape.h

#ifndef CITYSCAPE_H
#define CITYSCAPE_H#include <QMainWindow>class QGraphicsScene;
class CityView;class Cityscape : public QMainWindow
{Q_OBJECTpublic:Cityscape();private:void generateCityBlocks();QGraphicsScene *scene;CityView *view;
};#endif

此应用程序没有菜单栏和工具栏,它只是简单地使用 CityView 窗口部件来显示有标注的地图。 CityView 类从 QGraphicsView 类派生过来。

Cityscape.cpp

#include <QtGui>#include "annotation.h"
#include "cityblock.h"
#include "cityscape.h"
#include "cityview.h"Cityscape::Cityscape()
{scene = new QGraphicsScene(-22.25, -22.25, 1980, 1980);scene->setBackgroundBrush(QColor(255, 255, 238));generateCityBlocks();view = new CityView;view->setScene(scene);setCentralWidget(view);setWindowTitle(tr("Cityscape"));
}void Cityscape::generateCityBlocks()
{QSet<QString> names;names << "Adams" << "Agnew" << "Arthur" << "Breckinridge"<< "Buchanan" << "Burr" << "Bush" << "Calhoun" << "Carter"<< "Cheney" << "Cleveland" << "Clinton" << "Colfax"<< "Coolidge" << "Curtis" << "Dallas" << "Dawes"<< "Eisenhower" << "Fairbanks" << "Fillmore" << "Ford"<< "Garfield" << "Garner" << "Gerry" << "Gore" << "Grant"<< "Hamlin" << "Harding" << "Harrison" << "Hayes"<< "Hendricks" << "Hobart" << "Hoover" << "Humphrey"<< "Jackson" << "Jefferson" << "Johnson" << "Kennedy"<< "King" << "Lincoln" << "Madison" << "Marshall"<< "McKinley" << "Mondale" << "Monroe" << "Morton"<< "Nixon" << "Pierce" << "Polk" << "Quayle" << "Reagan"<< "Rockefeller" << "Roosevelt" << "Sherman" << "Stevenson"<< "Taft" << "Taylor" << "Tompkins" << "Truman" << "Tyler"<< "Van Buren" << "Wallace" << "Washington" << "Wheeler"<< "Wilson";QSetIterator<QString> i(names);for (int y = 0; y < 44; ++y) {for (int x = 0; x < 44; ++x) {int percentile;if (x > 20 && x < 24 && y > 20 && y < 24) {percentile = std::rand() % (std::rand() % 2 != 0? 10 : 100);} else if (x > 18 && x < 26 && y > 18 && y < 26) {percentile = std::rand() % (rand() % 3 != 0? 10 : 100);} else if (x > 15 && x < 29 && y > 15 && y < 29) {percentile = std::rand() % (std::rand() % 5 != 0? 10 : 100);} else {percentile = std::rand() % 100;}CityBlock::Kind kind;QString name;if (percentile == 0) {kind = CityBlock::Park;name = tr("%1 Park");} else if (percentile <= 2) {kind = CityBlock::SmallBuilding;} else if (percentile <= 4) {kind = CityBlock::Hospital;name = tr("%1 Hospital");} else if (percentile == 5) {kind = CityBlock::Hall;name = tr("%1 Hall");} else if (percentile <= 7) {kind = CityBlock::Building;name = tr("%1 Bldg");} else if (percentile <= 9) {kind = CityBlock::Tower;name = tr("%1 Tower");} else if (percentile <= 15) {kind = CityBlock::LShapedBlock;} else if (percentile <= 30) {kind = CityBlock::LShapedBlockPlusSmallBlock;} else if (percentile <= 70) {kind = CityBlock::TwoBlocks;} else {kind = CityBlock::BlockPlusTwoSmallBlocks;}CityBlock *block = new CityBlock(kind);block->setPos(QPointF(x * 44.5, y * 44.5));scene->addItem(block);if (!name.isEmpty()) {if (!i.hasNext())i.toFront();bool major = (std::rand() % 10 == 0);Annotation *annotation =new Annotation(name.arg(i.next()), major);annotation->setPos(block->pos());scene->addItem(annotation);}}}
}

Cityscape()
构造函数创建了一个QGraphicsScene,调用generateCityBlocks() 生成一幅地图。这幅地图包含2000个街区和200个标注。

首先将看到的是图形项的子类 CityBlock ,然后是图形项的子类Annotation,最后是图形视图的子类 CìtyView。

CtityBlock.h

#ifndef CITYBLOCK_H
#define CITYBLOCK_H#include <QColor>
#include <QGraphicsItem>
#include <QPainterPath>class QGradient;class CityBlock : public QGraphicsItem
{public:enum Kind { Park, SmallBuilding, Hospital, Hall, Building, Tower,LShapedBlock, LShapedBlockPlusSmallBlock, TwoBlocks,BlockPlusTwoSmallBlocks };CityBlock(Kind kind);QRectF boundingRect() const;void paint(QPainter *painter,const QStyleOptionGraphicsItem *option, QWidget *widget);private:int kind;QColor color;QPainterPath shape;
};#endif

一个城市的街区会有类型、颜色和形状属性。因为城市街区是不可选的,所以不用担心像前面例子中 Node 类那样去重新实现 shape() 函数。

CtityBlock.cpp

#include <QtGui>
#include <cmath>#include "cityblock.h"CityBlock::CityBlock(Kind kind)
{this->kind = kind;int green = 96 + (std::rand() % 64);int red = 16 + green + (std::rand() % 64);int blue = 16 + (std::rand() % green);color = QColor(red, green, blue);if (kind == Park) {color = QColor(192 + (std::rand() % 32), 255,192 + (std::rand() % 16));shape.addRect(boundingRect());} else if (kind == SmallBuilding) {QRectF block(-7.5, -7.5, 15, 15);block.moveBottomLeft(QPointF((std::rand() % 6) - 3,(std::rand() % 6) - 3));shape.addRect(block);} else if (kind == Hospital) {int a = (std::rand() % 6) + 10;int b = (std::rand() % 6) + 10;QPolygonF block;block << QPointF(-5, -a) << QPointF(-5, -5) << QPointF(-10, -5)<< QPointF(-10, 5) << QPointF(-5, 5)  << QPointF(-5, 10)<< QPointF(5, 10)  << QPointF(5, 5)   << QPointF(b, 5)<< QPointF(b, -5)  << QPointF(5, -5)  << QPointF(5, -a);shape.addPolygon(block);} else if (kind == Hall) {int padding1 = (std::rand() % 8) + 2;int padding2 = (std::rand() % 8) + 2;shape.addEllipse(boundingRect().adjusted(+padding1, +padding1,-padding2, -padding2));} else if (kind == Building) {shape.addRect(boundingRect());} else if (kind == Tower) {int padding1 = (std::rand() % 8) + 2;int padding2 = (std::rand() % 8) + 2;shape.addRect(boundingRect().adjusted(+padding1, +padding1,-padding2, -padding2));} else if (kind == LShapedBlock|| kind == LShapedBlockPlusSmallBlock) {int a = (std::rand() % 6) + 10;int b = (std::rand() % 6) + 10;int s = qMin(a, b) / 2;QPolygonF block;block << QPointF(-a, -a) << QPointF(-a, +a) << QPointF(-s, +a)<< QPointF(-s, -s) << QPointF(+b, -s) << QPointF(+b, -a);shape.addPolygon(block);if (kind == LShapedBlockPlusSmallBlock) {int inset = (std::rand() % 4) + 4;shape.addRect(QRectF(-s + inset, -s + inset, a, b));}} else if (kind == TwoBlocks) {int w1 = (std::rand() % 10) + 8;int h1 = (std::rand() % 28) + 8;int w2 = (std::rand() % 10) + 8;int h2 = (std::rand() % 24) + 8;shape.addRect(QRectF(-16, -16, w1, h1));shape.addRect(QRectF(-16 + w1 + 4, -16 + (std::rand() % 4),w2, h2));} else if (kind == BlockPlusTwoSmallBlocks) {int w1 = (std::rand() % 10) + 8;int h1 = (std::rand() % 28) + 8;int w2 = (std::rand() % 10) + 8;int h2 = (std::rand() % 10) + 8;int w3 = (std::rand() % 6) + 8;int h3 = (std::rand() % 6) + 8;int y = (std::rand() % 4) - 16;shape.addRect(QRectF(-16, -16, w1, h1));shape.addRect(QRectF(-16 + w1 + 4, y, w2, h2));shape.addRect(QRectF(-16 + w1 + 4,y + h2 + 4 + (std::rand() % 4), w3, h3));}
}QRectF CityBlock::boundingRect() const
{return QRectF(-20, -20, 40, 40);
}void CityBlock::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget * /* widget */)
{if (option->levelOfDetail < 4.0) {painter->fillPath(shape, color);} else {QLinearGradient gradient(QPoint(-20, -20), QPoint(+20, +20));int coeff = 105 + int(std::log(option->levelOfDetail - 4.0));gradient.setColorAt(0.0, color.lighter(coeff));gradient.setColorAt(1.0, color.darker(coeff));painter->fillPath(shape, gradient);}
}

CityBlock()
构造函数设置了一种随机颜色,可以根据需要显示的街区的类型生成一个合适的 QPainterPath()。

boundingRect()
每个街区都会占用一个40x40 的正方形空格,其中心点是(0,0)

paint()
在paint()函数中,我们用给定的 QPainter 绘制图形。需要区分两种情况:
⬤ 如果缩放因予比 4.0 小,则使用纯色填充图形。
⬤ 如果缩放因子是 4.0 或者更大,使用QLinearGradient 填充图形,以产生一种奇妙的光照效果。

QStyleOptionGraphicsltem 类的 levelOfDetail 成员函数存储一个浮点值,该值就是缩放因子。1.0意味着以原始大小显示场景,0.5 意味着以原始大小的一半显示场景,2.5 表示显示的大小是原始尺寸的 2.5 倍。使用"详细程度"信息可以让我们在那些放得太大以至于不能显示细节的场景中使用更快的绘图算法。

CityBlock 类很好用,但事实上,当场景缩放时造成项也被缩敢,这样就给显示文字的那些项带来了问题。一般情况下,我们不希望文字随着场景缩放。视图体系为这一问题提供了通用的解决方式,即使用 ItemIgnoresTransformation 标识。Annotation 类中就使用了此标识。

Annotation

#ifndef ANNOTATION_H
#define ANNOTATION_H#include <QGraphicsItem>
#include <QFont>class Annotation : public QGraphicsItem
{public:Annotation(const QString &text, bool major = false);void setText(const QString &text);QString text() const;QRectF boundingRect() const;void paint(QPainter *painter,const QStyleOptionGraphicsItem *option, QWidget *widget);private:QFont font;QString str;bool major;double threshold;int y;
};#endif

构造函数使用文字以及称作 major 的布尔型标识作为参数,这规定了该注解是主注解还是辅注解,还将影响到字体的大小。

Annotation.cpp

#include <QtGui>
#include <cmath>#include "annotation.h"Annotation::Annotation(const QString &text, bool major)
{font = qApp->font();font.setBold(true);if (major) {font.setPointSize(font.pointSize() + 2);font.setStretch(QFont::SemiExpanded);}if (major) {threshold = 0.01 * (40 + (std::rand() % 40));} else {threshold = 0.01 * (100 + (std::rand() % 100));}str = text;this->major = major;y = 20 - (std::rand() % 40);setZValue(1000);setFlag(ItemIgnoresTransformations, true);
}void Annotation::setText(const QString &text)
{prepareGeometryChange();str = text;update();
}QString Annotation::text() const
{return str;
}QRectF Annotation::boundingRect() const
{QFontMetricsF metrics(font);QRectF rect = metrics.boundingRect(str);rect.moveCenter(QPointF(0, y));rect.adjust(-4, 0, +4, 0);return rect;
}void Annotation::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget * /* widget */)
{if (option->levelOfDetail <= threshold)return;painter->setFont(font);QRectF rect = boundingRect();int alpha = int(30 * std::log(option->levelOfDetail));if (alpha >= 32)painter->fillRect(rect, QColor(255, 255, 255, qMin(alpha, 63)));painter->setPen(Qt::white);painter->drawText(rect.translated(+1, +1), str,QTextOption(Qt::AlignCenter));painter->setPen(Qt::blue);painter->drawText(rect, str, QTextOption(Qt::AlignCenter));
}

Annotation()
在构造函数中,假设主注解代表一个重要的建筑物或者地标,则首先可以把它设置成较大而且加粗的字体。阈值是通过伪随机方法计算出来的,比它小的注解不会被显示出来。主注解有较小的阈值,当场景缩小时,不太重要的注解会消失。

我们设置z的值为1000,以确保注解显示在最上面,而且使用 ItemIgoresTransformations 标识来确保无论场景被如何缩放,注解的大小都可以保持不变。

setText()
如果注解的文字需要修改,它可能比以前更长或者更短了,因此必须通知视图体系修改该项的尺寸。

boundingRect()
我们从注解的字体中取得字体规格信息,使用它们计算出文字的外接矩形。然后移动矩形的中心点到注解的y值的偏移位置,并且使矩形稍微大一些。外接矩形左边和右边的额外像素使文字相对于边框还有一些空间。

paint()
如果场景缩小的尺寸超过了注解的阈值,就不再绘制该注解。如果场景被放得足够大,就事先绘制一个半透明的白色矩形,这有助于在黑色的块上显示文字。

我们绘制文字两次,一次用白色,一次用蓝色。白色的文字在水平和竖直方向上平移一个像素来创建一个阴影效果,使文字更容易被读出来。

CityView.h

#ifndef CITYVIEW_H
#define CITYVIEW_H#include <QGraphicsView>class CityView : public QGraphicsView
{Q_OBJECTpublic:CityView(QWidget *parent = 0);protected:void wheelEvent(QWheelEvent *event);
};#endif

默认情况下,QGraphics View 类可以在需要的时候自动显示一些滚动条,但没有提供任何缩放场景的方法。因此,我们创建了微型 CityView 子类,以提供用户使用鼠标缩放的功能。

CityView.cpp

#include <QtGui>
#include <cmath>#include "cityview.h"CityView::CityView(QWidget *parent): QGraphicsView(parent)
{setDragMode(ScrollHandDrag);
}void CityView::wheelEvent(QWheelEvent *event)
{double numDegrees = -event->delta() / 8.0;double numSteps = numDegrees / 15.0;double factor = std::pow(1.125, numSteps);scale(factor, factor);
}

CityView()
设置拖动模式可支持通过鼠标拖动来滚动屏幕。

wheelEvent()
当用户滚动鼠标滚轴,就会触发滚轴事件。我们需要简单地计算出一个适当的比例因子,然后调用机QGraphicsView::scale()。此数学公式有点巧妙,基本上是按滚轴步长的 1.125 倍上下移动场景。

main.cpp

#include <QApplication>#include "cityscape.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);Cityscape cityscape;cityscape.show();return app.exec();
}

Qt4_有注解的地图程序相关推荐

  1. java spring入门小程序_springMVC学习笔记(二)-----注解和非注解入门小程序

    最近一直在做一个电商的项目,周末加班,忙的都没有时间更新博客了.终于在上周五上线了,可以轻松几天了.闲话不扯淡了,继续谈谈springMvc的学习. 现在,用到SpringMvc的大部分使用全注解配置 ...

  2. springMVC学习笔记(二)-----注解和非注解入门小程序

    阅读目录 一:基础环境准备 二:非注解的处理器和映射器 三:注解的处理器映射器和适配器 四:springmvc.xml中的内容以及项目工程目录 最近一直在做一个电商的项目,周末加班,忙的都没有时间更新 ...

  3. 一、第一个注解的 SpringMVC 程序

    ch01-hello-springmvc:第一个注解的springmvc项目 所谓 SpringMVC 的注解式开发是指,在代码中通过对类与方法的注解,便可完成处理器 在 springmvc 容器的注 ...

  4. 自定义注解!绝对是程序员装大佬的利器!!

    作者 l Hollis 来源 l Hollis(ID:hollischuang) 相信很多人对Java中的注解都很熟悉,比如我们经常会用到的一些如@Override.@Autowired.@Servi ...

  5. 微信小程序 地图组件使用

    本地图程序利用map组件,以及使用它的markers,polyline属性分别设置地图两个位置的标志以及他们之间的连线,利用腾讯地图的API对两个地点进行查询,动态设置地图的经纬度,以及动态初始化ma ...

  6. 自定义注解!绝对是程序员装逼的利器!!

    GitHub 18k Star 的Java工程师成神之路,不来了解一下吗! GitHub 18k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 18k Star 的Java工 ...

  7. springmvc注解入门程序

    springnvc注解的入门程序 上一篇写的是非注解的这里补充一下注解的,相比来说还是注解简单,但是个人感觉还是非注解的好理解 建议看这篇的时候,先看看上一章,这里主要是贴代码 1.web.xml是一 ...

  8. ios 实现谷歌地图

    摘自博文:http://tergol.blog.163.com/blog/static/170695028201081961057870 / Google地图实现之一 在iphone中可以用core  ...

  9. 贪吃蛇c++程序(A*算法自动追踪功能)

    需要源程序的可以关注评论我 我会给大家邮箱的形式发送~ 目录 一.摘要 二.概述 三.方法论证和比较 1.方案一:深度学习算法 2.方案二:广度优先搜索算法 3.方案三:A*算法 四.理论分析即代码 ...

最新文章

  1. 何时开始phonics学习及配套阅读训练zz
  2. 人生第一次手术:0806
  3. python合法标识符_python_判断标识符的合法性
  4. 阿里云天池 Python训练营Task1:从变量到异常处理
  5. C++(STL):10---vector空间分配
  6. 苹果新的编程语言 Swift 语言进阶(二)--基本数据类型
  7. P3195 [HNOI2008]玩具装箱TOY DP+优化
  8. 判断Sbo的Matrix中是否存在相同数据行
  9. Dreamweaver CS5.5+PhoneGap移动开发环境搭建
  10. python函数求导_python_exp
  11. Spark核心类:SQLContext和DataFrame
  12. 安全公司-* * * *-面试题:_ 安卓逆向分析分享
  13. Gnu Screen用法 【转】
  14. 樊昌信通信原理第7版笔记和课后习题答案
  15. 基于html评定奖学金页面代码,学校助学奖学金申请表页面模板
  16. excel单元格内容拆分_excel软件应用:如何妙用Word拆分单元格数据
  17. PHP MVC及模板引擎
  18. Plague Inc
  19. 输出教师信息c语言作业,C语言教师管理系统代码(最新整理)
  20. 读书笔记《能力陷阱》第四章:试着朝更多不同的方向发展自己

热门文章

  1. binwalk 提取bootimg_boot.img的解包与打包
  2. python灰度图cv2到plt变颜色_python中plt.imshow与cv2.imshow显示颜色问题
  3. 如何在cocoapods中使用更新版本的pod
  4. LucasExlucas
  5. 一种新的子波域滤波算法
  6. JavaWeb——Servlet开发3
  7. php ffmpeg
  8. 今天加入了“宇宙通史:木星”
  9. 微软发布Sample Browser for Windows 8版:5000示例代码,触手可及
  10. JS:ES6-11 数值扩展与对象扩展