上篇我们做了测距工具,两个可拖拽的点,中间自动连线,实时显示距离。但有个问题一直存在:刷新页面,所有标记全没了。这不是Bug,是因为数据存在内存里,浏览器一刷新就清空。今天来解决这个问题,让数据真正持久化保存。
一、什么是数据持久化
简单来说,数据持久化就是让数据在页面刷新、浏览器关闭后依然存在。之前我们做的都是临时数据,存在内存中,页面一刷新就没了。为了很好地存储地理数据,我们使用GeoJSON格式,配合浏览器的localStorage来实现本地持久化存储。
对于小型应用和学习项目,GeoJSON加localStorage完全够用,简单方便,不需要后端服务器。如果是大型应用,更推荐用空间数据库比如PostGIS,功能更强大,处理速度和效率更高。
二、认识GeoJSON
GeoJSON是一种保存地理信息数据的格式,用JSON来描述地图上的点、线、面等几何图形。官网是→https://geojson.org/
一个标准的GeoJSON对象包含两个核心部分:geometry和properties。geometry定义几何信息,比如类型是点还是线,坐标是多少。properties是自定义属性,可以用键值对定义任意信息,比如名称、类型、描述等。
geometry里type可以是Point点、LineString线、Polygon面等。coordinates是坐标,构造几何的经纬度坐标。注意GeoJSON的坐标顺序是经度在前,纬度在后,不要写反。
三、实战:GeoJSON数据持久化
3.1 引入资源创建地图
先搭建基础页面,引入高德地图资源,创建地图容器:
html
<!DOCTYPE html>
<html lang="en">
<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>智慧校园</title>
<!-- 引入资源 -->
<!-- css样式 -->
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
<link rel="stylesheet" href="./css/index.css">
<!-- 引入js资源 -->
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode:'111111',
}
</script>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=111111"></script>
</head>
<body>
<!-- 创建地图容器 -->
<div id="container"></div>
<script>
// 创建地图对象
var map = new AMap.Map('container',{
center:[114.402672, 30.518987],
zoom:16,
viewMode:'3D',
pitch:45,
})
</script>
</body>
</html>
这里把地图中心定位到武汉某高校附近,开启3D视图,俯仰角45度,让校园地图更有立体感。
3.2 使用控件
添加工具条、比例尺和控件栏,这些都是前面操作过的内容,复习一下:
javascript
// 使用控件
AMap.plugin(['AMap.ToolBar','AMap.Scale','AMap.ControlBar'],function(){
map.addControl(new AMap.ToolBar())
map.addControl(new AMap.Scale())
map.addControl(new AMap.ControlBar())
})
3.3 标记功能
这里使用高德的CSS样式来创建一个div控件,之后再添加地图点击监听事件:
javascript
// 监听地图点击事件
map.on('click',function(e){
// 创建标记
var marker = new AMap.Marker({
position:e.lnglat,
})
// 添加标记图层
map.add(marker)
})
效果就是右上角有了交互控件,可以用鼠标左键单击添加标记。不过这时候交互功能还没有实现,只是先放上控件,标记刷新页面就会消失。
3.4 数据持久化
现在进入核心部分,使用GeoJSON在本地存储中记录数据。
先实现读入数据和写数据的功能:
javascript
// 从local storage中读取数据
function getData(){
// 判断本地local storage中不存在数据
if(!localStorage.getItem('geojson')){
localStorage.setItem('geojson','[]')
}
return JSON.parse( localStorage.getItem('geojson') )
}
// 从local storage中写数据
function saveData(data){
localStorage.setItem('geojson',JSON.stringify(data))
}
这里有两个关键点。
第一个是在getData函数里,一定要判断本地local storage中是否存在geojson这个数据。如果不存在,就初始化一个空数组。如果不做这个判断,第一次访问时localStorage里没有geojson,getItem返回null,后面JSON.parse虽然不会直接报错,但后续逻辑会出问题。我查了很久才发现是这里写错了,报错Cannot read properties of null。
第二个是saveData函数,调用时一定要记得传参数。我就曾经遇到过存不进数据的问题,检查半天发现是saveData调用时自变量忘了写。
接下来定义全局变量,创建GeoJSON对象,导入已有数据:
javascript
// 定义全局变量
var geojson = new AMap.GeoJSON({
geoJSON: null,
})
// 导入数据
if(JSON.stringify(getData()) != '[]'){ // 有数据的时候才导入
geojson.importData(getData())
}
map.add(geojson)
在没有数据时,geojson为null。之后点击添加数据,保存,就会实现数据的持久化。
最后把点击事件改造一下,通过geojson对象来管理覆盖物:
javascript
// 监听地图点击事件
map.on('click',function(e){
// 创建标记
var marker = new AMap.Marker({
position:e.lnglat,
})
// 通过geojson对象管理覆盖物
geojson.addOverlay(marker)
// console.log(geojson)
saveData(geojson.toGeoJSON())
// 添加标记图层
// map.add(marker)
})
注意这里的变化:不再直接用map.add(marker),而是通过geojson.addOverlay(marker)把标记纳入GeoJSON管理。然后调用geojson.toGeoJSON()导出完整的GeoJSON数据,再用saveData保存到localStorage。
效果图:

3.5 踩坑记录
做这一篇的时候踩了三个坑,记录一下。
第一个坑是报错说不能用geojson。排查发现是引入JS API时没有在URL里添加GeoJSON插件。需要在script标签的src里加上plugin参数:
javascript
<script type="text/javascript"
src="https://webapi.amap.com/maps?
v=2.0&key=111111&plugin=AMap.GeoJSON"></script>
第二个坑是报错Cannot read properties of null。查了很久,发现是getData函数写错了。当localStorage里没有geojson这个key时,getItem返回null,后面虽然JSON.parse(null)不会报错,但importData时会出问题。解决方法是先判断是否存在,不存在就初始化一个空数组。
第三个坑是数据存不进去。排查发现是saveData调用时忘了写参数,应该是saveData(geojson.toGeoJSON()),结果写成了saveData()。
四、完整代码
把上面所有部分整合起来:
javascript
<!DOCTYPE html>
<html lang="en">
<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>智慧校园</title>
<!-- 引入资源 -->
<!-- css样式 -->
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
<link rel="stylesheet" href="./css/index.css">
<!-- 引入js资源 -->
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode:'111111',
}
</script>
<!-- 注意:这里要加上plugin=AMap.GeoJSON -->
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=111111&plugin=AMap.GeoJSON"></script>
</head>
<body>
<!-- 创建地图容器 -->
<div id="container"></div>
<script>
// 创建地图对象
var map = new AMap.Map('container',{
center:[114.402672, 30.518987],
zoom:16,
viewMode:'3D',
pitch:45,
})
// 使用控件
AMap.plugin(['AMap.ToolBar','AMap.Scale','AMap.ControlBar'],function(){
map.addControl(new AMap.ToolBar())
map.addControl(new AMap.Scale())
map.addControl(new AMap.ControlBar())
})
// 从local storage中读取数据
function getData(){
// 判断本地local storage中不存在数据
if(!localStorage.getItem('geojson')){
localStorage.setItem('geojson','[]')
}
return JSON.parse( localStorage.getItem('geojson') )
}
// 从local storage中写数据
function saveData(data){
localStorage.setItem('geojson',JSON.stringify(data))
}
// 定义全局变量
var geojson = new AMap.GeoJSON({
geoJSON: null,
})
// 导入数据
if(JSON.stringify(getData()) != '[]'){ // 有数据的时候才导入
geojson.importData(getData())
}
map.add(geojson)
// 监听地图点击事件
map.on('click',function(e){
// 创建标记
var marker = new AMap.Marker({
position:e.lnglat,
})
// 通过geojson对象管理覆盖物
geojson.addOverlay(marker)
// console.log(geojson)
saveData(geojson.toGeoJSON())
// 添加标记图层
// map.add(marker)
})
</script>
</body>
</html>
打开页面,地图正常显示,右上角有工具条。点击地图任意位置,出现红色标记。刷新页面,标记还在。关闭浏览器重新打开,标记还在。数据真正实现了持久化存储。
在浏览器控制台里打开Application面板,找到Local Storage,可以看到存储的geojson数据,格式就是标准的GeoJSON FeatureCollection。