OpenLayers 上传Shapefile文件

关注我,带你一起学GIS ^

注:当前使用的是 ol [9.2.4] 版本,天地图使用的key请到天地图官网申请,并替换为自己的key

前言

Shapefile是一种矢量数据文件,使用起来简单、方便。作为数据交换文件,在WebGIS开发中,经常需要实现实现上传shp文件。比如用户需要上传选址范围线或者叠加分析范围,都会选择上传shp文件。本文实现用户即可上传一个单独的shp文件,也可以上传包含所有shp文件的压缩包。

在开始之前,请提前下载好所需引用文件:jszip:https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js

shapefile:https://cdn.jsdelivr.net/npm/shapefile@0.6.6/dist/shapefile.min.js

1. 上传Shp文件

使用input元素,设置type属性为file,通过accept属性过滤上传文件格式。

<div class="upload-shp">
    <input type="file" class="select-file" accept=".zip,.shp">
</div>

2. 监听上传文件事件

获取input元素,监听change事件,在事件参数对象中有个属性files,在其中包含上传文件的信息,文件名、上传文件和上传时间戳等。通过截取文件后缀判断文件类型,因为需要识别用户上传的是单独的shp文件还是包含完整shp文件的压缩包。

const inputEle = document.querySelector(".select-file")
// 监听文件上传事件
inputEle.addEventListener('change', evt => {
    console.log("change事件参数:", evt.target.files)
    const file = evt.target.files[0]
    const fileName = file.name
    const isZip = fileName.indexOf('.zip') > -1
    isZip ? uploadZip(file) : uploadShp(file)
})

3. 上传压缩包

请先确保已经引入了jszip文件。通过loadAsync方法加载文件数据,然后使用file方法将文件数据转换为**’arraybuffer’**,最后再读取shp数据并将其添加到地图上。

function uploadZip(data) {
    const zip = new JSZip()
    zip.loadAsync(data).then(file => {
        const fileList = Object.keys(file.files)
        const regExp = new RegExp(/S.shp$/)
        const shpFile = fileList.find(file => regExp.test(file))
        zip.file(shpFile).async('arraybuffer')
            .then(content => {
                openShapefile(content)
            })
    }).catch(err => {
        // 请上传正确格式的文件!
        console.error(err)
    })
}

4. 上传Shp文件

对于单独的shp文件,只需使用FileReader对象,调用readAsArrayBuffer方法将文件数据转换为**’arraybuffer’**,然后再读取shp数据并将其添加到地图上。

// 上传Shp文件
function uploadShp(file) {
    const reader = new FileReader()
    reader.readAsArrayBuffer(file)
    reader.onload = (e) => {
        openShapefile(e.target.result)
    }
}

5. 读取Shp数据

通过引入shapefile文件,然后调用read方法读取数据并将**’arraybuffer’转换为GeoJSON数据格式。不过注意的是,read方法在读取数据时是一个异步操作,所以需要将其改为同步操作,例子中使用的是asyncawait方法。如果用户需要限制上传的文件类型的话,可以通过判断GeoJSON**对象type属性进行限制,如限制只能上传面文件数据。更多GeoJSON数据格式请参考:GeoJSONshujujainjian。如何添加GeoJSON数据请参考:添加GeoJSONshuju1。

// 读取shp文件
async function openShapefile(content) {
    // GeoJSON 对象
    const featureCollection = await shapefile.read(content)
    console.log("featureCollection:", featureCollection);
    const geoType = featureCollection.features[0].geometry.type
    // 限制上传文件类型
    // const isPolygon = geoType === 'Polygon' || geoType === 'MultiPolygon'
    // if (!isPolygon) {
    //     if (geoType === 'Point') {
    //      console.error('当前上传的文件类型为 "Point",请上传面对象!')
    //     } else if (geoType === 'LineString') {
    //         console.error('当前上传的文件类型为 "LineString",请上传面对象!')
    //     }
    //     return
    // }
    const geoJson = new ol.format.GeoJSON()
    const features = geoJson.readFeatures(featureCollection)
    // 添加对象
    addGeoJSON2Map(featureCollection, "", map)
}

6. 完整代码

其中libs文件夹下的包需要更换为自己下载的本地包或者引用在线资源。

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>OpenLayers 上传Shapefile文件</title>
    <meta charset="utf-8" />

    <link rel="stylesheet" href="../../libs/css/ol9.2.4.css">

    <script src="../../js/config.js"></script>
    <script src="../../js/util.js"></script>

    <script src="../../libs/js/ol9.2.4.js"></script>
    <script src="../../libs/js/jszip.min.js"></script>
    <script src="../../libs/js/shapefile.min.js"></script>
    <style>
        * {
            padding: 0;
            margin: 0;
            font-size: 14px;
            font-family: '微软雅黑';
        }

        html,
        body {
            width: 100%;
            height: 100%;
        }

        #map {
            position: absolute;
            top: 50px;
            bottom: 0;
            width: 100%;
        }

        #top-content {
            position: absolute;
            width: 100%;
            height: 50px;
            line-height: 50px;
            background: linear-gradient(135deg, #ff00cc, #ffcc00, #00ffcc, #ff0066);
            color: #fff;
            text-align: center;
            font-size: 32px;
        }

        #top-content span {
            font-size: 32px;
        }

        .upload-shp {
            position: absolute;
            right: 50px;
            top: 80px;
            padding: 10px 20px;
            color: #fff;
            border-radius: 5px;
            border: 1px solid #50505040;
            background: linear-gradient(135deg, #c850c0, #4158d0);
        }

        .upload-shp:hover {
            cursor: pointer;
            filter: brightness(120%);
            background: linear-gradient(135deg, #eb3ddf, #0931fb);
        }
        .select-file {
            width: 180px;
        }
    </style>
</head>

<body>
    <div id="top-content">
        <span>OpenLayers 上传Shapefile文件</span>
    </div>
    <div id="map" title="地图显示"></div>
    <div class="upload-shp">
        <input type="file" class="select-file" accept=".zip,.shp">
    </div>
</body>

</html>

<script>
    //地图投影坐标系
    const projection = ol.proj.get('EPSG:3857');
    //==============================================================================//
    //============================天地图服务参数简单介绍==============================//
    //================================vec:矢量图层==================================//
    //================================img:影像图层==================================//
    //================================cva:注记图层==================================//
    //======================其中:_c表示经纬度投影,_w表示球面墨卡托投影================//
    //==============================================================================//
    const TDTImgLayer = new ol.layer.Tile({
        title: "天地图影像图层",
        source: new ol.source.XYZ({
            url: "http://t0.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=" + TDTTOKEN,
            attibutions: "天地图影像描述",
            crossOrigin: "anoymous",
            wrapX: false
        })
    })
    const TDTImgCvaLayer = new ol.layer.Tile({
        title: "天地图影像注记图层",
        source: new ol.source.XYZ({
            url: "http://t0.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=" + TDTTOKEN,
            attibutions: "天地图注记描述",
            crossOrigin: "anoymous",
            wrapX: false
        })
    })
    const map = new ol.Map({
        target: "map",
        loadTilesWhileInteracting: true,
        view: new ol.View({
            center: [102.845864, 25.421639],
            zoom: 6.5,
            worldsWrap: false,
            minZoom: 1,
            maxZoom: 20,
            projection: 'EPSG:4326',
        }),
        layers: [TDTImgLayer],
        // 地图默认控件
        controls: ol.control.defaults.defaults({
            zoom: false,
            attribution: false,
            rotate: false
        })
    })
    map.on('click', evt => {
        console.log("获取地图坐标:", evt.coordinate)
    })

    const inputEle = document.querySelector(".select-file")
    // 监听文件上传事件
    inputEle.addEventListener('change', evt => {
        // console.log("change事件参数:", evt.target.files)
        const file = evt.target.files[0]
        const fileName = file.name
        const isZip = fileName.indexOf('.zip') > -1
        isZip ? uploadZip(file) : uploadShp(file)
    })

    // 监听上传完成事件
    inputEle.addEventListener('load', evt => {
        console.log("load事件参数:", evt)
    })
    // 监听上传错误事件
    inputEle.addEventListener('error', evt => {
        console.log("error事件参数:", evt)
    })

    // 上传Shp压缩包
    function uploadZip(data) {
        const zip = new JSZip()
        zip.loadAsync(data).then(file => {
            const fileList = Object.keys(file.files)
            const regExp = new RegExp(/\S\.shp$/)
            const shpFile = fileList.find(file => regExp.test(file))
            zip.file(shpFile).async('arraybuffer')
                .then(content => {
                    openShapefile(content)
                })
        }).catch(err => {
            // '请上传正确格式的文件!'
            console.error(err)       
        })
    }

    // 上传Shp文件
    function uploadShp(file) {
        const reader = new FileReader()
        reader.readAsArrayBuffer(file)
        reader.onload = (e) => {
            openShapefile(e.target.result)
        }
    }
    // 读取shp文件
    async function openShapefile(content) {
        const featureCollection = await shapefile.read(content)
        // console.log("featureCollection:", featureCollection);
        const geoType = featureCollection.features[0].geometry.type
        // const isPolygon = geoType === 'Polygon' || geoType === 'MultiPolygon'
        // if (!isPolygon) {
        //     if (geoType === 'Point') {
        // this.$message.error('当前上传的文件类型为 "Point",请上传面对象!')
        // console.error('当前上传的文件类型为 "Point",请上传面对象!')
        //     } else if (geoType === 'LineString') {
        //         this.$message.error('当前上传的文件类型为 "LineString",请上传面对象!')
        //         console.error('当前上传的文件类型为 "LineString",请上传面对象!')
        //     }
        //     return
        // }
        const geoJson = new ol.format.GeoJSON()
        const features = geoJson.readFeatures(featureCollection)
        // 添加对象
        addGeoJSON2Map(featureCollection, "", map)
    }
</script>

OpenLayers示例数据下载,请在公众号后台回复:ol数据

全国信息化工程师-GIS 应用水平考试资料,请在公众号后台回复:GIS考试

GIS之路公众号已经接入了智能助手,欢迎大家前来提问。

欢迎访问我的博客网站-长谈GIShttp://shanhaitalk.com

都看到这了,不要忘记点赞、收藏+关注 

本号不定时更新有关 GIS开发  相关内容,欢迎关注 

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部