Introduction to open source front-end GIS spatial analysis library (1) jsts and turf

1 Introduction

Usually, when referring to the spatial analysis of GIS, we will contact these GIS software such as ArcGIS and QGIS. These tool software are very powerful in terms of spatial processing capabilities. They are very powerful weapons for us to process spatial data, and they are also key skills that a GISer must master when getting started. However, these software, as products of the desktop software era, their spatial processing functions, as the basic functions of the software, cannot run independently from the software, and it is difficult to meet some of our customization needs. Although the secondary development based on these software can meet our customization requirements to a certain extent, the output is usually desktop software, which does not meet the basic demands of today's Internet era.

In today's Internet era, under the framework of front-end and back-end technologies, data is usually dynamic. When we encounter some spatial analysis requirements, we may usually think of putting the spatial analysis requirements in the background for processing. The advantages of this are: 1. It does not occupy client resources, all calculations are done by the server, and the server performance is fully delivered; 2. The technical route is relatively mature, and there are many toolkits to choose from. Compared with Geotools, geoserver, PostGIS, Geopandas, etc., there are more reference materials.

However, sometimes the requirements for spatial analysis may only be some relatively simple requirements, and it seems a bit overkill to introduce relatively complex toolkits for these requirements.

With the advancement of development technology, the improvement of personal computer performance, and the evolution of browsers, when a front-end developer gets the problem of GIS spatial analysis again, he starts to think, is it possible to deal with this problem on the browser side?

The answer is yes.
Haha, so much nonsense, in fact, it is to draw out the two protagonists of this article, JSTS and turf.

2 JSTS

jsts is a javascript package that can be used to process and analyze simple spatial geometry. It is converted from the source code of the Java package JTS, and retains the API of the original JTS.
In addition, its io module supports interconversion with WKT, GeoJSON and openlayers3+ evolution data. This function will be very friendly.

3 turf

Turf is a modular spatial analysis engine written in javascript produced by mapbox, which uses the geojson data format for spatial processing. It contains traditional spatial operations, helper functions for creating GeoJSON data, and tools for data classification and statistics.

Both packages can be run in the browser or in node.

4 Installation and use

4.1 jsts

4.1.1 Direct Import

<script src="https://unpkg.com/jsts/dist/jsts.min.js"></script>

4.1.2 NPM

1) install

npm install jsts

2) Introduce -node environment

const jsts = require('jsts')

3) Introduce -esModule
Here, you should pay attention to it. Direct import jsts from 'jsts' cannot correctly import the jsts package. Here you need to import specific modules as needed, and the root path is 'jsts/org/locationtech/jts'. For example, import the overlayOp module of jsts

import OverlayOp from 'jsts/org/locationtech/jts/operation/overlay/OverlayOp'

4.2 turf

4.1.1 Direct Import

<!-- 使用unpkg -->
<script src="https://unpkg.com/@turf/turf/turf.min.js"></script>

<!-- 在BootCDN上下载指定版本 -->
<script src="https://www.bootcdn.cn/Turf.js/"></script>

4.1.2 NPM

Download and install, you can install all modules at once or only some modules

1) Install all

Install

$ npm install @turf/turf

Import all modules at once

import * as turf from '@turf/turf'

or import separately

import {
    
     lineString, along } from '@turf/turf'

Node environment introduction

const turf = require('@turf/turf')

2) Independent installation

Install

$ npm install @turf/collect

introduce

import collect from '@turf/collect';

5 Spatial Analysis

Here are a few common examples of judging the spatial position relationship, using jsts and turf to judge

5.1 Determine whether to include

// turf
const line = turf.lineString([[1, 1], [1, 2], [1, 3], [1, 4]]);
const point = turf.point([1, 2]);
console.log('turf运算结果:', turf.booleanContains(line, point))

// jsts
const reader = new jsts.io.WKTReader()
const jstsLine = reader.read('LINESTRING (1 1, 1 2, 1 3, 1 4)')
const jstsPoint = reader.read('POINT (1 2)')
console.log('jsts运算结果:', jsts.operation.relate.RelateOp.contains(jstsLine, jstsPoint))

operation result

turf运算结果: true
jsts运算结果: true

5.2 Judging whether to cross

// turf
const line1 = turf.lineString([[-2, 2], [4, 2]]);
const line2 = turf.lineString([[1, 1], [1, 2], [1, 3], [1, 4]]);
console.log('turf运算结果:', turf.booleanCrosses(line1, line2))

// jsts
const reader = new jsts.io.WKTReader()
const jstsLine1 = reader.read('LINESTRING (-2 2, 4 2)')
const jstsLine2 = reader.read('LINESTRING (1 1, 1 2, 1 3, 1 4)')
console.log('jsts运算结果:',jsts.operation.relate.RelateOp.crosses(jstsLine1, jstsLine2))

operation result

turf运算结果: true
jsts运算结果: true

5.3 Judging whether to overlap

// turf
var poly1 = turf.polygon([[[0,0],[0,5],[5,5],[5,0],[0,0]]]);
var poly2 = turf.polygon([[[1,1],[1,6],[6,6],[6,1],[1,1]]])
console.log('turf运算结果:', turf.booleanOverlap(poly1, poly2))

// jsts
var reader = new jsts.io.WKTReader()
var jstsPoly1 = reader.read('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))')
var jstsPoly2= reader.read('POLYGON ((1 1, 1 6, 6 6, 6 1, 1 1))')
console.log('jsts运算结果:',jsts.operation.relate.RelateOp.overlaps(jstsPoly1, jstsPoly2))

operation result

turf运算结果: true
jsts运算结果: true

5.4 Buffer

// turf
const point = turf.point([-90.548630, 14.616599]);
const buffered = turf.buffer(point, 500, {
    
    units: 'miles'});
console.log('turf运算结果:', buffered)

// jsts
const reader = new jsts.io.WKTReader()
const jstsPoint = reader.read('POINT (-90.548630 14.616599)')
const jstsBuffer = jsts.operation.buffer.BufferOp.bufferOp(jstsPoint, 500)
console.log('jsts运算结果:', jstsBuffer)

operation result

turf运算结果: {type: 'Feature', properties: {…}, geometry: {…}}
jsts运算结果: it {_shell: ut, _holes: Array(0), _envelope: null, _userData: null, _factory: Ct, …}

The above is just an introduction. If you think that these two packages can only do this, you are very wrong. In fact, the functions of these two packages are very powerful, and there are a lot of GIS-related calculation operations in it. jsts even supports calculations in 3D space. Welcome everyone to study and dig.

6 Size and performance

6.1 Volume

Let’s talk about the size first. Colleagues who are more demanding on the size of the project may pay more attention to this point. Due to its powerful functions, the package size is relatively large. Below are the sizes of the latest full versions of the two packages.

project Version Source size Compressed *.min.js
jsts 2.6.1 914k 475k
turf 6.3.0 590k

If you don’t often do front-end performance optimization and are not very sensitive to the size, here is a reference data, openlayers6, the compressed code size is close to 1M, which is about twice that of these two.
If our project is a front-end engineering development and you are particularly concerned about traffic issues, you can reduce the final packaging volume of the project by introducing it on demand.

6.2 Performance

6.2.1 jsts on the node side

In terms of performance, taking judging the spatial containment relationship as an example, the author did some tests in the node environment and the browser environment to check their time-consuming situation. The following is part of the code for testing the jsts package on the node side:

var i = 0
var count = 1000000

var time1 = new Date().getTime()
var reader = new jsts.io.WKTReader()
var jstsPolygon = reader.read('POLYGON((1 1, 35 1, 35 84, 1 84, 1 1))')
while (i < count) {
    
    
  var x = Math.random() * 100
  var y = Math.random() * 100
  var p = reader.read(`POINT(${
      
      x} ${
      
      y})`)
  jsts.operation.relate.RelateOp.contains(jstsPolygon, p)
  i++
}
var time2 = new Date().getTime()
console.log(`jsts test done, ${
      
      time2 - time1}ms`)

On the node side, jtst calculates the spatial inclusion relationship 10,000 times, which takes 91ms.

jsts test done, 91ms

If the total is set to 100w, it takes about 3s.

jsts test done, 3042ms

This time-consuming, seems a bit long.
However, there is a problem with the above code, using wktreader to translate the data into jsts geometric objects. In the case of a large number of cycles, this may cause a relatively large error. If you only consider the time-consuming of judging the spatial relationship, let's take a look at it. Adjust the code slightly.

const reader = new jsts.io.WKTReader()
const jstsPolygon = reader.read('POLYGON ((1 1, 35 1, 35 84, 1 84, 1 1))')
let jstsPoints = []
let i = 0
let max = 10000

while (i < max) {
    
    
  let x = Math.random() * 100
  let y = Math.random() * 100
  var jstsPoint = reader.read(`POINT (${
      
      x} ${
      
      y})`)
  jstsPoints.push(jstsPoint)
  i++
}

const time1 = new Date().getTime()
jstsPoints.forEach((p) => {
    
    
  var r = jsts.operation.relate.RelateOp.contains(jstsPolygon, p)
})
const time2 = new Date().getTime()
console.log(`jsts test done, ${
      
      time2 - time1}ms`)

Okay, let's take a look at it again.
10,000 times 19ms.

jsts test done, 19ms

1 million times 233ms

jsts test done, 233ms

10,000 times changed from 91ms to 19ms, and 100 times changed from 3042ms to 233ms. It seems that a lot of time cost is indeed spent in WKTReader.

6.2.2 Comparison between jsts and turf

At first, the author did not mean to compare the two, and it is estimated that the calculation speed of the two should be equivalent. For the sake of the integrity of the test results, run both packages.

function testJsts (count) {
    
    
  const reader = new jsts.io.WKTReader()
  const jstsPolygon = reader.read('POLYGON ((1 1, 35 1, 35 84, 1 84, 1 1))')
  let jstsPoints = []
  
  let i = 0
  while (i < count) {
    
    
    let x = Math.random() * 100
    let y = Math.random() * 100
    var jstsPoint = reader.read(`POINT (${
      
      x} ${
      
      y})`)
    jstsPoints.push(jstsPoint)
    i++
  }

  const time1 = new Date().getTime()
  jstsPoints.forEach((p) => {
    
    
    var r = jsts.operation.relate.RelateOp.contains(jstsPolygon, p)
  })
  const time2 = new Date().getTime()
  console.log(`jsts test done, ${
      
      time2 - time1}ms`)
}

testJsts(10000)
function testTurf (count) {
    
    
  const turfPoints = []
  const polygon = turf.polygon([[
    [1, 1],
    [35, 1],
    [35, 84],
    [1, 84],
    [1, 1]
  ]])
  
  let i = 0
  while (i < count) {
    
    
    let x = Math.random() * 100
    let y = Math.random() * 100
    turfPoints.push(turf.point([x, y]))
    i++
  }
  const time1 = new Date().getTime()
  turfPoints.forEach((p) => {
    
    
    turf.booleanContains(polygon, p)
  })
  const time2 = new Date().getTime()
  console.log(`turf test done, ${
      
      time2 - time1}ms`)
}
testTurf(10000)

Run the above codes on the personal notebook respectively, count 10000, 1000000, 10000000 respectively, run each count three times and take the median of the results.
The result, unexpected...

In the node environment

Bag 10000 times 1000000 times 10000000 times
jsts 20 251 3395
turf 9 137 1257

browser environment

Bag 10000 times 1000000 times 10000000 times
jsts 19 447 4074
turf 10 97 797

Looking horizontally, it seems that turf has higher computing efficiency. Looking vertically, it seems that turf performs better on the browser side than node environment, and jsts seems to be more suitable for node environment. However, here is just a case for comparison, and it cannot be generalized.

Since there are many options for GIS spatial analysis at the back end, there are relatively few opportunities to use it in node. Here we consider more applications in the front-end start.

Both packages are excellent and easy to use, and can make up for the shortcomings of front-end GIS visualization libraries such as openlayers and leaflet, allowing us to process GIS data and perform spatial analysis without relying on the back-end.

This article refers to
1. Turf.js Chinese website https://turfjs.fenxianglu.cn/
2. https://www.npmjs.com/package/jsts
3. http://bjornharrtell.github.io/jsts/
4. https://docs.mapbox.com/help/glossary/turf/

Guess you like

Origin blog.csdn.net/u012413551/article/details/116233450