地图导航系统
一饭多吃.jpg
放个最初版本,A*求最短路
#include "Logger.h"
#include "ColorUtils.h"
#include "ImgUtils.h"
#include<graphics.h>
#include<windows.h>
#include<iostream>
#include<conio.h>
#include<cstdio>
#include<cmath>
#include<queue>
#include <thread>
#define DIS_PERCENT 1.2
#define WALK_PERCENT 65
#define INF 1000
using namespace std;
unsigned int unWidth = 0, unHeight = 0;
const double sqrt2 = sqrt(2.0);
IMAGE Map;
int lu[2333][2333], vis[2333][2333];
double dis[2333][2333];
string lastInf;
bool selected = false;
void mapToSpot()
{
memset(lu, 0, sizeof(lu));
for (int i = 0; i < unWidth; ++i)
for (int j = 0; j < unHeight; ++j)
if (ColorUtils::isTarget(i, j))lu[i][j] = 1;
}
struct dian
{
int x, y;
double dis_from, dis_to;
friend bool operator <(const dian& a, const dian& b)
{
return a.dis_from + a.dis_to > b.dis_from + b.dis_to;
}
}from[2333][2333];//用来找回来的路径
priority_queue<dian>q;
int dx[10] = { 0,-1,-1,1,1,-1,0,0,1 };
int dy[10] = { 0,-1,1,-1,1,0,-1,1,0 };
void DIJ(int X1, int Y1, int X2, int Y2)
{
while (!q.empty())q.pop();
memset(from, 0, sizeof(from));
memset(vis, 0, sizeof(vis));
for (int i = 0; i < unWidth; ++i)
for (int j = 0; j < unHeight; ++j) dis[i][j] = 114514;
dian now, to; double tmp_dis; now.x = X1; now.y = Y1; now.dis_from = 0;
dis[now.x][now.y] = 0;
q.push(now);
bool flag = false;
while (!q.empty() && !flag)
{
now = q.top(); q.pop();
if (vis[now.x][now.y])continue; vis[now.x][now.y] = 1;
setfillcolor(RGB(255, 0, 0));
solidcircle(now.x, now.y, 1);
for (int i = 1; i <= 8; ++i)
{
to.x = now.x + dx[i]; to.y = now.y + dy[i];
if (i <= 4)tmp_dis = now.dis_from + sqrt2;
else tmp_dis = now.dis_from + 1.0;
if (to.x < 0 || unWidth <= to.x || to.y < 0 || unHeight <= to.y || lu[to.x][to.y] == 0 || dis[to.x][to.y] <= tmp_dis)continue;
dis[to.x][to.y] = tmp_dis; to.dis_from = tmp_dis;
//Sleep(2);//用来展示寻路过程
to.dis_to = sqrt((double)(X2 - to.x) * (X2 - to.x) + (Y2 - to.y) * (Y2 - to.y));
from[to.x][to.y] = now;
q.push(to);
if (to.x == X2 && to.y == Y2) flag = true;
}
}
now.x = X2; now.y = Y2;
int prepts[5000] = { 0 };
putimage(0, 0, &Map);
while (now.x != X1 || now.y != Y1)
{
//Sleep(2);
setfillcolor(RGB(0, 0, 255));
solidcircle(now.x, now.y, 2);
now = from[now.x][now.y];
}
}
ExMessage m1, m2, m3;
void watch()
{
if (!selected)
{
cin >> m1.x >> m1.y;
cin >> m2.x >> m2.y;
selected = true;
}
}
void showMouse()
{
while (1)
{
ExMessage m;
if (peekmessage(&m))
{
setfillcolor(BLACK);
solidrectangle(0, 0, 170, 30);
setfillcolor(RGB(255, 0, 0));
string ch = "Mouse: " + to_string(m.x) + ' ' + to_string(m.y);
if (ColorUtils::isTarget(m.x, m.y))
{
ch += " Yes";
}
else
{
ch += " No";
}
int num = MultiByteToWideChar(0, 0, ch.c_str(), -1, NULL, 0);
wchar_t* wide = new wchar_t[num];
MultiByteToWideChar(0, 0, ch.c_str(), -1, wide, num);
outtextxy(0, 0, wide);
}
}
}
int main()
{
ImgUtils::GetPicWidthHeight("./ChangZhou.png", &unWidth, &unHeight);
initgraph(unWidth, unHeight, EW_SHOWCONSOLE);
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT | PS_JOIN_BEVEL, 3);
loadimage(&Map, _T(".\\ChangZhou.png"));
putimage(0, 0, &Map);
Logger::logInfo("预处理地图中...");
mapToSpot();
Logger::logSuccess("加载完毕!可以开始选点了!");
thread mouse(showMouse);
mouse.detach();
while (1)
{
selected = false;
setfillcolor(RGB(255, 0, 0));
thread input(watch);
input.detach();
while (!selected)
{
if (peekmessage(&m1))
{
if (m1.message == WM_LBUTTONDOWN && ColorUtils::isTarget(m1.x, m1.y))
{
solidcircle(m1.x, m1.y, 4);
if (m1.ctrl != true) break;
}
}
}
lastInf = "第一个点的坐标为: " + to_string(m1.x);
lastInf += " " + to_string(m1.y);
Logger::logInfo(lastInf);
while (!selected)
{
if (peekmessage(&m2))
{
if (m2.message == WM_LBUTTONDOWN && ColorUtils::isTarget(m2.x, m2.y))
{
solidcircle(m2.x, m2.y, 4);
selected = true;
break;
}
}
}
lastInf = "第二个点的坐标为: " + to_string(m2.x);
lastInf += " " + to_string(m2.y);
Logger::logInfo(lastInf);
double dur;
clock_t start, end;
start = clock();
Logger::logInfo("Starting A*...");
DIJ(m1.x, m1.y, m2.x, m2.y);
end = clock();
dur = (double)(end - start);
Logger::logSuccess("Use Time: " + to_string(dur / CLOCKS_PER_SEC));
while (1)
{
if (peekmessage(&m3)&& m3.message == WM_LBUTTONDOWN)break;
}
putimage(0, 0, &Map);
}
return 0;
}
最新版本
#include "Logger.h"
#include "ImgUtils.h"
#include<graphics.h>
#include<windows.h>
#include<iostream>
#include<conio.h>
#include<cstdio>
#include<cmath>
#include<queue>
#include <thread>
#define DIS_PERCENT 1.2
#define WALK_PERCENT 65
#define INF 1000
using namespace std;
unsigned int unWidth = 0, unHeight = 0;
const double sqrt2 = sqrt(2.0);
IMAGE Map;
int tot,cnt;
int lu[2333][2333], vis[2333 * 2333], headEdge[2333 * 2333],from[2333 * 2333],id[2333][2333],wei[2333 * 2333][2];
double dis[2333* 2333];
string lastInf;
bool selected = false;
struct dian
{
int hao;double dis_from,dis_to;
friend bool operator <(const dian& a, const dian& b) { return a.dis_from+ a.dis_to > b.dis_from+b.dis_to; }//A*找最短路
};
priority_queue<dian>q;
struct bian
{
int to, nt; double len;
}e[1000000];
void addEdge(int x, int y, double len)//链式前向星存边 其实可以不存边的,这样做是为了以后方便拓展功能
{
e[++tot].to = y; e[tot].len = len; e[tot].nt = headEdge[x]; headEdge[x] = tot;
}
bool isTarget(int x, int y)//根据像素颜色判断是不是路
{
COLORREF col = getpixel(x, y);
int r = GetRValue(col),g = GetGValue(col),b = GetBValue(col);
if (r > 250 && g > 250 && b > 250)return true;
else return false;
}
void mapToGraph()//将像素转化为图论中点
{
memset(lu, 0, sizeof(lu));
for (int i = 0; i < unWidth; ++i)
for (int j = 0; j < unHeight; ++j)
if (isTarget(i, j))lu[i][j] = 1;//判断是不是路
for (int i = 0; i < unWidth; ++i)
for (int j = 0; j < unHeight; ++j)id[i][j] = ++cnt,wei[cnt][0]=i,wei[cnt][1]=j;//给每个像素编一个号
int tox, toy;
for (int x = 0; x < unWidth; ++x)
for (int y = 0; y < unHeight; ++y)
for(int dx = -1;dx <= 1; ++dx)
for (int dy = -1; dy <= 1; ++dy)
{
tox = x + dx; toy = y + dy;
if (x == tox && y == toy)continue;
if (tox < 0 || unWidth <= tox || toy < 0 || unHeight <= toy || lu[tox][toy] == 0 )continue;
addEdge(id[x][y], id[tox][toy], abs(dx) + abs(dy) == 2 ? sqrt2 : 1.0);//图论加边
}
}
void DIJ(int X1, int Y1, int X2, int Y2)//堆优化DIJ
{
while (!q.empty())q.pop();
memset(from, 0, sizeof(from));
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= cnt; ++i)dis[i] = 114514;//初始化
double tmp_dis; int nowId; dian nowDian;
dis[id[X1][Y1]] = 0;
nowDian.hao = id[X1][Y1]; nowDian.dis_from = 0; nowDian.dis_to = 0; q.push(nowDian);
while (!q.empty())
{
nowId = q.top().hao; q.pop();//取堆顶
if (vis[nowId])continue; vis[nowId] = 1;
if (wei[nowId][0]==X2&& wei[nowId][1]==Y2)break;
//setfillcolor(RGB(255, 0, 0));solidcircle(wei[nowId][0], wei[nowId][1], 1);//画一下寻路过程
//Sleep(1);//取消这句话的注释可以看清楚寻路过程
for (int i = headEdge[nowId]; i; i = e[i].nt)
{
if (dis[e[i].to] > dis[nowId] + e[i].len)//更新最短路
{
dis[e[i].to] = dis[nowId] + e[i].len;
nowDian.hao = e[i].to; nowDian.dis_from = dis[e[i].to]; nowDian.dis_to = sqrt((double)(X2 - wei[e[i].to][0]) * (X2 - wei[e[i].to][0]) + (Y2 - wei[e[i].to][1]) * (Y2 - wei[e[i].to][1]));; q.push(nowDian);
from[e[i].to] = nowId;
}
}
}
nowId = id[X2][Y2];
putimage(0, 0, &Map);
while (nowId != id[X1][Y1])//把路程画出来
{
//Sleep(2);
setfillcolor(RGB(0, 0, 255));
solidcircle(wei[nowId][0], wei[nowId][1], 2);
nowId = from[nowId];
}
}
ExMessage m1, m2, m3;
void watch()
{
if (!selected)
{
cin >> m1.x >> m1.y;
cin >> m2.x >> m2.y;
selected = true;
}
}
int cntJing;
struct jingdian
{
int l, r, u, d;
string jieshao;
}jing[233];
void jingInit()
{
jing[++cntJing].l = 216; jing[cntJing].r = 309; jing[cntJing].u = 451; jing[cntJing].d = 470; jing[cntJing].jieshao = " 青枫公园——是个集“生态、科普、活力”三大主题于一身的城市森林公园,与常州市钟楼区政府隔街相望,位置得天独厚,依托运河、白鹤河、童子河而建,不仅是广大市民休闲健身的适宜场所,也是展示常州城市形象的重要窗口。";
jing[++cntJing].l = 365; jing[cntJing].r = 521; jing[cntJing].u = 498; jing[cntJing].d = 514; jing[cntJing].jieshao = " 龙城天街是龙湖集团在全国布局的主要商业地产品牌,定位为面向中等收入新兴家庭的区域型购物中心,是集购物、餐饮、休闲、娱乐等多业态的一站式商业综合体。随着天街品牌化运营,未来龙湖天街将成为越来越多城市转念即达的欢乐入口。";
jing[++cntJing].l = 605; jing[cntJing].r = 746; jing[cntJing].u = 731; jing[cntJing].d = 747; jing[cntJing].jieshao = " 中天钢铁体育馆位于清潭路、劳动路和广化街的交界处,南倚清潭荆川居民区,北临市中心区域繁华商业街,东西两翼均为商铺云集,人流量密集路段。体育中心下辖常州市体育馆和常州市人民体育场,是常州市中心的标志性建筑。";
jing[++cntJing].l = 830; jing[cntJing].r = 918; jing[cntJing].u = 244; jing[cntJing].d = 261; jing[cntJing].jieshao = " 万达广场包含大型购物中心、五星级酒店、国际电影城、室内外步行街、餐饮娱乐风情街等多种业态的大型城市综合体,打造常州首个集购物、餐饮、文化、娱乐、商务、休闲等多种业态于一体的大型城市商业中心";
jing[++cntJing].l = 919; jing[cntJing].r = 1024; jing[cntJing].u = 17; jing[cntJing].d = 32; jing[cntJing].jieshao = " 江南环球港分成环球广场,太阳大厅,花园中庭和世界港口小镇四个区域,集“文、旅、商”三大中心功能于一体,是长三角“集大成”之超级商业航母。江南环球港是一个文旅商全面发展的新型综合体,涵盖了吃住行游购娱的旅游全产业链。";
jing[++cntJing].l = 1100; jing[cntJing].r = 1205; jing[cntJing].u = 186; jing[cntJing].d = 200; jing[cntJing].jieshao = " 中华恐龙园是常州市环球恐龙城休闲旅游区的一部分,也是一座集科普、游乐、演艺、住宿、购物于一体的主题乐园,有“东方侏罗纪”之称。由七大主题区域组成,分别是魔幻雨林、库克苏克、中华恐龙馆、鲁布拉、梦幻庄园、冒险港、疯狂恐龙人。";
jing[++cntJing].l = 864; jing[cntJing].r = 950; jing[cntJing].u = 687; jing[cntJing].d = 702; jing[cntJing].jieshao = " 红梅公园分三区八景。在公园的南部是文物古迹区,有红梅阁和文笔塔;在公园的西北部是娱乐活动区,有运动场、春晖茶室、青少年活动场所、游艇、听松楼和舞厅;在公园的东部是科普教育区,有动物园、盆景园、月季园和屠一道根艺藏珍馆等。";
jing[++cntJing].l = 897; jing[cntJing].r = 968; jing[cntJing].u = 765; jing[cntJing].d = 780; jing[cntJing].jieshao = " 东坡园,是苏东坡当年弃舟登岸入城之地。舣舟亭位于园内南山顶,亭不大但造型精美,乾隆皇帝下江南时,亲笔题“玉局风流”匾额。公园内绕过曲廊,豁然开朗,林木蔚秀,水石清奇,呈现出我国古典园林风格。大运河绕园东流,使其更富江南景色";
jing[++cntJing].l = 1146; jing[cntJing].r = 1236; jing[cntJing].u = 517; jing[cntJing].d = 532; jing[cntJing].jieshao = " 紫荆公园以东经一百二十度经线这一地理特色为建设主题。这条线线是“北京时间”的基准经线,而常州是该经线唯一穿越城区的地级市。这条线将紫荆公园、中华恐龙园串联在一起,共同构筑了一条集旅游、生态、科普于一体的旅游线路。";
jing[++cntJing].l = 907; jing[cntJing].r = 967; jing[cntJing].u = 259; jing[cntJing].d = 294; jing[cntJing].jieshao = " 河海大学常州校区,位于常州国家级高新技术产业开发区,与常州市行政中心毗邻,是河海大学具有较完整办学功能的重要组成部分,是学校建设高水平特色研究型大学的重要力量";
jing[++cntJing].l = 1031; jing[cntJing].r = 1133; jing[cntJing].u = 259; jing[cntJing].d = 290; jing[cntJing].jieshao = " 常州工学院,位于江苏省常州市,是江苏省主管的一所全日制普通本科院校,入选国家“十三五”产教融合发展工程立项高校和首批启动高校,教育部和江苏省卓越工程师教育培养计划试点高校,江苏省服务外包人才培养试点高校。";
}
void showString(int posX,int posY,string s)
{
int num = MultiByteToWideChar(0, 0, s.c_str(), -1, NULL, 0);
wchar_t* wide = new wchar_t[num];
MultiByteToWideChar(0, 0, s.c_str(), -1, wide, num);
outtextxy(posX, posY, wide);
}
void showMouse()
{
while (1)
{
ExMessage m;
if (peekmessage(&m))
{
setfillcolor(BLACK);
solidrectangle(0, 0, 170, 30);
setfillcolor(RGB(255, 0, 0));
string ch = "Mouse: " + to_string(m.x) + ' ' + to_string(m.y);
if (isTarget(m.x, m.y)) ch += " Yes";
else ch += " No";
showString(0, 0, ch); ch = " ";
for (int i = 1; i <= 9;++i)showString(0, i*15, ch);
for (int i = 1; i <= cntJing; ++i)//在左上角输出景点信息
if (jing[i].l <= m.x && m.x <= jing[i].r && jing[i].u <= m.y && m.y <= jing[i].d)
{
int nowStringPos = 0,hang,j;
for (hang = 1;; ++hang)
{
ch = "";
for (j= nowStringPos; j < jing[i].jieshao.length(); ++j)
{
ch += jing[i].jieshao[j];
if (j - nowStringPos == 25)break;//25字节换一行
}
nowStringPos = j+1;
showString(0, hang*15, ch);
if (nowStringPos >= jing[i].jieshao.length())break;
}
}
}
}
}
int main()
{
ImgUtils::GetPicWidthHeight("./ChangZhou.png", &unWidth, &unHeight);
initgraph(unWidth, unHeight, EW_SHOWCONSOLE);
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT | PS_JOIN_BEVEL, 3);
loadimage(&Map, _T(".\\ChangZhou.png"));
putimage(0, 0, &Map);
Logger::logInfo("预处理地图中...");
mapToGraph(); jingInit();
Logger::logSuccess("加载完毕!可以开始选点了!");
thread mouse(showMouse);
mouse.detach();
while (1)
{
selected = false;
setfillcolor(RGB(255, 0, 0));
thread input(watch);
input.detach();
while (!selected)
{
if (peekmessage(&m1))
{
if (m1.message == WM_LBUTTONDOWN && isTarget(m1.x, m1.y))
{
solidcircle(m1.x, m1.y, 4);
if (m1.ctrl != true) break;
}
}
}
lastInf = "第一个点的坐标为: " + to_string(m1.x);
lastInf += " " + to_string(m1.y);
Logger::logInfo(lastInf);
while (!selected)
{
if (peekmessage(&m2))
{
if (m2.message == WM_LBUTTONDOWN && isTarget(m2.x, m2.y))
{
solidcircle(m2.x, m2.y, 4);
selected = true;
break;
}
}
}
lastInf = "第二个点的坐标为: " + to_string(m2.x);
lastInf += " " + to_string(m2.y);
Logger::logInfo(lastInf);
double dur;
clock_t start, end;
start = clock();
Logger::logInfo("Starting A*...");
DIJ(m1.x, m1.y, m2.x, m2.y);
end = clock();
dur = (double)(end - start);
Logger::logSuccess("Use Time: " + to_string(dur / CLOCKS_PER_SEC));
while (1)
{
if (peekmessage(&m3)&& m3.message == WM_LBUTTONDOWN)break;
}
putimage(0, 0, &Map);
}
return 0;
}