Coder Social home page Coder Social logo

shamos-hoey's Introduction

shamos-hoey

A fast module for checking if segment intersections exist using the Shamos-Hoey algorithm. Can be used for

  • detecting if a geometry has self-intersections, or
  • if multiple geometries have segments which intersect

Note: This module only detects if an intersection does exist, not where, or how many. If you need to find the points of intersection I suggest using the sweepline-intersections module.

Documentation

Install

npm install shamos-hoey

Basic Use

Valid inputs: Geojson Feature or Geometry including Polygon, LineString, MultiPolygon, MultiLineString, as well as FeatureCollection's.

Returns true if there are no intersections

Returns false if there are intersections

    const noIntersections = require('shamos-hoey')

    const box = {type: 'Polygon', coordinates: [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]}
    noIntersections(box)
    // => true

Complex Use

This library also provide a class-based approach which is helpful if you need to check multiple geometries against a single geometry. This allows you to save the state of the initial event queue with the primary geometry.

    import ShamosHoeyClass from 'shamos-hoey/dist/ShamosHoeyClass'

    // create the base instance
    const sh = new ShamosHoeyClass()
    // populate the event queue with your primary geometry
    sh.addData(largeGeoJson)
    // clone the event queue in the original state so you can reuse it
    const origQueue = sh.cloneEventQueue()

    // now you can iterate through some other set of features saving
    // the overhead of having to populate the complete queue multiple times
    someOtherFeatureCollection.features.forEach(feature => {
        // add another feature to test against your original data
        sh.addData(feature, origQueue)
        // check if those two features intersect
        sh.noIntersections()
    })

API

new ShamosHoeyClass() - creates a new instance

.addData(geojson, existingQueue) - add geojson to the event queue. The second argument for an existingQueue is optional, and takes a queue generated from .cloneEventQueue()

.cloneEventQueue() - clones the state of the existing event queue that's been populated with geojson. Returns a queue that you can pass to the addData method

.noIntersections() - Checks for segment intersections. Returns true if there are no segment intersections or false if there are intersections.

Similar modules

If you need to find the points of self-intersection I suggest using the sweepline-intersections module. The sweeline-intersections module is also smaller (4kb vs 12kb) and very fast for most use cases. It uses alot of the same logic although doesn't inlude a tree structure which makes up the major dependency for this library.

Benchmarks

Detecting an intersection in a polygon with roughly 700 vertices. Note that the other libraries report the intersection point/s.

// Has intersections
// ShamosHoey x 4,132 ops/sec ±0.60% (95 runs sampled)
// SweeplineIntersections x 2,124 ops/sec ±0.70% (92 runs sampled)
// GPSI x 36.85 ops/sec ±1.06% (64 runs sampled)
// - Fastest is ShamosHoey

For the class-based module vs the basic use on a very large geojson file (approx. 14,000 vertices)

// Class-based reuse vs Basic
// ShamosHoey x 1,011 ops/sec ±8.12% (89 runs sampled)
// ShamosHoeyClass x 2,066 ops/sec ±0.60% (93 runs sampled)
// - Fastest is ShamosHoeyClass

Further Reading

Original Paper

Article on Geom algorithms website

shamos-hoey's People

Contributors

rowanwins avatar sheerun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

shamos-hoey's Issues

Tag releases on git and manage a changelog (using GitHub releases?)

Hi, thank you for your amazing library ✋

When you make a new release on npm, can you also tag releases on git?

Using GitHub release may also be super useful to handle and manage the git tags along with changelogs.

As an example see other OSS projects like (I'm picking them randomly)

You may also have a look at https://semver.org for semantic versioning

Can shamos-hoey detect multiple intersections on a single segment?

{
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              12.65625,
              22.59372606392931
            ],
            [
              -0.703125,
              8.754794702435618
            ],
            [
              36.5625,
              -25.16517336866393
            ],
            [
              22.5,
              4.565473550710278
            ],
            [
              -1.0546875,
              -23.24134610238612
            ],
            [
              40.078125,
              2.8113711933311403
            ],
            [
              12.65625,
              22.59372606392931
            ]
          ]
        ]
      }
    }

3 intersections should be reported by only 2 are currently reported

Is this an implementation problem or can shamos-hoey not do this because it's original design is only to detect if a self-intersection exists (and not actually return the self-intersections)?

If it can't detect multiple intersections on a line then I should probably change the API to reflect that and get rid of the option that allows you to return the intersections and just make it a true/false response only

not detecting intersections in this case.

The algorithm did not detect intersections in the following coordinates of a polygon.

const box = {
  type: "Polygon",
  coordinates: [
    [
      [90.39974817514972, 23.873991637113175],
      [90.40572498813789, 23.872911671285607],
      [90.40575223587952, 23.874804784092955],
      [90.4053503301788, 23.875873772177464],
      [90.40030947983682, 23.87861920694433],
      [90.39621276706808, 23.878127145947108],
      [90.3921160542993, 23.87747811375598],
      [90.39023118436477, 23.882439106594358],
      [90.38922437638114, 23.88382080428333],
      [90.38693010807042, 23.88390756033242],
      [90.38277413010324, 23.877399629023042],
      [90.37991123348131, 23.875909945040224],
      [90.37897952735014, 23.873517645288647],
      [90.37864863603832, 23.871792452376454],
      [90.37969103574207, 23.869125362621826],
      [90.3834212654788, 23.86583190806136],
      [90.38663651108469, 23.86556034446246],
      [90.39203703403477, 23.865488168279917],
      [90.3940094372637, 23.865251113185526],
      [90.3959818404927, 23.86548501039226],
      [90.39992664695066, 23.865638836372778],
      [90.40017732859211, 23.874030880518813],
      [90.39974817514972, 23.873991637113175],
      [90.39974817514972, 23.873991637113175],
      [90.39974817514972, 23.873991637113175],
    ],
  ],
};

It is a geoJSON coordinate list, the screenshot from geoJSON.io (Maybe the intersection only happens in spherical surfaces)

Screenshot 2023-02-23 at 1 42 03 PM

Get error with 'ReferenceError: L is not defined'

It appears the distributed package contains a call to a debug call?
image

It appears the cause is because in the package.json this line exists:

  "module": "src/main.js",

So instead of using the bundled version, it uses that instead which means it will execute the debug functions. In the compiled version, the rollup config is stripping out that function.

Anyway... the right the thing to do seems to be to probably remove that line in the package.json.

Some polygons produce false negatives

Hello,

thanks for providing this valuable algorithm!

I get some false negatives here, e.g.

[{"x":233,"y":115},{"x":190,"y":186},{"x":247,"y":260},{"x":430,"y":304},{"x":531,"y":244},{"x":510,"y":170},{"x":415,"y":104},{"x":553,"y":196}]

Please let me know if I can help debugging.

Here is another polygon with comparable coordinates.

grafik

Getting Uncaught (in promise) TypeError: Cannot read property 'segmentAbove' of null

Hi,

I wanted to use your module to check if a polygonal chain contains self-intersection. However, for some input coordinates, I get the error stated in the title. One example where this error occurs is the following:

const coords = [[[258.75,97.93749809265137],[266.25,96.68749809265137],[272.5,94.18749809265137],[281.25,91.68749809265137],[287.5,89.18749809265137],[301.25,87.93749809265137],[312.5,87.93749809265137],[322.5,87.93749809265137],[332.5,87.93749809265137],[341.25,87.93749809265137],[352.5,90.43749809265137],[362.5,90.43749809265137],[373.75,94.18749809265137],[388.75,95.43749809265137],[401.25,96.68749809265137],[411.25,100.43749809265137],[421.25,100.43749809265137],[443.75,109.18749809265137],[453.75,110.43749809265137],[462.5,111.68749809265137],[471.25,114.18749809265137],[478.75,114.18749809265137],[483.75,115.43749809265137],[491.25,116.68749809265137],[497.5,119.18749809265137],[501.25,119.18749809265137],[503.75,120.43749809265137],[507.5,120.43749809265137],[512.5,120.43749809265137],[516.25,121.68749809265137],[518.75,124.18749809265137],[523.75,125.43749809265137],[528.75,125.43749809265137],[536.25,129.18749809265137],[542.5,130.43749809265137],[547.5,134.18749809265137],[553.75,135.43749809265137],[558.75,135.43749809265137],[563.75,139.18749809265137],[567.5,140.43749809265137],[572.5,144.18749809265137],[576.25,145.43749809265137],[578.75,149.18749809265137],[582.5,150.43749809265137],[587.5,154.18749809265137],[591.25,156.68749809265137],[593.75,160.43749809265137],[598.75,165.43749809265137],[602.5,169.18749809265137],[607.5,171.68749809265137],[608.75,175.43749809265137],[612.5,179.18749809265137],[616.25,181.68749809265137],[617.5,185.43749809265137],[621.25,189.18749809265137],[622.5,190.43749809265137],[622.5,194.18749809265137],[623.75,195.43749809265137],[626.25,196.68749809265137],[626.25,199.18749809265137],[627.5,201.68749809265137],[627.5,205.43749809265137],[628.75,210.43749809265137],[628.75,210.43749809265137],[628.75,214.18749809265137],[627.5,216.68749809265137],[625,220.43749809265137],[621.25,225.43749809265137],[618.75,230.43749809265137],[616.25,235.43749809265137],[611.25,240.43749809265137],[607.5,245.43749809265137],[605,250.43749809265137],[601.25,255.43749809265137],[597.5,260.43749809265137],[595,264.18749809265137],[592.5,265.43749809265137],[591.25,266.68749809265137],[588.75,270.43749809265137],[586.25,271.68749809265137],[583.75,275.43749809265137],[582.5,275.43749809265137],[580,276.68749809265137],[578.75,280.43749809265137],[576.25,280.43749809265137],[572.5,284.18749809265137],[570,285.43749809265137],[568.75,285.43749809265137],[567.5,286.68749809265137],[566.25,289.18749809265137],[565,290.43749809265137],[563.75,290.43749809265137],[561.25,291.68749809265137],[560,294.18749809265137],[557.5,295.43749809265137],[556.25,295.43749809265137],[553.75,296.68749809265137],[551.25,299.18749809265137],[548.75,300.43749809265137],[545,300.43749809265137],[542.5,301.68749809265137],[538.75,304.18749809265137],[535,305.43749809265137],[531.25,305.43749809265137],[528.75,306.68749809265137],[525,309.18749809265137],[521.25,310.43749809265137],[518.75,311.68749809265137],[515,311.68749809265137],[511.25,311.68749809265137],[508.75,314.18749809265137],[506.25,314.18749809265137],[503.75,314.18749809265137],[501.25,314.18749809265137],[498.75,314.18749809265137],[496.25,314.18749809265137],[493.75,314.18749809265137],[492.5,314.18749809265137],[490,314.18749809265137],[488.75,314.18749809265137],[486.25,314.18749809265137],[485,314.18749809265137],[482.5,314.18749809265137],[480,314.18749809265137],[478.75,312.93749809265137],[476.25,312.93749809265137],[475,311.68749809265137],[472.5,310.43749809265137],[471.25,310.43749809265137],[470,309.18749809265137],[468.75,307.93749809265137],[467.5,306.68749809265137],[466.25,306.68749809265137],[465,305.43749809265137],[463.75,305.43749809265137],[461.25,304.18749809265137],[461.25,302.93749809265137],[460,301.68749809265137],[458.75,301.68749809265137],[457.5,300.43749809265137],[456.25,300.43749809265137],[455,299.18749809265137],[453.75,299.18749809265137],[452.5,297.93749809265137],[451.25,297.93749809265137],[450,296.68749809265137],[448.75,295.43749809265137],[447.5,295.43749809265137],[446.25,294.18749809265137],[445,294.18749809265137],[443.75,292.93749809265137],[442.5,292.93749809265137],[441.25,291.68749809265137],[438.75,290.43749809265137],[437.5,290.43749809265137],[436.25,290.43749809265137],[435,289.18749809265137],[433.75,287.93749809265137],[432.5,286.68749809265137],[430,285.43749809265137],[428.75,284.18749809265137],[427.5,282.93749809265137],[426.25,282.93749809265137],[425,281.68749809265137],[423.75,280.43749809265137],[423.75,279.18749809265137],[422.5,277.93749809265137],[421.25,276.68749809265137],[420,276.68749809265137],[418.75,275.43749809265137],[418.75,274.18749809265137],[417.5,272.93749809265137],[416.25,271.68749809265137],[415,271.68749809265137],[415,270.43749809265137],[413.75,270.43749809265137],[413.75,269.18749809265137],[412.5,269.18749809265137],[411.25,269.18749809265137],[411.25,267.93749809265137],[411.25,266.68749809265137],[411.25,265.43749809265137],[410,265.43749809265137],[410,264.18749809265137],[408.75,262.93749809265137],[408.75,261.68749809265137],[407.5,260.43749809265137],[407.5,256.68749809265137],[407.5,254.18749809265137],[406.25,249.18749809265137],[406.25,242.93749809265137],[406.25,237.93749809265137],[406.25,232.93749809265137],[406.25,226.68749809265137],[406.25,222.93749809265137],[406.25,219.18749809265137],[406.25,215.43749809265137],[406.25,212.93749809265137],[406.25,210.43749809265137],[406.25,207.93749809265137],[406.25,205.43749809265137],[407.5,202.93749809265137],[407.5,200.43749809265137],[407.5,199.18749809265137],[407.5,197.93749809265137],[408.75,196.68749809265137],[408.75,195.43749809265137],[408.75,194.18749809265137],[408.75,192.93749809265137],[411.25,192.93749809265137],[412.5,191.68749809265137],[413.75,191.68749809265137],[416.25,190.43749809265137],[417.5,190.43749809265137],[417.5,190.43749809265137]]]
const polygonalChain = {type: 'MultiLineString', coordinates: coords}
console.log(isSimple(polygonalChain))

And here is the error message in detail:

/node_modules/shamos-hoey/dist/shamosHoey.esm.js:1067 Uncaught (in promise) TypeError: Cannot read property 'segmentAbove' of null
    at isSimple (/node_modules/shamos-hoey/dist/shamosHoey.esm.js:1067)
    at eval (DrawingCanvas.svelte:85)
    at run (index.mjs:18)
    at Array.map (<anonymous>)
    at index.mjs:1294
    at flush (index.mjs:635)
    at ...

I would be very happy if you could help me with that.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.