零基础WebGIS开发入门 | GeoJSON数据持久化

上篇我们做了测距工具,两个可拖拽的点,中间自动连线,实时显示距离。但有个问题一直存在:刷新页面,所有标记全没了。这不是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。

相关推荐
sunywz2 小时前
【c#】 Web Deploy一键发布,IIS部署全流程
开发语言·前端·c#
宁&沉沦2 小时前
Chrome 扩展 Manifest 字段版本支持一览(全量)
前端·后端·编辑器
乖孩子2 小时前
Service Worker + IndexedDB 实践
前端
浮生望2 小时前
BFF架构实战:用Express+SSE为Vue3搭建安全的流式对话中间层
前端
Quz2 小时前
QML 与 JavaScript 交互方式:内联函数、外部文件、信号槽与工作线程
javascript·qt
先吃饱再说3 小时前
React 组件通信:从 Props 单向传递到自定义事件
前端·react.js·前端框架
swipe3 小时前
05|(前端转后全栈)不手写一堆 SQL,后端怎么操作数据库?MyBatis-Plus 入门
前端·后端·全栈
Allshadow3 小时前
Nest.js - 连接数据库
javascript·nestjs
冰暮流星3 小时前
javascript之date对象
开发语言·javascript·ecmascript