Show HN: TG – Fast geometry library for C

TG

TG is a geometry library for C that is small, fast, and easy to use.
I designed it for programs that need real-time geospatial, such as geofencing, monitoring, and streaming analysis.

Features

  • Implements OGC Simple Features including Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection.
  • Optimized polygon indexing that introduces two new structures.
  • Reads and writes WKT, WKB, and GeoJSON.
  • Provides a purely functional API that is reentrant and thread-safe.
  • Spatial predicates including “intersects”, “covers”, “touches”, “equals”, etc.
  • Test suite with 100% coverage using sanitizers and Valgrind.
  • Self-contained library that is encapsulated in the single tg.c source file.
  • Pretty darn good performance. 🚀 [benchmarks]

Goals

The main goal of TG is to provide the fastest, most memory efficent geometry library for the purpose of monitoring spatial relationships, specifically operations like point-in-polygon and geometry intersect.

It’s a non-goal for TG to be a full GIS library. Consider GEOS if you need GIS algorithms like generating a convex hull or voronoi diagram.

Performance

TG uses entirely new indexing structures that speed up geometry predicates. It can index more than 10GB per second of point data on modern hardware, while using less than 7% of additional memory, and can perform over 10 million point-in-polygon operations per second, even when using large polygons with over 10K points.

The following benchmark provides an example of the point-in-polygon performance
of TG when using a large polygon. In this case of Brazil, which has 39K points.

Brazil              ops/sec    ns/op  points  hits       built      bytes
tg/none              96,944    10315   39914  3257    46.73 µs    638,720
tg/natural       10,143,419       99   39914  3257    53.17 µs    681,360
tg/ystripes      15,174,761       66   39914  3257   884.06 µs  1,059,548
geos/none            29,708    33661   39914  3257   135.18 µs    958,104
geos/prepared     7,885,512      127   39914  3257  2059.94 µs  3,055,496
  • “built”: Column showing how much time the polygon and index took to construct.
  • “bytes”: Column showing the final in-memory size of the polygon and index.
  • “none”: No indexing was used.
  • “natural”: Using TG Natural indexing
  • “ystripes”: Using TG YStripes indexing
  • “prepared”: Using a GEOS PreparedGeometry

See all benchmarks for more information.

Using

Just drop the “tg.c” and “tg.h” files into your project.
Uses standard C11 so most modern C compilers should work.

Programmer notes

Check out the complete API for detailed information.

Pure functions

TG library functions are thread-safe, reentrant, and (mostly) without side
effects.
The exception being with the use of malloc by some functions like
geometry constructors.
In those cases, it’s the programmer’s responsibiilty to check the return value
before continuing.

struct tg_geom *geom = tg_geom_new_point(-112, 33);
if (!geom) {
    // System is out of memory.
}

Fast cloning

The cloning of geometries, as with tg_geom_clone(), are
O(1) operations that use implicit sharing through an atomic reference counter.
Geometry constructors like tg_geom_new_polygon() will
use this method under the hood to maintain references of its inputs.

While this may only be an implementation detail, it’s important for the
programmer to understand how TG uses memory and object references.

For example:

struct tg_geom *geom = tg_geom_new_polygon(exterior, holes, nholes);

Above, a new geometry “geom” was created and includes a cloned reference to the
tg_ring “exterior” and all of the holes.

Providing TG_NOATOMICS to the compiler will disable the use of atomics and
instead use non-atomic reference counters.

cc -DTG_NOATOMICS tg.c ...

Alternatively, the tg_geom_copy() method is available to perform a deep copy of the
geometry.

Avoid memory leaks

To avoid memory leaks, call tg_geom_free() on geometries
created from geometry constructors,
geometry parsers, tg_geom_clone(), and tg_geom_copy()

In other words, for every tg_geom_new_*(), tg_geom_parse_*(),
tg_geom_clone(), and tg_geom_copy() there should be (eventually and exactly)
one tg_geom_free().

Upcasting

The TG object types tg_line, tg_ring, and
tg_poly can be safely upcasted to a tg_geom with no
cost at runtime.

struct tg_geom *geom1 = (struct tg_geom*)line; // Cast tg_line to tg_geom
struct tg_geom *geom2 = (struct tg_geom*)ring; // Cast tg_ring to tg_geom
struct tg_geom *geom3 = (struct tg_geom*)poly; // Cast tg_poly to tg_geom

This allows for exposing all tg_geom functions to the other
object types.

In addition, the tg_ring type can also cast to a
tg_poly.

struct tg_poly *poly = (struct tg_poly*)ring; // Cast tg_ring to tg_poly

Do not downcast. It’s not generally safe to cast from a tg_geom to other
types.

Example

Create a program that tests if two geometries intersect using WKT as inputs.

n”, argv[0]);
return 1;
}

// Parse the input geometries and check for errors.
struct tg_geom *a=tg_parse_wkt(argv[1]);
if (tg_geom_error(a)) {
fprintf(stderr, “%sn”, tg_geom_error(a));
return 1;
}
struct tg_geom *b=tg_parse_wkt(argv[2]);
if (tg_geom_error(b)) {
fprintf(stderr, “%sn”, tg_geom_error(b));
return 1;
}

// Execute the “intersects” predicate to test if both geometries intersect.
if (tg_geom_intersects(a, b)) {
printf(“yesn”);
} else {
printf(“non”);
}

// Free geometries when done.
tg_geom_free(a);
tg_geom_free(b);
return 0;
}”>

#include 
#include 

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s  n", argv[0]);
        return 1;
    }

    // Parse the input geometries and check for errors.
    struct tg_geom *a = tg_parse_wkt(argv[1]);
    if (tg_geom_error(a)) {
        fprintf(stderr, "%sn", tg_geom_error(a));
        return 1;
    }
    struct tg_geom *b = tg_parse_wkt(argv[2]);
    if (tg_geom_error(b)) {
        fprintf(stderr, "%sn", tg_geom_error(b));
        return 1;
    }

    // Execute the "intersects" predicate to test if both geometries intersect.
    if (tg_geom_intersects(a, b)) {
        printf("yesn");
    } else {
        printf("non");
    }

    // Free geometries when done.
    tg_geom_free(a);
    tg_geom_free(b);
    return 0;
}

Build and run the example:

$ cc -I. examples/intersects.c tg.c
$ ./a.out 'POINT(15 15)' 'POLYGON((10 10,20 10,20 20,10 20,10 10))'

License

TG source code is available under the MIT License.

Note: This article have been indexed to our site. We do not claim legitimacy, ownership or copyright of any of the content above. To see the article at original source Click Here

Related Posts
New Research Links Covid-19 Transmission to Blood Types thumbnail

New Research Links Covid-19 Transmission to Blood Types

Pesquisas científicas já apontaram que o tipo sanguíneo influencia no aumento do risco de infecção pela Covid-19. Agora, um novo estudo pode ajudar a explicar melhor esse comportamento do vírus no corpo humano. De acordo com um levantamento feito por cientistas da Universidade Kent, no Reino Unido, o vírus da Covid-19 se comporta de forma…
Read More
Scientists use 15th century cannon replicas to test medieval gunpowder formulas thumbnail

Scientists use 15th century cannon replicas to test medieval gunpowder formulas

一个由化学家和历史学家组成的跨学科小组想更多地了解几个世纪以来各种火药的配方是如何演变的。研究人员在最近发表在ACS Omega杂志上的一篇论文中描述了他们的发现。他们甚至通过在西点军校射击场发射15世纪投石大炮的复制品来测试其中几个配方。 火药也被称为黑火药,从化学角度来说,它很简单。它是作为燃料的硫磺和木炭(碳)与硝酸钾(KNO3)的混合物,硝酸钾是一种氧化剂,也被称为硝石。黑火药最早于公元904年左右在中国用于战争,到13世纪末,其使用已遍及欧洲和亚洲。现代黑火药配方要求75%的硝石、15%的木炭和10%的硫磺。但几个世纪以来,中世纪的枪炮大师们尝试了许多不同的配方,其中许多包括樟脑、清漆或白兰地等添加剂,其目的至今仍不清楚。到14世纪末,制造商发现,人们可以通过一种湿磨工艺来改善火药的性能。当其他成分被研磨在一起时,会加入某种液体(通常是蒸馏酒),产生一种潮湿的糊状物。糊状物会被卷成球状,然后晒干,这些球在使用前会被战场上的炮手在研钵中压碎。所谓的蛇纹石干混火药在15世纪的欧洲最常使用。标准程序包括用研钵和铁杵将成分研磨在一起,将这些成分还原成细粉可能需要长达24小时。颗粒越小,混合越彻底,火药的燃烧就越迅速和有效。研究人员确定了在公元1336年至1449年之间中世纪文本中记录的20多种不同的配方,并按照这些配方制造自己的不同批次的火药。Riegner等人测试了蛇纹石和粟粒状样品,使用炸弹量热法来记录相对燃烧热和反应速率。他们使用差示扫描量热法来测量燃烧的开始(预燃),以及燃烧扩散的速度,并分析每种配方的残留物以确定燃烧的有效性。该小组还比较了不同的样品制备方法以及有添加剂和无添加剂的配方有效性,并进行了射程炮实验。研究小组发现,在公元1338年和1400年之间,配方中增加了硝石的比例,减少了木炭的数量。这将导致较低的燃烧热量,但对于战场上的中世纪炮手来说,也会更安全。1400年后,炮手们对相对成分进行了更多的调整,稍微减少了硝石,而明显增加了硫磺和木炭,也许是为了在炮手安全和燃烧热量之间找到最佳平衡。很明显,中世纪的枪炮大师至少在某些方面已经对影响火药装填有效功率输出变量有了坚实了解,包括成分的纯度、木炭的品种、颗粒大小和混合方法。
Read More
Best Samsung Galaxy Watch deals for January 2022 thumbnail

Best Samsung Galaxy Watch deals for January 2022

Digital Trends may earn a commission when you buy through links on our site. This year’s Samsung Galaxy Watch deals have started to arrive, so if you’re planning to purchase one of Samsung’s smartwatches, you can already begin buying what’s in your cart. The best models of the Samsung Galaxy Watch often have huge discounts and…
Read More
Index Of News