openstreetmap是一种完全开放的地理信息系统,数据由个人、公司免费捐赠、维护。在这个博客的前文中,我们大多围绕搭建地图环境展开讨论。实际上,它更具价值的是数据本身。今天,我们来看使用Qt5分析openstreetmap数据库(样本为2019-01导入全球数据),获得城市科技指数这个自定义指标。
openSteetMap详细知识、数据、虚拟机见:
http://www.goldenhawking.org:8088/
https://www.openstreetmap.org
https://planet.openstreetmap.org/
数据库的搭建见本博客其他博文。专栏:
https://blog.csdn.net/goldenhawking/column/info/22354
在各类官方、非官方的统计学报告中,通过各种角度对城市进行排名。 有根据GDP的,根据绿化的,等等。openStreetMap作为在国内并不很流行的GIS,因为主要数据都来自不受共同利益约束的个人、小公司,因而更能体现一个地方的科技发达程度。
各个城市的大小不一样,形状也不同。如果直接用外接多边形(行政区)进行统计,显然,面积越大的城市越占优势。我们用城市中心区域500平方公里圆形范围(12.616千米半径)内的地标个数为统计方法,主要理由:
因此,统计的方法为查找各个地级市最繁华的半径为12公里的圆型区域中,地标有多少个。
PostgreSQL支持地理计算,以下语句会直接搜索出距离中心点最近的5.642千米内的地标ID(100平方公里的圆)。PostGIS允许SQL语句中加入地理空间计算,对集合的子交并补支持的非常好。
select distinct osm_id from planet_osm_line where
ST_Covers(
ST_Transform(
ST_SetSRID(
ST_MakeBox2D(
ST_Point(112.556-0.5, 37.8562-0.5),ST_Point(112.556+0.3, 37.8562+0.3)
),4326),
3857),
way)
and ST_Distance(
ST_Transform(
ST_SetSRID(
ST_MakePoint(112.556, 37.8562),
4326),
3857),
way) < 12616
可以通过CSDN资源
https://download.csdn.net/download/u012266955/10664500
获得CSV格式的城市经纬度。由于该城市经纬度是行政区域的中心,因而需要和OpenStreetMap的位置进行矫正,以便找到最繁华的市中心。
该数据类似:
我们根据城市的名字,加上行政中心经纬度,使用名称在数据库搜索:
select name, St_X(St_Transform(way,4326)),St_Y(St_Transform(way,4326)) ,
ST_Distance(
ST_Transform(
ST_SetSRID(
ST_MakePoint(116.753, 38.2658),
4326),
3857),
way) as dis
from planet_osm_point where
ST_Covers(
ST_Transform(
ST_SetSRID(
ST_MakeBox2D(
ST_Point(116.753-2, 38.2658-2),ST_Point(116.753+2, 38.2658+2))
,4326)
,3857),
way)
and place in ('city','town','suburb')
and (name like '沧州%' or name like '沧州市%')
order by admin_level, dis asc
统计结果如下:
顺序 | 城市 | 省份 | 命名地点个数 |
---|---|---|---|
1 | 广州 | 广东 | 19237 |
2 | 北京 | 北京 | 15733 |
3 | 上海 | 上海 | 14878 |
4 | 珠海 | 广东 | 12470 |
5 | 深圳 | 广东 | 12134 |
6 | 杭州 | 浙江 | 11178 |
7 | 南昌 | 江西 | 9208 |
8 | 济南 | 山东 | 6593 |
9 | 合肥 | 安徽 | 6463 |
10 | 南通 | 江苏 | 6457 |
11 | 成都 | 四川 | 6446 |
12 | 南京 | 江苏 | 6335 |
13 | 苏州 | 江苏 | 5849 |
14 | 武汉 | 湖北 | 5667 |
15 | 青岛 | 山东 | 4547 |
16 | 临沂 | 山东 | 4511 |
17 | 佛山 | 广东 | 4389 |
18 | 西安 | 陕西 | 4197 |
19 | 天津 | 天津 | 4056 |
20 | 长沙 | 湖南 | 3939 |
21 | 兰州 | 甘肃 | 3697 |
22 | 潍坊 | 山东 | 3222 |
23 | 江门 | 广东 | 3162 |
24 | 福州 | 福建 | 3028 |
25 | 长春 | 吉林 | 2707 |
26 | 无锡 | 江苏 | 2683 |
27 | 唐山 | 河北 | 2597 |
28 | 汕头 | 广东 | 2593 |
29 | 清远 | 广东 | 2572 |
30 | 昆明 | 云南 | 2455 |
31 | 常州 | 江苏 | 2439 |
32 | 厦门 | 福建 | 2154 |
33 | 中山 | 广东 | 2071 |
34 | 湘潭 | 湖南 | 2004 |
35 | 鸡西 | 黑龙江 | 1975 |
36 | 洛阳 | 河南 | 1954 |
37 | 大连 | 辽宁 | 1832 |
38 | 张家口 | 河北 | 1785 |
39 | 平顶山 | 河南 | 1725 |
40 | 宁波 | 浙江 | 1706 |
41 | 泰安 | 山东 | 1700 |
42 | 贵阳 | 贵州 | 1642 |
43 | 石家庄 | 河北 | 1597 |
44 | 哈尔滨 | 黑龙江 | 1536 |
45 | 九江 | 江西 | 1500 |
46 | 遵义 | 贵州 | 1405 |
47 | 宁德 | 福建 | 1391 |
48 | 蚌埠 | 安徽 | 1376 |
49 | 镇江 | 江苏 | 1374 |
50 | 桂林 | 广西 | 1374 |
… |
细节主要包括:
#include
#include
#include
#include
#include
#include
#include
#include
QVector<QVector<double> > correctPosition(const double lon, const double lat,
const QString & name1, const QString & name2);
qint64 queryCount(const double lon, const double lat, const QString & filter);
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QTextStream st_out(stdout,QIODevice::WriteOnly);
try {
//打开数据库,准备选择
QSqlDatabase dbq = QSqlDatabase::addDatabase("QPSQL","TEST");
if (!dbq.isValid())
throw dbq.lastError();
dbq.setHostName("127.0.0.1");
dbq.setPort(5433);
dbq.setUserName("archosm");
dbq.setPassword("archosm");
dbq.setDatabaseName("gis");
if (!dbq.open())
throw dbq.lastError();
//读取CSV数据,https://download.csdn.net/download/u012266955/10664500
QFile f_city(":/ctz_city_type.csv");
QFile f_city_out("ctz_city_type.csv");
if (!f_city.open(QIODevice::ReadOnly))
throw f_city.errorString();
if (!f_city_out.open(QIODevice::WriteOnly))
throw f_city_out.errorString();
QTextStream csv_in(&f_city);
QTextStream csv_out(&f_city_out);
csv_out<<"city,provice,citycode,citytype,cityclass,citype,conspeople,"
"cityname,cityym,lng_gd,lat_gd,spare,corr_lon,corr_lat,osm_count_1000km^2,url\n";
//skip first line(titles)
csv_in.readLine();
while (!csv_in.atEnd())
{
const QString line = csv_in.readLine();
const QStringList lst_elems = line.split(",");
if (lst_elems.size()<11)
continue;
const double lon = lst_elems.at(9).toDouble();
const double lat = lst_elems.at(10).toDouble();
if (lon < 40 || lon >140 || lat<5 || lat >65)
throw QString("Bad Input lat lon.");
const QVector<QVector<double> > corrected =
correctPosition(lon,lat,lst_elems.first(),lst_elems.at(7));
qint64 best_hit = 0;
double best_lat = lat, best_lon = lon;
foreach(const QVector<double> & cter, corrected)
{
const qint64 count = queryCount(cter[0],cter[1]," name is not null ");
if (count>best_hit)
{
best_hit = count;
best_lon = cter[0];
best_lat = cter[1];
}
}
foreach (const QString & v, lst_elems) {
csv_out<<v<<",";
}
csv_out<<best_lon<<","<<best_lat<<","<<best_hit<<",";
csv_out<<QString("\"https://www.openstreetmap.org/#map=10/%2/%1\"")
.arg(best_lon).arg(best_lat)<<"\n";
st_out << lst_elems.at(0)<<":"<<best_hit << "\n";
st_out.flush();
}
f_city.close();
f_city_out.close();
}
catch (QString err) {
st_out << err << "\n";
st_out.flush();
}
catch (QSqlError err) {
st_out << err.text() << "\n";
st_out.flush();
}
a.processEvents();
return 0;
}
qint64 queryCount(const double lon, const double lat, const QString & filter)
{
//2000 km^2圆形区域内。
QTextStream st_out(stdout,QIODevice::WriteOnly);
static const QString s(
"select count(osm_id) from ("
"select distinct osm_id from ("
"select distinct osm_id from planet_osm_line where ST_Covers("
"ST_Transform(ST_SetSRID(ST_MakeBox2D(ST_Point(%1-0.5, %2-0.5),ST_Point(%1+0.3, %2+0.3)),4326),3857),way) "
"and ST_Distance(ST_Transform(ST_SetSRID(ST_MakePoint(%1, %2), 4326),3857),way) < 12616 and (%3) "
" union "
"select distinct osm_id from planet_osm_polygon where ST_Covers("
"ST_Transform(ST_SetSRID(ST_MakeBox2D(ST_Point(%1-0.5, %2-0.5),ST_Point(%1+0.3, %2+0.3)),4326),3857),way) "
"and ST_Distance(ST_Transform(ST_SetSRID(ST_MakePoint(%1, %2), 4326),3857),way) < 12616 and (%3) "
" union "
"select distinct osm_id from planet_osm_point where ST_Covers("
"ST_Transform(ST_SetSRID(ST_MakeBox2D(ST_Point(%1-0.5, %2-0.5),ST_Point(%1+0.3, %2+0.3)),4326),3857),way) "
"and ST_Distance(ST_Transform(ST_SetSRID(ST_MakePoint(%1, %2), 4326),3857),way) < 12616 and (%3) "
" union "
"select distinct osm_id from planet_osm_roads where ST_Covers("
"ST_Transform(ST_SetSRID(ST_MakeBox2D(ST_Point(%1-0.5, %2-0.5),ST_Point(%1+0.3, %2+0.3)),4326),3857),way) "
"and ST_Distance(ST_Transform(ST_SetSRID(ST_MakePoint(%1, %2), 4326),3857),way) < 12616 and (%3) "
") all_eles) joined_uniq;"
);
QSqlQuery query(QSqlDatabase::database("TEST"));
QString sql = s.arg(lon).arg(lat).arg(filter);
//st_out<
if (query.exec(sql)==false)
throw query.lastError();
qint64 count = 0;
if (query.next())
count = query.value(0).toLongLong();
return count;
}
QVector<QVector<double> > correctPosition(const double lon, const double lat, const QString & name1, const QString & name2)
{
QVector<QVector<double> > res;
QVector<double> lonlat_raw{lon,lat};
//res<
static const QString s(
"select St_X(St_Transform(way,4326)),St_Y(St_Transform(way,4326)) ,"
"ST_Distance(ST_Transform(ST_SetSRID(ST_MakePoint(%1, %2), 4326),3857),way) as dis,name from planet_osm_point"
" where ST_Covers("
"ST_Transform(ST_SetSRID(ST_MakeBox2D(ST_Point(%1-2, %2-2),ST_Point(%1+2, %2+2)),4326),3857),way) "
" %5 and (name ='%3' or name ='%4') "
" order by admin_level, dis asc "
);
QTextStream st_out(stdout,QIODevice::WriteOnly);
QTextStream st_in(stdin,QIODevice::ReadOnly);
QSqlQuery query(QSqlDatabase::database("TEST"));
QString sql = s.arg(lon).arg(lat).arg(name1).arg(name2)
.arg(" and place in ('city','town','suburb') ");
//st_out<<"\n"<
if (query.exec(sql)==false)
throw query.lastError();
while (query.next())
{
QVector<double> lonlat{0,0};
lonlat[0] = query.value(0).toDouble();
lonlat[1] = query.value(1).toDouble();
res<<lonlat;
}
if (res.size()==0)
{
sql = s.arg(lon).arg(lat).arg(name1).arg(name2)
.arg(" and place is not null ");
if (query.exec(sql)==false)
throw query.lastError();
while (query.next())
{
QVector<double> lonlat{0,0};
lonlat[0] = query.value(0).toDouble();
lonlat[1] = query.value(1).toDouble();
res<<lonlat;
}
}
if (res.size()==0)
{
sql = s.arg(lon).arg(lat).arg(name1).arg(name2)
.arg(" ");
if (query.exec(sql)==false)
throw query.lastError();
while (query.next())
{
QVector<double> lonlat{0,0};
lonlat[0] = query.value(0).toDouble();
lonlat[1] = query.value(1).toDouble();
res<<lonlat;
}
}
if (res.size()==0)
{
res<<lonlat_raw;
st_out<<"\n"<<sql<<" \nNot corrented\n";
}
return res;
}