Giter VIP home page Giter VIP logo

s2's Introduction

CircleCI

Node.js JavaScript & TypeScript bindings for Google S2.

What is S2?

S2 is a library from Google for easily storing, indexing, and retrieving geographic locations.

Geographic regions can be indexed by S2 cell ids of various levels in a data store and then later retrieved by these ids for extremely quick geolocation lookups.

The Library

The goal of this library is to maintain Node.js TypeScript bindings for the latest version of Google's C++ S2 library.

Other JavaScript projects available on GitHub appear unmaintained.

The project has been built against Node's N-API, meaning that it's compatible across Node.js versions that support BigInt. This means that Node.js version 9 and below are unsupported.

As of today, the library is built and tested against Node.js 16, 18 and 20. The library has been in production use at Radar and has been built against OS X and Linux. Feel free to open an issue or PR if you'd like other platform support.

See test.sh for more details.

Usage

To install:

npm install @radarlabs/s2

To run tests (you'll need Docker):

./test.sh

S2 Cells can be generated from BigInt S2 IDs or string tokens:

const s2 = require('@radarlabs/s2');

const cell1 = new s2.CellId(9926595695177891840n);
console.log(cell1.token());
> 89c25a31

const cell2 = new s2.CellId('89c25a31');
console.log(cell2.id());
> 9926595695177891840n

To generate a covering for a given area:

const s2 = require('@radarlabs/s2');

# an array of lat/lng pairs representing a region (a part of Brooklyn, in this case)
const loopLLs = [[40.70113825399865,-73.99229764938354],[40.70113825399865,-73.98766279220581],[40.70382234072197,-73.98766279220581],[40.70382234072197,-73.99229764938354]];

# map to an array of normalized s2.LatLng
const s2LLs = loopLLs.map(([lat, lng]) => (new s2.LatLng(lat, lng)));

# generate s2 cells to cover this polygon
const s2level = 14;
const covering = s2.RegionCoverer.getCoveringTokens(s2LLs, { min: s2level, max: s2level });
covering.forEach(c => console.log(c));

> 89c25a31
> 89c25a33
> 89c25a35
> 89c25a37

# check if a point is contained inside this region
const point = new s2.CellId(new s2.LatLng(40.70248844447621, -73.98991584777832));
const pointAtLevel14 = point.parent(s2level);
console.log(pointAtLevel14.token());
> 89c25a31

const coveringSet = new Set(covering);
console.log(coveringSet.contains(pointAtLevel14.token()));
> true

To generate a covering for a given radius around a point:

const s2 = require('@radarlabs/s2');

# make an S2 latlng object for downtown San Diego
const s2LatLong = new s2.LatLng(32.715651, -117.160542);

# set cell covering options so the biggest region is a 6 and smallest is a 13, and limit to 10 cells
const cellCoveringOptions = {min: 6, max: 13, max_cells: 10};

# get the cells (with the size range allowed) covering a 10,000 meter search radius centered on the given location
# Note that this call returns a CellUnion object instead of a list of tokens, which is useful for comparisons
const coveringCells = s2.RegionCoverer.getRadiusCovering(s2LatLong, 10000, cellCoveringOptions);
# For this example though, we'll loop over the cellIds within the CellUnion and get their tokens
console.log(coveringCells.cellIds().map((cellId) => cellId.token()));

> 80d94d
> 80d951
> 80d953
> 80d955
> 80d956c
> 80dbffc
> 80dc01
> 80deab
> 80dead
> 80deb2ac

# the "coveringCells" CellUnion is like the "coveringSet" from the previous example, so can be used directly without converting to a set
# test by checking a cell centered at our lat long
console.log(coveringCells.has(new s2.CellId(s2LatLong)));
> true

Here's a visualization of the above set of covering cells. The center of the 10k radius is in downtown San Diego.

Check if a cell is contained in another:

const c1 = s2.CellId.fromToken('89c25a37')
const c2 = s2.CellId.fromToken('89c25')
c2.contains(c1)
> true
c1.contains(c2)
> false

If you'd like to see more functionality, feel free to open an issue or create a pull request.

More detailed usage can be found in the tests folder.

Versioning

The Node S2 is library is at its infancy, so APIs are likely to change. In order to help with versioning, we publish TypeScript bindings so that your compiler can check if anything has changed. To keep up with updates, see CHANGELOG.md

Resources

s2's People

Contributors

ashalam avatar blackmad avatar dependabot[bot] avatar jkao avatar jpangburn avatar kochis avatar mast avatar tjulien 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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

s2's Issues

Error deploying on Vercel with Node v18

Vercel is depreciating Node 16 soon but I'm having an issue getting it to work on version 18. Deploying in a serverless function on Node 16 works fine, but on Node 18 it returns an error saying the s2.node module is missing. I created a vercel-build script to locate the file in the build step and it seems to exist in the same place that it does in the working deployment. I'm not really sure how else to troubleshoot this.

Node 16 (working):
https://radarlabs-71k264462-unilease1.vercel.app/api/radius

Node 18 (not working):
https://radarlabs-9xyzsq3jp-unilease1.vercel.app/api/radius

Function Log error message:

Cannot find module '/var/task/node_modules/@radarlabs/s2/lib/binding/Release/node-v108-linux-x64/s2.node'
Require stack:
- /var/task/node_modules/@radarlabs/s2/index.js
Did you forget to add it to "dependencies" in `package.json`?

Here is a repo I made to recreate the issue:
https://github.com/unilease/vercel-radarlabs-s2-mre

Typos in Readme

line:
const pointAtLevel14 = point.parent(s2Level);
should be:
const pointAtLevel14 = point.parent(s2level); // both lowercase l

line:
console.log(coveringCells.contains(new s2.CellId(s2LatLong)));
should be:
console.log(coveringCells.has(new s2.CellId(s2LatLong)));

libc version mismatch Node 18 - AWS Lambda

We ran into an issue when we updated to node 18 on AWS Lambda, the binary for node 18 in the node-s2-binaries bucket has been compiled with libc 2.29, whereas the nodejs18.x lambda container has 2.26 causing the following runtime error:

node:internal/modules/cjs/loader:1338
  return process.dlopen(module, path.toNamespacedPath(filename));
                 ^

Error: /lib64/libm.so.6: version `GLIBC_2.29' not found (required by /var/task/node_modules/@radarlabs/s2/lib/binding/Release/node-v108-linux-x64/s2.node)
    at Module._extensions..node (node:internal/modules/cjs/loader:1338:18)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Module._load (node:internal/modules/cjs/loader:958:12)
    at Module.require (node:internal/modules/cjs/loader:1141:19)
    at require (node:internal/modules/cjs/helpers:110:18)
    at Object.<anonymous> (/var/task/node_modules/@radarlabs/s2/index.js:5:12)
    at Module._compile (node:internal/modules/cjs/loader:1254:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Module._load (node:internal/modules/cjs/loader:958:12) {
  code: 'ERR_DLOPEN_FAILED'
}

I've created an example repo reproducing the problem using the lambda docker container.

Currently the libc version mentioned in the node-pre-gyp versioning documentation isn't included in the package.json > binary > package_name property. I think it would be good to add this, I'll attempt to, and see what you think.

Add check for unsupported platforms

It would be nice if the install would check for an unsupported platform, like Windows, and gracefully exist if not compatible - similiar to how it is handled with fsevent.

info [email protected]: The platform "linux" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.

cellunion contains method not working

for [ -122.435577, 37.751363] this location I am creating radius covering[s2.RegionCoverer.getRadiusCovering] cellunion with 500 m radius
and I am trying that cell union contains this point [-122.434339,37.751433 ]
It returns false every time
I tried same scenario for other locations as well but it was working for them

build: fatal error: absl/hash/hash.h not found

When running ./test.sh on MacOSX12.6 →
#8 8.141 ../third_party/s2geometry/src/s2/s2point.h:21:10: fatal error: absl/hash/hash.h: No such file or directory
#8 8.141 #include "absl/hash/hash.h"

Does radar have updated docker build files that use abseil-cpp as a github submodule? Or updated CMakeFiles used for in-house builds?

Support ARM 64

Hi Maintainers,

Thank you for the library. I'm wondering if you could add ARM 64 support for newer Mac. Thanks!

Please find the error message below:

error

builder: segmentation fault

Hi guys,

I was having a play with this lib today when I encountered the following seg fault:

const SegfaultHandler = require('segfault-handler');
SegfaultHandler.registerHandler('crash.log');

// https://developers.google.com/maps/documentation/javascript/examples/layer-data-polygon
// Define the LatLng coordinates for the outer path.
const outerCoords = [
  { lat: -32.364, lng: 153.207 }, // north west
  { lat: -35.364, lng: 153.207 }, // south west
  { lat: -35.364, lng: 158.207 }, // south east
  { lat: -32.364, lng: 158.207 }, // north east
];

// Define the LatLng coordinates for an inner path.
const innerCoords1 = [
  { lat: -33.364, lng: 154.207 },
  { lat: -34.364, lng: 154.207 },
  { lat: -34.364, lng: 155.207 },
  { lat: -33.364, lng: 155.207 },
];

// Define the LatLng coordinates for another inner path.
const innerCoords2 = [
  { lat: -33.364, lng: 156.207 },
  { lat: -34.364, lng: 156.207 },
  { lat: -34.364, lng: 157.207 },
  { lat: -33.364, lng: 157.207 },
];

const s2 = require('@radarlabs/s2');
const builder = new s2.Builder()

builder.addLoop(new s2.Loop(outerCoords.map(c => new s2.LatLng(c.lat, c.lng))))
builder.addLoop(new s2.Loop(innerCoords1.map(c => new s2.LatLng(c.lat, c.lng))))
// builder.addLoop(new s2.Loop(innerCoords2.map(c => new s2.LatLng(c.lat, c.lng))))

const polygon = builder.build()
PID 50798 received SIGSEGV for address: 0x9
0   segfault-handler.node               0x000000011246ffb0 _ZL16segfault_handleriP9__siginfoPv + 304
1   libsystem_platform.dylib            0x00007fff6dd2a5fd _sigtramp + 29
2   libsystem_malloc.dylib              0x00007fff6dcecd8b free_tiny + 459
3   s2.node                             0x00000001124b9bc7 _ZN19MutableS2ShapeIndex5ClearEv + 23
4   s2.node                             0x000000011255645a _ZN9S2Polygon10InitNestedENSt3__16vectorINS0_10unique_ptrI6S2LoopNS0_14default_deleteIS3_EEEENS0_9allocatorIS6_EEEE + 42
5   s2.node                             0x00000001125586be _ZN9S2Polygon12InitOrientedENSt3__16vectorINS0_10unique_ptrI6S2LoopNS0_14default_deleteIS3_EEEENS0_9allocatorIS6_EEEE + 126
6   s2.node                             0x0000000112513b15 _ZN13s2builderutil14S2PolygonLayer5BuildERKN9S2Builder5GraphEP7S2Error + 1493
7   s2.node                             0x00000001124d85c9 _ZN9S2Builder11BuildLayersEv + 905
8   s2.node                             0x00000001124d7fef _ZN9S2Builder5BuildEP7S2Error + 383
9   s2.node                             0x0000000112480c5b _ZN7Builder5BuildERKN4Napi12CallbackInfoE + 107
10  s2.node                             0x000000011248230b _ZZN4Napi10ObjectWrapI7BuilderE29InstanceMethodCallbackWrapperEP10napi_env__P20napi_callback_info__ENKUlvE_clEv + 139
11  s2.node                             0x000000011248222a _ZN4Napi10ObjectWrapI7BuilderE29InstanceMethodCallbackWrapperEP10napi_env__P20napi_callback_info__ + 42
12  node                                0x0000000105d178e1 _ZN6v8impl12_GLOBAL__N_123FunctionCallbackWrapper6InvokeERKN2v820FunctionCallbackInfoINS2_5ValueEEE + 119
13  node                                0x0000000105edd286 _ZN2v88internal25FunctionCallbackArguments4CallENS0_15CallHandlerInfoE + 520
14  node                                0x0000000105edc948 _ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE + 769
15  node                                0x0000000105edc01f _ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE + 246
16  node                                0x000000010651d1f9 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 57
17  node                                0x00000001064b64ab Builtins_InterpreterEntryTrampoline + 203
[1]    50798 segmentation fault  node example.js

If I uncomment the third loop, I get a different error:

node(50860,0x113123dc0) malloc: *** error for object 0xe8df8948105f8b48: pointer being freed was not allocated
node(50860,0x113123dc0) malloc: *** set a breakpoint in malloc_error_break to debug
[1]    50860 abort      node example.js

question: RegionCoverer interface

Heya, I was checking out the RegionCoverer today and I noticed that the GetCovering* methods currently only support a single Loop (s2.LatLng[]) for the first argument.

Diving into the C++ code I see that the S2RegionCoverer::GetCovering method accepts an interface, and that interface provides an abstraction so it can operate on all sorts of polygonal geometries and spherical geometry such as discs (S2Cap).

Similarly in the Go Port the s2.Region is also an interface.

Looking at the C++ code I also noticed that these methods (such as GetRadiusCovering) don't exist in the C++ API, but are convenience methods of the JS API.

It would be really cool if I could pass a Polygon to a GetCovering() method and it compute the covering, considering any holes etc which I'm not confident in computing manually via CellUnion intersection of each ring, due to various error cases which could creep in (such as the holes being too large).

Do you have any long-term plans for developing the RegionCoverer? if so would you consider reverting to an API more similar to the C++ one which supports interfaces, if that's even possible in JS/TS?

Could not resolve "mock-aws-s3"

Hello

I started a starter react project with vite and got thoses errors below
I wanted to test it quickly in a frontend with mapbox and then put the logic in the back

To reproduce

  • pnpm create vite radars2 --template react-ts
  • pnpm install @radarlabs/s2
  • change app.tsx
  • pnpm dev
import './App.css';

import viteLogo from '/vite.svg';
import { useState } from 'react';

import reactLogo from './assets/react.svg';

const s2 = require('@radarlabs/s2');

function App() {
  const [count, setCount] = useState(0);
  const s2LatLong = new s2.LatLng(32.715651, -117.160542);
  const cellCoveringOptions = { min: 6, max: 13, max_cells: 10 };
  const coveringCells = s2.RegionCoverer.getRadiusCovering(
    s2LatLong,
    10000,
    cellCoveringOptions
  );
  console.log(coveringCells.cellIds().map((cellId) => cellId.token()));
  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;
[ERROR] Could not resolve "mock-aws-s3"

    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28:
      43 │     const AWSMock = require('mock-aws-s3');
         ╵                             ~~~~~~~~~~~~~

  You can mark the path "mock-aws-s3" as external to exclude it from the bundle, which will remove
  this error. You can also surround this "require" call with a try/catch block to handle this
  failure at run-time instead of bundle-time.

✘ [ERROR] Could not resolve "aws-sdk"

    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22:
      76 │   const AWS = require('aws-sdk');
         ╵                       ~~~~~~~~~

  You can mark the path "aws-sdk" as external to exclude it from the bundle, which will remove this
  error. You can also surround this "require" call with a try/catch block to handle this failure at
  run-time instead of bundle-time.

✘ [ERROR] Could not resolve "nock"

    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23:
      112 │   const nock = require('nock');
          ╵                        ~~~~~~

  You can mark the path "nock" as external to exclude it from the bundle, which will remove this
  error. You can also surround this "require" call with a try/catch block to handle this failure at
  run-time instead of bundle-time.

Windows Install

Hi there,

After installing necessary build-tools, I am getting swarmed by a ton on errors during the post-install build.
I have provided a small snippet to try and illustrate the type of errors I am getting, though they are filling up my buffer entirely.

Some more info:

  • Windows 10 Build 17763.805
  • Node 10.16.0
  • Tried with both Python 2.7.16
  • windows-build-tools 5.2.2

Cheers!

Error snippet:

  c:\users\<...>\node_modules\@radarlabs\s2\third_party\s2geometry\src\s2\s1angle.h(195): note: failure was caused by control reaching the end of a constexpr function (compiling source file ..\third_party\s2geometry\src\s2\s2wedge_relations.cc)                                                                         
      c:\users\<...>\node_modules\@radarlabs\s2\third_party\s2geometry\src\s2\s1angle.h( 307): error C2065: 'M_PI': undeclared identifier (compiling source file ..\third_party\s2geometry\src\s2\s2wedge_relati ons.cc) [C:\Users\<...>\node_modules\@radarlabs\s2\build\s2.vcxproj]              
      c:\users\<...>\node_modules\@radarlabs\s2\third_party\s2geometry\src\s2\base\port.h(74): fatal error C1189: #error:  chars must be unsigned!  Use the /J flag on the compiler command line.  // NOLINT (c ompiling source file ..\third_party\s2geometry\src\s2\s2text_format.cc) [C:\Users\<...>\node_modules\@radarlabs\s2\build\s2.vcxproj]  

Deprecation issues - external dependencies

I was wondering if there are plans to resolve these issues in the future:

npm WARN deprecated [email protected]: this library is no longer supported
npm WARN deprecated [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated [email protected]: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated [email protected]: Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future

I see low activity on this project. is there another repo/library where it is being maintained?

Missing Expected Exports

Missing classes such as S1Angle etc. Was curious why these are not a part of the repo.

Thank you,
Kevin

Can I use this library to make a UDF in BigQuery?

Hello,

Thanks for the library, it's really useful. I use S2 cells a lot within BigQuery and it would be really useful to be able to access these functions directly within BigQuery.

Is it possible to do so? I've bundled a javascript library before and uploaded it to the Google Cloud Bucket which allowed me to access the functions, but i'm not sure if I can do it with this library if it contains bindings.

Any help would be really appreciated.

Thanks,
Xavier

Type mismatch - node 19 mac M1 & missing dependency on node 20

I think there is a type mismatch between the current latest version and the types available on node20.
Using this code Code:

export const getLatLonByHash = (hash: string): s2.LatLng => {
  const point = s2.CellId.fromToken(hash)
  const cell = new s2.Cell(point)

  const latlon = new s2.LatLng(cell.getCenter())

  return latlon
}

I get this error:

  const cell = new s2.Cell(point)
               ^
TypeError: CellId expected.

This is currently happening on node 19 and now after updating it happens on node 20 as well.

  • node version: v20.2.0
  • @radarlabs/s2 version: "0.0.5"

EDIT: I have edited this description to highlight just one issue. the missing node-v115-darwin-arm64 module was user error.

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.