基于PostGIS使用GeoServer发布数据量大的GPS轨迹路线图
1. 引言
人类在行走或者驾驶过程中产生的GPS轨迹,是道路的一种采样,根据GPS轨迹路线,我们可以推知道路的存在,根据轨迹的密度,可以推知道路的热度以及重要性。如何才能在地图中显示大量的轨迹,这是一个值得思考的问题。诚然,可以直接加载原始轨迹数据,但是这会造成极大的网络压力。地图切片技术可以有效地解决这一问题,GIS服务器在一定比例下对地图进行切片,然后返回给客户端需要的部分,这对于地图底图(比如遥感影像)有着不错的效果
更详细的信息可参考:地图切片的原理、基于地图切片WebGIS原理及其优缺点、发布地图服务时缓存切片设置_CassyChu的博客-CSDN博客_地图切片的原理
这里,笔者使用装载PostGIS插件的PostgreSQL存储GPS轨迹矢量数据,然后使用GeoServer发布WMS地图服务,最后使用OpenLayers可视化结果
2. 环境准备
GeoServer的Windows版本安装极为简单,直接去官网GeoServer下载下来,运行start.bat脚本即可,管理员初始账户为admin,密码为geoserver
PostgreSQL+PostGIS安装可参考:PostgreSQL+PostGIS安装教程_Oruizn的博客-CSDN博客_postgresql安装postgis
- 作为实验,也可使用docker直接pull一个安装好PostgreSQL+PostGIS的镜像
- 数据库安装好后可使用数据库管理软件(如dbeaver、navicat)测试连接,并创建一个新的数据库(笔者这里创建
travel-map
)
GPS数据的获取可参考:GPS地图生成03之数据获取 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)
笔者这里使用Python对数据库进行操作,Python版本为3.6(3以上即可),使用到的库有:
- ppygis3
- psycopg2
可使用pip安装:
pip install psycopg2 ppygis3
3. 数据上传至数据库
这里笔者使用的从网站获取的原始JSON数据
获取文件夹下的JSON数据文件:
import os
file = os.listdir('./trackdata/origin/')
trip_files = []
for trip_file in file:
if trip_file.startswith('trackjson'):
trip_files.append(trip_file)
# print(trip_files)
连接数据库,创建游标:
import psycopg2
from ppygis3 import Point, LineString, Geometry
# Connect to an existing spatially enabled database
connection = psycopg2.connect(database="travel-map", user="admin", password="root", host="127.0.0.1", port="5432")
cursor = connection.cursor()
- 注意:请修改为你的数据库配置
创建表:
cursor.execute('CREATE TABLE IF NOT EXISTS gps_track(tid INT PRIMARY KEY, geometry GEOMETRY)')
数据上载至数据库:
import json
dir = './trackdata/origin/'
for file in trip_files:
trackjson = json.loads(open(dir+file, 'r').read())
tmp = []
for track in trackjson:
tmp.append(Point(track[2],track[1],track[3]))
geometry = LineString(tmp)
cursor.execute('INSERT INTO gps_track VALUES(%s,%s)', (int(file[9:][:-5]),geometry))
connection.commit()
更新空间坐标系:
cursor.execute('update gps_track set geometry = ST_SetSRID(geometry,4326)')
查询是否上传成功:
# Retrieve the table contents and print it
cursor.execute('SELECT * FROM gps_track')
for row in cursor:
print(row[0])
关闭连接:
# Disconnect from the database
cursor.close()
connection.close()
完整代码如下:
import os
file = os.listdir('./trackdata/origin/')
trip_files = []
for trip_file in file:
if trip_file.startswith('trackjson'):
trip_files.append(trip_file)
# print(trip_files)
trip_files.__len__()
import psycopg2
from ppygis3 import Point, LineString, Geometry
# Connect to an existing spatially enabled database
connection = psycopg2.connect(database="travel-map", user="admin", password="root", host="127.0.0.1", port="5432")
cursor = connection.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS gps_track(tid INT PRIMARY KEY, geometry GEOMETRY)')
import json
dir = './trackdata/origin/'
for file in trip_files:
trackjson = json.loads(open(dir+file, 'r').read())
tmp = []
for track in trackjson:
tmp.append(Point(track[2],track[1],track[3]))
geometry = LineString(tmp)
cursor.execute('INSERT INTO gps_track VALUES(%s,%s)', (int(file[9:][:-5]),geometry))
connection.commit()
# update gps_track set geometry = ST_SetSRID(geometry,3857)
# DROP TABLE IF EXISTS gps_track
cursor.execute('update gps_track set geometry = ST_SetSRID(geometry,4326)')
# Retrieve the table contents and print it
cursor.execute('SELECT * FROM gps_track')
for row in cursor:
print(row[0])
# Disconnect from the database
cursor.close()
connection.close()
4. GeoServer连接数据库并发布服务
GeoServer的地图发布参考:
- GeoServer速成:安装启动,发布地图,加载QGIS - 知乎 (zhihu.com)
- GeoServer在Linux上源码安装、启动、发布地图服务 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)
这里笔者直接添加数据源:
设置好参数,尤其是连接参数:
顺利的话点击保存进入发布界面:
设置发布地图的参数,主要是坐标系与范围:
点击保存,可点击图层预览预览刚才上传的数据:
以下是设置地图样式的步骤
这里笔者使用QGIS设置样式并导出SLD样式:
点击样式,添加样式:
设置参数,上传SLD文件,并点击upload
:
- upload后文本框内会出现相应的文本代码
点击验证与保存
点击图层选择你的图层并点击发布设置样式:
设置你创建的样式为默认样式并保存:
再次预览,可以发现样式已经更改:
5. 使用OpenLayers调用地图服务
其实刚才的预览就已经是使用OpenLayers调用地图服务
这里笔者主要是使用OpenLayer的图层控制插件ol-layerswitcher: walkermatt/ol-layerswitcher: Layer control for OpenLayers (github.com) 来加载与控制影像地图、轨迹地图与矢量路网等
加载OpenLayer与ol-layerswitcher的CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.js"></script>
<script src="https://unpkg.com/ol-layerswitcher@3.8.3"></script>
<link rel="stylesheet" href="https://unpkg.com/ol-layerswitcher@3.8.3/dist/ol-layerswitcher.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.css">
加载GPS轨迹地图:
new ol.layer.Tile({
title: 'GPS轨迹',
source: new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/yuelushan/wms',
params: { 'LAYERS': 'yuelushan:gps_track' }
})
}),
添加图层控制控件:
const layerSwitcher = new LayerSwitcher({
reverse: true,
groupSelectStyle: 'group'
});
olMap.addControl(layerSwitcher);
- 图层名为加载图层时的title
完整代码如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.js"></script>
<script src="https://unpkg.com/ol-layerswitcher@3.8.3"></script>
<link rel="stylesheet" href="https://unpkg.com/ol-layerswitcher@3.8.3/dist/ol-layerswitcher.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.css">
<style>
#olmap {
width:
100%;
height: 99%;
position: absolute;
}
#menu {
position: absolute;
top: 30px;
left: 20px;
z-index: 11;
}
</style>
</head>
<body>
<body>
<div id="olmap"></div>
<script type="text/javascript">
var projection = ol.proj.get("EPSG:4326"); var projectionExtent = projection.getExtent();
var size = ol.extent.getWidth(projectionExtent) / 256; var resolutions = [];
for (var z = 2; z < 19; ++z) {
resolutions[z] = size / Math.pow(2, z);
}
var vectorSource = new ol.source.Vector({
url: "./final_map.json",
format: new ol.format.GeoJSON()
})
var olMap = new ol.Map({
target: "olmap",
layers: [
new ol.layer.Tile({
title: "OSM地图",
source: new ol.source.XYZ({
url: 'http://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'
})
}),
new ol.layer.Tile({
title: "中国矢量1-4级",
source: new ol.source.WMTS({
url: "http://t{0-6}.tianditu.gov.cn/vec_c/wmts?tk=1d109683f4d84198e37a38c442d68311",
name: "中国矢量1-4级",
layer: "vec",
style: "default",
matrixSet: "c",
format: "tiles",
wrapX: true,
tileGrid: new ol.tilegrid.WMTS({
origin: ol.extent.getTopLeft(projectionExtent),
resolutions: resolutions,
matrixIds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
})
}),
}),
new ol.layer.Tile({
title: '遥感影像',
source: new ol.source.BingMaps({
key: "ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp",
imagerySet: 'Aerial',
}),
}),
new ol.layer.Tile({
title: 'GPS轨迹',
source: new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/yuelushan/wms',
params: { 'LAYERS': 'yuelushan:gps_track' }
})
}),
new ol.layer.Vector({
title: "矢量路网",
source: vectorSource,
style: new ol.style.Style({
// stroke: new ol.style.Stroke({
// color: 'rgba(0,255,0,0.8)',
// width: 4,
// }),
stroke: new ol.style.Stroke({
color: '#0000ff',//颜色
width: 3,//宽度
lineCap: 'round',//线帽样式
//butt:末端添加平直边缘;round:末端添加圆形线帽;square:末端添加方形线帽;
lineJoin: 'round'//线条连接处样式
//bevel:创建斜角;round:创建圆角;square:创建尖角;
})
}),
}),
new ol.layer.Tile({
title: "中国矢量注记1-4级",
source: new ol.source.WMTS({
name: "中国矢量注记1-4级",
url: "http://t{0-6}.tianditu.gov.cn/cva_c/wmts?tk=1d109683f4d84198e37a38c442d68311",
layer: "cva",
style: "default",
matrixSet: "c",
format: "tiles",
wrapX: true,
tileGrid: new ol.tilegrid.WMTS({
origin: ol.extent.getTopLeft(projectionExtent),
resolutions: resolutions,
matrixIds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
})
}),
}),
],
view: new ol.View({
// 将长沙作为地图中心
center: [112.92597770690918, 28.186654954789518],
projection: 'EPSG:4326',
zoom: 14,
}),
controls: [],
});
const layerSwitcher = new LayerSwitcher({
reverse: true,
groupSelectStyle: 'group'
});
olMap.addControl(layerSwitcher);
</script>
</body>
</html>
使用VS Code的Live Server插件加载网页:
基于PostGIS使用GeoServer发布数据量大的GPS轨迹路线图的更多相关文章
- 分布式系统中我们会对一些数据量大的业务进行分拆,分布式系统中唯一主键ID的生成问题
分布式全局唯一ID生成策略 https://www.cnblogs.com/vandusty/p/11462585.html 一.背景 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订 ...
- 关于dedecms数据量大以后生成目录缓慢的问题解决
四月份的时候博客被封.我不知情.因为一直很忙,没有来得及看.前两天来看以后,发现居然被封,吓傻了我. 赶紧找原因,原来是转载了某个人的博文,被他举报了,然后就被封了. 觉得很伤心,毕竟这个博客陪伴了我 ...
- 使用POI导出EXCEL工具类并解决导出数据量大的问题
POI导出工具类 工作中常常会遇到一些图表需要导出的功能,在这里自己写了一个工具类方便以后使用(使用POI实现). 项目依赖 <dependency> <groupId>org ...
- DataTable 数据量大时,导致内存溢出的解决方案
/// <summary> /// 分解数据表 /// </summary> /// <param name="originalTab">需要分 ...
- Thinkphp解决phpExcel导出数据量大导致内存溢出
工作需要导出几万的数据量.操作比较频繁.之前数据在七八千是数据导出很慢.phpExcel是方便但是性能一般.现在改为使用csv导出数据:可以缓解内存压力,一次导出两三万是没问题的.当然服务器内存给力, ...
- sklearn 增量学习 数据量大
问题 实际处理和解决机器学习问题过程中,我们会遇到一些"大数据"问题,比如有上百万条数据,上千上万维特征,此时数据存储已经达到10G这种级别.这种情况下,如果还是直接使用传统的方式 ...
- ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)
要在ASP.NET MVC站点上做excel导出功能,但是要导出的excel文件比较大,有几十M,所以导出比较费时,为了不影响对界面的其它操作,我就采用异步的方式,后台开辟一个线程将excel导出到指 ...
- MVC学习笔记---MVC导出excel(数据量大,非常耗时的,异步导出)
要在ASP.NET MVC站点上做excel导出功能,但是要导出的excel文件比较大,有几十M,所以导出比较费时,为了不影响对界面的其它操作,我就采用异步的方式,后台开辟一个线程将excel导出到指 ...
- PHP 导出excel 数据量大时
public function ceshiexcel1(){ set_time_limit(0); $filename = '病毒日志'; header('Content-Type: applicat ...
- extjs4 前台导出grid数据 生成excel,数据量大后台无法接收到数据
最近做的一个web项目使用的是extsj4 框架,需要一个导出excel功能,通过extjs4 自带的导出方法实现.在前台生成excel的代码,form提交传递到后台输出.前台grid数据超过1000 ...
随机推荐
- 一定要用Photoshop?no!动手用Python做一个颜色提取器! ⛵
作者:韩信子@ShowMeAI Python3◉技能提升系列:https://www.showmeai.tech/tutorials/56 计算机视觉实战系列:https://www.showmeai ...
- SpringBoot中搭配AOP实现自定义注解
1 springBoot的依赖 确定项目中包含可以注解的依赖 <dependency> <groupId>org.springframework.boot</groupI ...
- ORM数据增删改查 django请求生命周期 django路由层 反向解析
目录 可视化界面之数据增删改查 补充 1.建表 2.数据展示功能 3.数据添加功能 4.数据编辑功能 5.数据删除功能 django请求生命周期流程图 crsf wsgirel 与 uwsgi ngi ...
- APICloud 入门教程窗口篇
什么是窗口,窗口可以理解为一屏幕内容的一个基本载体,里面可以放导航,图片,视频,文字等组成一屏幕内容. 不同的窗口组成一个APP, 例如购物APP有[首页],[购物车],[我的]等不同的窗口.不同的窗 ...
- Redis 中ZSET数据类型命令使用及对应场景总结
转载请注明出处: 目录 1.zadd添加元素 2.zrem 从有序集合key中删除元素 3.zscore 返回有序集合key中元素member的分值 4.zincrby 为有序集合key中元素增加分值 ...
- 巧用视觉障眼法,还原 3D 文字特效
最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果: 这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗? 不是特别好实现,但是,如果仅仅只 ...
- JavaScript 中如何拦截全局 Fetch API 的请求和响应?
本文翻译自 Intercepting JavaScript Fetch API requests and responses 拦截器是可用于预处理或后处理 HTTP 请求的代码块,有助于全局错误处理. ...
- 3xx HTTP状态码的终极指南
前言 如果你在管理一些网站,那么对HTTP重定向的理解对于可靠的网站性能至关重要.在这篇文章中,我们将全面了解一下3xx HTTP状态码,从这里你可以了解它们是如何工作的,如何更好地管理它们,以及它们 ...
- JUC源码学习笔记5——1.5w字和你一起刨析线程池ThreadPoolExecutor源码,全网最细doge
源码基于JDK8 文章1.5w字,非常硬核 系列文章目录和关于我 一丶从多鱼外卖开始 话说,王多鱼给好友胖子钱让其投资,希望亏得血本无归.胖子开了一个外卖店卖国宴,主打高端,外卖小哥都是自己雇佣,并且 ...
- Apache RocketMQ 5.0 笔记
RocketMQ 5.0:云原生"消息.事件.流"实时数据处理平台,覆盖云边端一体化数据处理场景. 核心特性 云原生:生与云,长与云,无限弹性扩缩,K8s友好 高吞吐:万亿级吞吐保 ...