OpenLayers结合JSTS实现空间扩展

概况

JSTS是JavaScript 空间分析库,常见API有buffer、union、intersection、symDifference、getBoundary、getCentroid、convexHull,css样式可以根据自己的需求写。
环境:webpack构建的vue项目,另外,JS资源官网是可以找到的。

一、需要在项目中引入用到的文件

image.png

二、写一个工具类文件Utils.js

包含创建地图,添加图层,数据的转换,JSTS API 的使用

let wktReader = new OpenLayers.Format.WKT();
// 初始化地图
let initMap = (name, url) => {
    OpenLayers.Layer.OFFLINEMAP = OpenLayers.Class(
        OpenLayers.Layer.XYZ,
        {
            name: 'MAP',
            url: 'https://yoururl/${z}/${x}/${y}.png',  // 这是你发布的地图
            sphericalMercator: true,    // 是否应将平铺范围设置为球形墨卡托的默认值,地图才可以显示 
            wrapDateLine: true,         // 允许拖动平移地图
            tileOptions: null,
            isBaseLayer: true,
            zoom: 10,
            initialize: function (a, b, c) {
                OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments)
                this.tileOptions = OpenLayers.Util.extend(
                    { crossOriginKeyword: 'anonymous' },
                    this.options && this.options.tileOptions
                )
            },
            clone: function (a) {
                a == null && (a = new OFFLINEMAP(this.name, this.url, this.getOptions()));
                return a = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [a])
            },
            getOption(){
                // console.log(this.getOptions())
            },
            CLASS_NAME: 'OpenLayers.Layer.OFFLINEMAP'
        }
    )
}
// 创建地图
let CreatMap = (_this, name) => {
    _this.map = new OpenLayers.Map({
        div: 'map',
        projection: new OpenLayers.Projection('EPSG:900913'),       // 指定添加到此地图的图层的默认投影
        displayProjection: new OpenLayers.Projection('EPSG:4326'),  // 要求proj4js支持EPSG:4326的其他投影
        center: [12614654.172192, 2647187.2285552],                 // 中心点
        isBaseLayer: true,
        numZoomLevels: [0, 24],                                     // 地图缩放级别
        resolutions: [                                              // 地图分辨率的列表(每像素地图单位)以降序排列
            0.0439453125, 
            0.703125, 
            0.3515625, 
            0.17578125, 
            0.087890625, 
            0.0439453125
        ],
        zoom: 16,                                                   // 设置地图级别 ,貌似没有效果
    });
    var configIP = 'https://yourUrl/${z}/${x}/${y}.png';
    var mapoptions = {
        name: 'map',
        url: configIP,
        sphericalMercator: true,
        wrapDateLine: true,
        tileOptions: null,
        box: true,  // 允许框选
        numZoomLevels: 24,
        isBaseLayer: true,
        controls: [],
        zoom: 16
    };

    // * 集合图层,从layer中继承
    var layer = new OpenLayers.Layer.OFFLINEMAP(mapoptions);
    layer.getOption()
    _this.map.addLayer(layer);
    
    // 设置图层样式
    var polystyle = new OpenLayers.Style(
        {
            pointRadius: 3.5,
            fillOpacity: 0.7,
            fillColor: 'black',
            strokeWidth: 1,
            strokeOpacity: 1,
            graphicZIndex: 1
        },
        {
            rules:[
                new OpenLayers.Rule({
                    filter: new OpenLayers.Filter.Comparison({
                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
                        property: 'COLOR',
                        value: 1
                    }),
                    symbolizer: {
                        fillOpacity: 0.9,
                        fillColor: '#03A9F4',
                        strokeWidth: 0.5,
                        strokeOpacity: 0.9,
                        strokeColor: 'white'
                    }
                }),
                new OpenLayers.Rule({
                    filter: new OpenLayers.Filter.Comparison({
                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
                        property: 'COLOR',
                        value: 2
                    }),
                    symbolizer: {
                        fillOpacity: 0.9,
                        fillColor: '#82d09a',
                        strokeWidth: 0.5,
                        strokeOpacity: 0.9,
                        strokeColor: 'white'
                    }
                }),
                new OpenLayers.Rule({
                    filter: new OpenLayers.Filter.Comparison({
                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
                        property: 'COLOR',
                        value: 3
                    }),
                    symbolizer: {
                        fillOpacity: 0.9,
                        fillColor: '#ffbc00',
                        strokeWidth: 3,
                        strokeOpacity: 0.9,
                        strokeColor: '#ffbc00'
                    }
                }),
                new OpenLayers.Rule({
                    filter: new OpenLayers.Filter.Comparison({
                        type: OpenLayers.Filter.Comparison.EQUAL_TO,
                        property: 'COLOR',
                        value: 4
                    }),
                    symbolizer: {
                        fillOpacity: 0.9,
                        fillColor: '#a7fff7',
                        strokeWidth: 3,
                        strokeOpacity: 0.9,
                        strokeColor: '#a7fff7'
                    }
                })
            ]
        }
    );

    // 创建一个图层,添加需要计算的polygon
    var wfslayer = new OpenLayers.Layer.Vector(name, {
        styleMap: new OpenLayers.StyleMap({
            'default': polystyle
        })
    });
    this.layer = wfslayer;
    _this.map.addLayer(wfslayer)
};

// 把 wkt 转成 geometry
let toGeometryFromWkt = (wkt) => {
    var geometry = wktReader.read(wkt);
    var attributes = {
        name:'name',
        wkt: wkt
    }
    geometry.attributes = attributes;
    return geometry
};

// 添加一个 feature 到图层
let addFeature = (wkt, layer, i) => {
    var geometry = toGeometryFromWkt(wkt);
    // 扩展属性
    $.extend(geometry.attributes, {
        NAME: 'name',
        WKT: wkt,
        COLOR: i
    });
    if (wkt != null) {
        this.layer.addFeatures(geometry)
    }
}

// 清除
let removeFeature = (_this, name, clearName) => {
    if (clearName.length) {
        for (let i = 0; i < clearName.length; i++) {
            _this[clearName[i]] = ''
        }
    }
    var layer = _this.map.getLayersByName(name);
    if (layer.length > 0) {
        layer[0].removeAllFeatures()
    }
    _this.map.setCenter([12614654.172192, 2647187.2285552], 24);
}

// jsts API
let JstsUtils = (param) => {
    var firstGeom = null;
    var secondGeom = null;
    var jstsFirstGeom = null;
    var jstsSecondGeom = null;
    var jstsResultGeom = null;
    var jstsReader = new jsts.io.WKTReader();
    var jstsParser = new jsts.io.OpenLayersParser();
    if (param.firstwkt) {
        firstGeom = toGeometryFromWkt(param.firstwkt);
        jstsFirstGeom = jstsParser.read(firstGeom.geometry)
    }
    if (param.secondwkt) {
        secondGeom = toGeometryFromWkt(param.secondwkt);
        jstsSecondGeom = jstsParser.read(secondGeom.geometry)
    }
    // 空间分析功能
    switch (param.name) {
        case 'buffer':
            jstsResultGeom = jstsFirstGeom.buffer(parseFloat(param.radiu == null ? 0.0 : param.radiu))
            break;
        case 'union':
            jstsResultGeom = jstsFirstGeom.union(jstsSecondGeom);
            break;
        case 'intersection':
            jstsResultGeom = jstsFirstGeom.intersection(jstsSecondGeom);
            break;
        case 'symDifference':
            jstsResultGeom = jstsFirstGeom.symDifference(jstsSecondGeom);
            break;
        case 'getBoundary':
            jstsResultGeom = jstsFirstGeom.getBoundary();
            break;
        case 'getCentroid':
            jstsResultGeom = jstsFirstGeom.getCentroid();
            break;
        case 'convexHull':
            jstsResultGeom = jstsFirstGeom.convexHull();
            break;
        default:
            throw 'data format error!';
    }
    // 转成openlayer geometry 对象
    return jstsParser.write(jstsResultGeom)
};

export {
    initMap,
    CreatMap,
    toGeometryFromWkt,
    addFeature,
    JstsUtils,
    removeFeature
}

三、写一个公共的组件public-single-space.vue

<template>
    <div class="col-md-12">
        <div class="layout">
            <div class="nav">
                <div class="feature">
                    <div class="coll_btn_box">
                        <button @click="drawGeometry">绘制</button>
                        <button @click="drawRun">运行</button>
                        <button @click="revertMap">清除</button>
                    </div>
                    <div class="legend_btn_box">
                        <div :class="item.cla" v-for="(item,index) in btnData" :key="index">
                            <span class="rect"></span>
                            <span class="txt">{{item.text}}</span>
                        </div>
                    </div>
                </div>
                <textarea v-model="value" class="text_area"></textarea>
            </div>
            <div class="map">
                <div id="map" class="map_box"></div>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        name: "public-single-space",
        props:{
            btnData:{
                type: [Array,Object]
            },
            value:{
                type: String,
                default: ''
            }
        },
        methods:{
            drawGeometry(){
                this.$emit('draw_geometry');
            },
            drawRun(){
                this.$emit('draw_run');
            },
            revertMap(){
                this.$emit('revert_map');
            }
        },
    };
</script>
<style scoped>
    @import "../assets/css/common.css";
</style>

四、使用,单个图形(以convexHull为案例)

  • convexHull(外接矩形)
  • buffer(缓冲区)
  • getCentroid(中心点)
  • getBoundary(边框)
<template>
    <publicSingleSpace
        :btnData="btnData"
        :value="polygon"
        @draw_geometry="drawGeometry"
        @draw_run="drawRun"
        @revert_map="revertMap">
    </publicSingleSpace>
</template>
<script>
    import publicSingleSpace from './public-single-space'
    import {
        initMap,
        CreatMap,
        addFeature,
        toGeometryFromWkt,
        JstsUtils,
        removeFeature
    } from './Utils.js'

    export default {
        nam: 'ConvexHull',
        components: {
            publicSingleSpace
        },
        data() {
            return {
                map: null,
                polygon: "POLYGON((113.31886002411747 23.12592000980574,113.31973442425445 23.125826276039394,113.31975051750989 23.1248050670806,113.31944474568361 23.125628941581002,113.3187741934338 23.12513067078125,113.31862398972841 23.125638808311095,113.31886002411747 23.12592000980574))",
                layerName: 'convexHull_layer',
                wkt: '',
                btnData:[
                    { cla: 'legend_blue', text: '多边形'},
                    { cla: 'legend_red', text: '边界'},
                ],
            }
        },
        mounted() {
            initMap();
            CreatMap(this, this.layerName);
        },
        methods: {
            drawGeometry() {
                var geometry = toGeometryFromWkt(this.polygon);
                var feature = geometry.geometry.transform(
                    new OpenLayers.Projection("EPSG:4326"),
                    new OpenLayers.Projection("EPSG:900913")
                );
                this.wkt = feature.toString();
                addFeature(this.wkt, this.map.layers, 1);
            },
            drawRun() {
                if(this.wkt != ''){
                    var convexHullFeature = new OpenLayers.Feature.Vector("convexHullVector");
                    convexHullFeature.geometry = JstsUtils({
                        name: "convexHull",
                        firstwkt: this.wkt
                    });
                    var feature = convexHullFeature.geometry.toString();
                    addFeature(feature, this.map.layers, 3);
                }
            },
            revertMap() {
                removeFeature(this, this.layerName,['wkt'])
            }
        }
    };
</script>
<style scoped>
    @import "../assets/css/common.css";
</style>
五、单个图形效果
image.png

image.png

image.png

image.png

六、使用,两个图形(以intersection为例)

  • intersection(相交区)
  • symDifference(不同区)
  • union(合并区)
<template>
    <div class="col-md-12">
        <div class="layout">
            <div class="nav twoNav">
                <div class="left">
                    <div class="feature">
                        <div class="coll_btn_box">
                            <button @click="drawGeometry(1)">绘制</button>
                        </div>
                        <div class="legend_btn_box">
                            <div class="legend_blue">
                                <span class="rect"></span>
                                <span class="txt">几何体1</span>
                            </div>
                            <div class="legend_green">
                                <span class="rect"></span>
                                <span class="txt">几何体2</span>
                            </div>
                            <div class="legend_red">
                                <span class="rect"></span>
                                <span class="txt">相交区</span>
                            </div>
                        </div>
                    </div>
                    <textarea v-model="value1" class="text_area"> </textarea>
                </div>
                <div class="right">
                    <div class="feature">
                        <div class="coll_btn_box">
                            <button @click="drawGeometry(2)">绘制</button>
                            <button @click="drawRun">运行</button>
                            <button @click="revertMap">清除</button>
                        </div>
                    </div>
                    <textarea v-model="value2" class="text_area"> </textarea>
                </div>
            </div>

            <div class="map">
                <div id="map" class="map_box"></div>
            </div>
        </div>
    </div>
</template>
<script>
    import {
        initMap,
        CreatMap,
        addFeature,
        toGeometryFromWkt,
        JstsUtils,
        removeFeature
    } from './Utils.js'

    export default {
        nam: "Intersection",
        data() {
            return {
                map: null,
                value1: "POLYGON((113.31886002411747 23.12592000980574,113.31973442425445 23.125826276039394,113.31975051750989 23.1248050670806,113.31944474568361 23.125628941581002,113.3187741934338 23.12513067078125,113.31862398972841 23.125638808311095,113.31886002411747 23.12592000980574))",
                value2: "POLYGON((113.31875810016938 23.125846009469857,113.31926235546736 23.125323072596977,113.31904241432473 23.124652131736205,113.31856498112215 23.124775466705003,113.31880637993568 23.12506160339534,113.31875810016938 23.125846009469857))",
                layerName: 'intersection_layer',
                wkt: '',
                wktS: '',
            };
        },
        mounted() {
            initMap();
            CreatMap(this, this.layerName);
        },
        methods: {
            drawGeometry(index) {
                var val = index == 1 ?  this.value1 : this.value2;
                var geometry = toGeometryFromWkt(val);
                var featrue = geometry.geometry.transform(
                    new OpenLayers.Projection("EPSG:4326"),
                    new OpenLayers.Projection("EPSG:900913")
                );
                var string = featrue.toString();
                if(index == 1){
                    this.wkt = string;
                }else{
                    this.wktS = string;
                }
                addFeature(string, this.map.layers, index);
            },
            drawRun() {
                var convexHullFeature = new OpenLayers.Feature.Vector("convexHullVector");
                convexHullFeature.geometry = JstsUtils({
                    name: "intersection", // symDifference/合并区
                    firstwkt: this.wkt,
                    secondwkt: this.wktS
                });
                var feature = convexHullFeature.geometry.toString();
                addFeature(feature, this.map.layers, 3);
            },
            revertMap(){
                removeFeature(this, this.layerName, ['wkt','wktS'])
            }
        }
    };
</script>
<style scoped>
    @import "../assets/css/common.css";
</style>

七、两个图形效果图

image.png

image.png

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352