Real-time Geospatial and Geofencing

Tile38

Slack Channel Docker Ready

Tile38 is an open source (MIT licensed), in-memory geolocation data store, spatial index, and realtime geofence. It supports a variety of object types including lat/lon points, bounding boxes, XYZ tiles, Geohashes, and GeoJSON.

This README is quick start document. You can find detailed documentation at https://tile38.com.

Nearby Within Intersects Geofencing Roaming Geofences

Features

Components

  • tile38-server - The server
  • tile38-cli - Command line interface tool
  • tile38-benchmark - Server benchmark tool

Getting Started

Getting Tile38

Perhaps the easiest way to get the latest Tile38 is to use one of the pre-built release binaries which are available for OSX, Linux, FreeBSD, and Windows. Instructions for using these binaries are on the GitHub releases page.

Docker

To run the latest stable version of Tile38:

docker pull tile38/tile38
docker run -p 9851:9851 tile38/tile38

Visit the Tile38 hub page for more information.

Homebrew (macOS)

Install Tile38 using Homebrew

brew install tile38
tile38-server

Building Tile38

Tile38 can be compiled and used on Linux, OSX, Windows, FreeBSD, and probably others since the codebase is 100% Go. We support both 32 bit and 64 bit systems. Go must be installed on the build machine.

To build everything simply:

$ make

To test:

$ make test

Running

For command line options invoke:

$ ./tile38-server -h

To run a single server:

$ ./tile38-server

# The tile38 shell connects to localhost:9851
$ ./tile38-cli
> help

Playing with Tile38

Basic operations:

$ ./tile38-cli

# add a couple of points named 'truck1' and 'truck2' to a collection named 'fleet'.
> set fleet truck1 point 33.5123 -112.2693   # on the Loop 101 in Phoenix
> set fleet truck2 point 33.4626 -112.1695   # on the I-10 in Phoenix

# search the 'fleet' collection.
> scan fleet                                 # returns both trucks in 'fleet'
> nearby fleet point 33.462 -112.268 6000    # search 6 kilometers around a point. returns one truck.

# key value operations
> get fleet truck1                           # returns 'truck1'
> del fleet truck2                           # deletes 'truck2'
> drop fleet                                 # removes all 

Tile38 has a ton of great commands.

Fields

Fields are extra data that belongs to an object. A field is always a double precision floating point. There is no limit to the number of fields that an object can have.

To set a field when setting an object:

> set fleet truck1 field speed 90 point 33.5123 -112.2693             
> set fleet truck1 field speed 90 field age 21 point 33.5123 -112.2693

To set a field when an object already exists:

> fset fleet truck1 speed 90

Searching

Tile38 has support to search for objects and points that are within or intersects other objects. All object types can be searched including Polygons, MultiPolygons, GeometryCollections, etc.

Search Within

Within

WITHIN searches a collection for objects that are fully contained inside a specified bounding area.

Search Intersects

Intersects

INTERSECTS searches a collection for objects that intersect a specified bounding area.

Search Nearby

Nearby

NEARBY searches a collection for objects that intersect a specified radius.

Search options

SPARSE - This option will distribute the results of a search evenly across the requested area.
This is very helpful for example; when you have many (perhaps millions) of objects and do not want them all clustered together on a map. Sparse will limit the number of objects returned and provide them evenly distributed so that your map looks clean.

You can choose a value between 1 and 8. The value 1 will result in no more than 4 items. The value 8 will result in no more than 65536. 1=4, 2=16, 3=64, 4=256, 5=1024, 6=4098, 7=16384, 8=65536.

No SparsingSearch Within Sparse 1Search Within Sparse 2Search Within Sparse 3Search Within Sparse 4Search Within Sparse 5Search Within

Please note that the higher the sparse value, the slower the performance. Also, LIMIT and CURSOR are not available when using SPARSE.

WHERE - This option allows for filtering out results based on field values. For example
nearby fleet where speed 70 +inf point 33.462 -112.268 6000 will return only the objects in the 'fleet' collection that are within the 6 km radius and have a field named speed that is greater than 70.

Multiple WHEREs are concatenated as and clauses. WHERE speed 70 +inf WHERE age -inf 24 would be interpreted as speed is over 70 and age is less than 24.

The default value for a field is always 0. Thus if you do a WHERE on the field speed and an object does not have that field set, the server will pretend that the object does and that the value is Zero.

MATCH - MATCH is similar to WHERE except that it works on the object id instead of fields.
nearby fleet match truck* point 33.462 -112.268 6000 will return only the objects in the 'fleet' collection that are within the 6 km radius and have an object id that starts with truck. There can be multiple MATCH options in a single search. The MATCH value is a simple glob pattern.

CURSOR - CURSOR is used to iterate though many objects from the search results. An iteration begins when the CURSOR is set to Zero or not included with the request, and completes when the cursor returned by the server is Zero.

NOFIELDS - NOFIELDS tells the server that you do not want field values returned with the search results.

LIMIT - LIMIT can be used to limit the number of objects returned for a single search request.

Geofencing

Geofence animation

A geofence is a virtual boundary that can detect when an object enters or exits the area. This boundary can be a radius, bounding box, or a polygon. Tile38 can turn any standard search into a geofence monitor by adding the FENCE keyword to the search.

Tile38 also allows for Webhooks to be assigned to Geofences.


A simple example:

> nearby fleet fence point 33.462 -112.268 6000

This command opens a geofence that monitors the 'fleet' collection. The server will respond with:

{"ok":true,"live":true}

And the connection will be kept open. If any object enters or exits the 6 km radius around 33.462,-112.268 the server will respond in realtime with a message such as:

{"command":"set","detect":"enter","id":"truck02","object":{"type":"Point","coordinates":[-112.2695,33.4626]}}

The server will notify the client if the command is del | set | drop.

  • del notifies the client that an object has been deleted from the collection that is being fenced.
  • drop notifies the client that the entire collection is dropped.
  • set notifies the client that an object has been added or updated, and when it's position is detected by the fence.

The detect may be one of the following values.

  • inside is when an object is inside the specified area.
  • outside is when an object is outside the specified area.
  • enter is when an object that was not previously in the fence has entered the area.
  • exit is when an object that was previously in the fence has exited the area.
  • cross is when an object that was not previously in the fence has entered and exited the area.

These can be used when establishing a geofence, to pre-filter responses. For instance, to limit responses to enter and exit detections:

> nearby fleet fence detect enter,exit point 33.462 -112.268 6000

Pub/sub channels

Tile38 supports delivering geofence notications over pub/sub channels.

To create a static geofence that sends notifications when a bus is within 200 meters of a point and sends to the busstop channel:

> setchan busstop nearby buses fence point 33.5123 -112.2693 200

Subscribe on the busstop channel:

> subscribe busstop

Object types

All object types except for XYZ Tiles and QuadKeys can be stored in a collection. XYZ Tiles and QuadKeys are reserved for the SEARCH keyword only.

Lat/lon point

The most basic object type is a point that is composed of a latitude and a longitude. There is an optional z member that may be used for auxiliary data such as elevation or a timestamp.

set fleet truck1 point 33.5123 -112.2693     # plain lat/lon
set fleet truck1 point 33.5123 -112.2693 225 # lat/lon with z member

Bounding box

A bounding box consists of two points. The first being the southwestern most point and the second is the northeastern most point.

set fleet truck1 bounds 30 -110 40 -100

Geohash

A geohash is a string representation of a point. With the length of the string indicating the precision of the point.

set fleet truck1 hash 9tbnthxzr # this would be equivalent to 'point 33.5123 -112.2693'

GeoJSON

GeoJSON is an industry standard format for representing a variety of object types including a point, multipoint, linestring, multilinestring, polygon, multipolygon, geometrycollection, feature, and featurecollection.

* All ignored members will not persist.

Important to note that all coordinates are in Longitude, Latitude order.

set city tempe object {"type":"Polygon","coordinates":[[[0,0],[10,10],[10,0],[0,0]]]}

XYZ Tile

An XYZ tile is rectangle bounding area on earth that is represented by an X, Y coordinate and a Z (zoom) level. Check out maptiler.org for an interactive example.

QuadKey

A QuadKey used the same coordinate system as an XYZ tile except that the string representation is a string characters composed of 0, 1, 2, or 3. For a detailed explanation checkout The Bing Maps Tile System.

Network protocols

It's recommended to use a client library or the Tile38 CLI, but there are times when only HTTP is available or when you need to test from a remote terminal. In those cases we provide an HTTP and telnet options.

HTTP

One of the simplest ways to call a tile38 command is to use HTTP. From the command line you can use curl. For example:

# call with request in the body
curl --data "set fleet truck3 point 33.4762 -112.10923" localhost:9851

# call with request in the url path
curl localhost:9851/set+fleet+truck3+point+33.4762+-112.10923

Websockets

Websockets can be used when you need to Geofence and keep the connection alive. It works just like the HTTP example above, with the exception that the connection stays alive and the data is sent from the server as text websocket messages.

Telnet

There is the option to use a plain telnet connection. The default output through telnet is RESP.

telnet localhost 9851
set fleet truck3 point 33.4762 -112.10923
+OK

The server will respond in JSON or RESP depending on which protocol is used when initiating the first command.

  • HTTP and Websockets use JSON.
  • Telnet and RESP clients use RESP.

Client Libraries

Tile38 uses the Redis RESP protocol natively. Therefore most clients that support basic Redis commands will in turn support Tile38. Below are a few of the popular clients.

Contact

Josh Baker @tidwall

License

Tile38 source code is available under the MIT License.

Comments
  • Does Set Point Increase the RAM

    Does Set Point Increase the RAM

    I have been constantly ingesting Set Points close to 10K/sec . Need to know does Set point keeps the trail of all the points in RAM or it just over writes a Set Point for an ID e.g

    	redis.NewStringCmd("set", "fleetX", locData.iD,
    						"field", "eventTime", locData.evnDtime,
    						"point", locData.laT, locData.loN)
    

    Does there will be a single entry of a locData.iD or it will keep trail of all the points that i injest in fleetX against an ID ?

  • Are there any benchmarks ?

    Are there any benchmarks ?

    Hello,

    this server is a piece of great work. I like it very much. Do you have any benchmarks or metric numbers we can expect? How many points / fences / notifications can be handled by one instance?

    Thank you fro this great piece of software !

    Maciej Bednarz, Hannover, Germany

  • Question: Reloading a large AOF file (and running out of memory)

    Question: Reloading a large AOF file (and running out of memory)

    This is a by-product of issue #70

    1. We have an AOF file (produced by a version 1.5.1 instance of Tile38) that is ~17GB on disk and, when its all said and done, consumes about 24GB RAM on a 32GB machine. This is an AOF that has not been SHRINK -ed (so maybe that's the root problem?)
    2. After backporting to version 1.4.2 we spun up Tile38 and watched it import the AOF file eventually running out of memory.
    3. After upgrading to version 1.5.2 we spun up Tile38 and watched it import the AOF file eventually running out of memory, again.

    Is this a known-known and if so are there any best practices for mitigating against running out of memory when starting up a Tile38 server? Is there a relationship between the size of the AOF file on disk and memory requirements for both startup and general operations?

    (As I write this simply re-feeding the index is not a big deal.)

    Cheers,

    screen shot 2016-10-20 at 18 29 31

  • NEARBY appears to not sort correctly

    NEARBY appears to not sort correctly

    I have this simple test case, using version 1.9.0:

    DROP test
    SET test 1 OBJECT '{"type":"Point","coordinates":[-149.831200065,61.1181799835]}'
    SET test 2 OBJECT '{"type":"Point","coordinates":[-149.831220014,61.1185399862]}'
    SET test 3 OBJECT '{"type":"Point","coordinates":[-149.832450058,61.1169899628]}'
    SET test 4 OBJECT '{"type":"Point","coordinates":[-149.832280073,61.1170899589]}'
    NEARBY test LIMIT 3 DISTANCE POINT 61.1177399755 -149.831590075
    

    this returns:

    {
       "elapsed" : "65.708µs",
       "count" : 3,
       "ok" : true,
       "objects" : [
          {
             "distance" : 53.22,
             "object" : {
                "type" : "Point",
                "coordinates" : [
                   -149.831200065,
                   61.1181799835
                ]
             },
             "id" : "1"
          },
          {
             "distance" : 91.15,
             "object" : {
                "type" : "Point",
                "coordinates" : [
                   -149.831220014,
                   61.1185399862
                ]
             },
             "id" : "2"
          },
          {
             "id" : "4",
             "distance" : 81.23,
             "object" : {
                "coordinates" : [
                   -149.832280073,
                   61.1170899589
                ],
                "type" : "Point"
             }
          }
       ],
       "cursor" : 0
    }
    

    I was expecting to get the points in the nearest-first order, according to the docs. Instead I get results that are 53, 91, and 81 meters away from the center of the search. Is this a bug, or am I missing something obvious here? Thanks in advance for any help!

    Alex

  • How to make better use of TILE38 in this business?

    How to make better use of TILE38 in this business?

    There is a scene in the business, the use of real-time GPS data of the device to simulate multiple real-time access to the fence, the message can be triggered at each access(out/in).

    GPS data generated by the terminal about tens of thousands, each terminal every 10 seconds to produce a GPS message; and the fence is relatively static definition(Hundreds of thousands of).

    How to use TILE38 to achieve such a scene more concise it?

    Thanks!

  • Add example code for each major client library

    Add example code for each major client library

    We should have example code documented for each recommended client library

    At the moment only the node_redis library has an example of how to use the client library.

    What to do for each client library

    • Create an example code wiki page that executes the following commands:
    SET fleet truck1 POINT 33 -115
    GET fleet truck1
    NEARBY fleet POINT 33 -115 1000
    

    Use the node_redis example as a template.

    Recommended #100

  • NEARBY sort results by distance

    NEARBY sort results by distance

    Hey, I'm currently playing arround with tile38 and i found #79 and #119 which are both looking very interessting to me while i'm trying to find a point, which sits within the radius of n given points. At the moment i'm using n queries – one for each input point – and i'd like to return the closest point which was found in all subqueries. During the iteration through the result lists, comparing ids and measuring distance to find the closest point i was wondering wheather i can get back ordered points (by distance) from a query instead of doing it manually afterwards.

    Besides from combining multiple queries sorting by distance could be useful in other scenarios as well. What do you think?

  • Memory leak

    Memory leak

    I have ~ 23K objects of multipolygon type, representing California boundies. Enter hook assigned for each poligon --> ~ 23K web hooks

    When I am starting point insertion with SET commands (2 points per second) I see that memory, consumed by tile38 server grows from initial 200MB to 700MB (and it will grow if I continue ingestion). The bad thing is that after I stop ingestion - memory is not freed.

    When I am restarting Tile38 - AOF is reloaded and initial memory is again 200MB

    p.s. working with master branch

  • Tile38 server memory usage seems to be increasing indefinitely

    Tile38 server memory usage seems to be increasing indefinitely

    We have quite a modest usage of a tile38 server and it's geofencing capabilities. We currently have 11 hooks, and about one or two thousand of data points at a time. Each data injected have a TTL of 10s.

    The issue is that the memory usage keep increasing while the number of data remain the same. After several days, we reach up to 3Go of ram ! While at the start, tile38 server uses about 300mB of ram. It may be a memory leak.

    gc gc server tile38 pprof pprof002

  • Redis Interface

    Redis Interface

    I didn't want to hijack the previous topic. So i decided to ask directly. Another user mentioned solely using the the redis interface and not using any webhooks. Is this possible with Geofences? I want to be able to get the entry,exit notifications without the need for hooks if possible. In my situation the Raspberry 3.14 is the device running Tile and should only worry about up coming Geofence locations and act based on entry/exit of each. Right now my code grabs the gps coords and updates Tile and when an entry is detected sends a mqtt webhook to a local broker on the 3.14 then makes a decision thereafter. But It would be nice to be able to sub to the notifications without the need for the broker. I know Redis has the PUBSUB feature but I did not see this listed in the commands for Tile. For exampe lf I setup Fences LOC1 - LOC500 and sub LOC* to get all notifications as each Geofence is entered/exited that would be great .

    Thanks and sorry for the long post

  • How to deal with a growing aof?

    How to deal with a growing aof?

    First of all, thanks for this awesome project! Have been using Tile38 quite a while to do some proof of concepts and i'm trying to push it into our prod system. I hope to be able to contribute to the Project soon.

    I've been encountering some memory issues. There are a couple SET commands every 10-20 seconds, but mostly on already existing objects with an expire of 30 minutes. There aren't many objects and no followers. We shrink the aof file every day. There used to be a couple of Geofences up, but we have removed them. The RAM usage is pretty high, but this grew gradually over time and is around 4gigs now, even after using aof shrink. Does the whole aof have to be in memory? Some tips how we can get that under control?

       _______ _______
      |       |       |
      |____   |   _   |   Tile38 1.11.0 (19f53a6) 64 bit (amd64/linux)
      |       |       |   Port: 9851, PID: 1
      |____   |   _   | 
      |       |       |   tile38.com
      |_______|_______| 
    2018/03/13 15:24:03 [INFO] Server started, Tile38 version 1.11.0, git 19f53a6
    2018/03/13 15:27:17 [INFO] AOF loaded 28176826 commands: 194.22s, 145073/s, 21 MB/s
    2018/03/13 15:27:17 [INFO] The server is now ready to accept connections on port 9851
    2018/03/14 16:30:49 [INFO] aof shrink ended 838.087958ms
    2018/03/14 17:00:01 [INFO] aof shrink ended 14.189253ms
    2018/03/14 17:05:15 [INFO] aof shrink ended 9.732037ms
    
    127.0.0.1:9851> keys *
    {"ok":true,"keys":["a","b","c","d","e","f","g"],"elapsed":"15.926µs"}
    127.0.0.1:9851> stats a
    {"ok":true,"stats":[{"in_memory_size":7722,"num_objects":198,"num_points":198,"num_strings":0}],"elapsed":"28.79µs"}
    127.0.0.1:9851> stats b
    {"ok":true,"stats":[{"in_memory_size":6176,"num_objects":193,"num_points":193,"num_strings":0}],"elapsed":"21.811µs"}
    127.0.0.1:9851> stats c
    {"ok":true,"stats":[{"in_memory_size":17291,"num_objects":630,"num_points":630,"num_strings":0}],"elapsed":"33.636µs"}
    127.0.0.1:9851> stats d
    {"ok":true,"stats":[{"in_memory_size":20416,"num_objects":638,"num_points":638,"num_strings":0}],"elapsed":"33.562µs"}
    127.0.0.1:9851> stats e
    {"ok":true,"stats":[{"in_memory_size":7683,"num_objects":197,"num_points":197,"num_strings":0}],"elapsed":"34.799µs"}
    127.0.0.1:9851> stats f
    {"ok":true,"stats":[{"in_memory_size":6336,"num_objects":198,"num_points":198,"num_strings":0}],"elapsed":"47.184µs"}
    127.0.0.1:9851> stats g
    {"ok":true,"stats":[{"in_memory_size":19,"num_objects":1,"num_points":1,"num_strings":0}],"elapsed":"61.989µs"}
    
    
    127.0.0.1:9851> server
    {"ok":true,"stats":{"aof_size":5746035,"avg_item_size":1430975,"heap_released":5379784704,"heap_size":2944947800,"http_transport":true,"id":"db0fb9da7f37e715df4fdcf9483dacc3","in_memory_size":65757,"max_heap_size":0,"mem_alloc":2944947800,"num_collections":7,"num_hooks":10,"num_objects":2058,"num_points":2058,"num_strings":0,"pid":1,"pointer_size":8,"read_only":false},"elapsed":"181.097µs"}
    127.0.0.1:9851> pdelhook *
    {"ok":true,"elapsed":"21.634µs"}
    127.0.0.1:9851> aofshrink
    {"ok":true,"elapsed":"339ns"}
    127.0.0.1:9851> server
    {"ok":true,"stats":{"aof_size":412590,"avg_item_size":1474227,"heap_released":4385636352,"heap_size":3014795976,"http_transport":true,"id":"db0fb9da7f37e715df4fdcf9483dacc3","in_memory_size":65320,"max_heap_size":0,"mem_alloc":3014795976,"num_collections":7,"num_hooks":0,"num_objects":2045,"num_points":2045,"num_strings":0,"pid":1,"pointer_size":8,"read_only":false},"elapsed":"147.98µs"}
    127.0.0.1:9851> gc
    {"ok":true,"elapsed":"519ns"}
    127.0.0.1:9851> server
    {"ok":true,"stats":{"aof_size":1142900,"avg_item_size":1387913,"heap_released":5578768384,"heap_size":2864653016,"http_transport":true,"id":"db0fb9da7f37e715df4fdcf9483dacc3","in_memory_size":65981,"max_heap_size":0,"mem_alloc":2864653016,"num_collections":7,"num_hooks":0,"num_objects":2064,"num_points":2064,"num_strings":0,"pid":1,"pointer_size":8,"read_only":false},"elapsed":"142.264µs"}
    127.0.0.1:9851> aofshrink
    {"ok":true,"elapsed":"234ns"}
    127.0.0.1:9851> server
    {"ok":true,"stats":{"aof_size":514319,"avg_item_size":1389086,"heap_released":4408262656,"heap_size":2858740512,"http_transport":true,"id":"db0fb9da7f37e715df4fdcf9483dacc3","in_memory_size":65761,"max_heap_size":0,"mem_alloc":2858740512,"num_collections":7,"num_hooks":0,"num_objects":2058,"num_points":2058,"num_strings":0,"pid":1,"pointer_size":8,"read_only":false},"elapsed":"149.717µs"}
    
  • memory leak in azure eventhub endpoint

    memory leak in azure eventhub endpoint

    connection should be reused instead of creating a new connection in every send causing

    • usage of memory from a few mb to gb for the same workload
    • eventually hitting the connection limit of eventhub even for a small workload

    i have verifed with below changes

    type EvenHubConn struct {
    	mu   sync.Mutex
    	ep   Endpoint
    	conn *eventhub.Hub
    }
    
    func (conn *EvenHubConn) close() {
    	if conn.conn != nil {
    		conn.conn.Close(context.Background())
    		conn.conn = nil
    	}
    }
    
    // Send sends a message
    func (conn *EvenHubConn) Send(msg string) error {
    	conn.mu.Lock()
    	defer conn.mu.Unlock()
    
    	if conn.conn == nil {
    		hub, err := eventhub.NewHubFromConnectionString(conn.ep.EventHub.ConnectionString)
    
    		if err != nil {
    			return err
    		}
    		conn.conn = hub
    	}
    	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
    	defer cancel()
    
    	// parse json again to get out info for our kafka key
    	key := gjson.Get(msg, "key")
    	id := gjson.Get(msg, "id")
    	keyValue := fmt.Sprintf("%s-%s", key.String(), id.String())
    
    	evtHubMsg := eventhub.NewEventFromString(msg)
    	evtHubMsg.PartitionKey = &keyValue
    	err := conn.conn.Send(ctx, evtHubMsg)
    	if err != nil {
    		conn.close()
    		return err
    	}
    
    	return nil
    }
    
    
  • Fatal error on running tile38-server after installation

    Fatal error on running tile38-server after installation

    Describe the bug Upgraded from 1.29.0 to 1.30.0 on MacOS. on running tile-server command it immediately crashes with error: 2022/12/03 20:07:28 [FATA] invalid argument ''

    To Reproduce Steps to reproduce the behavior:

    1. Install tile38 with:

    brew install tile38

    1. run command tile38-server

    Expected behavior Tile server should start and keep running.

    Logs


    |_ || || | . | Tile38 1.30.0 (a09ff07c) 64 bit (amd64/darwin) | | | | | -| | . | Port: 9851, PID: 672 || ||||___|___| tile38.com

    2022/12/03 20:07:27 [INFO] Server started, Tile38 version 1.30.0, git a09ff07c 2022/12/03 20:07:27 [INFO] Ready to accept connections at [::]:9851 2022/12/03 20:07:27 [INFO] AOF loaded 2195 commands: 0.01s, 347112/s, 81 MB/s 2022/12/03 20:07:28 [FATA] invalid argument ''

    Operating System (please complete the following information):

    • OS: [Mac OS]
    • CPU: [Intel]
    • Version: [1.30.0]
    • Container: [None]

    Additional context Tried uninstalling tile38 completely and fresh reinstalling, still same behavior is observed.

  • nearby objects within a polygon

    nearby objects within a polygon

    I have been attempting to do a query where I am searching within a polygon for the closest N number of objects to a specific point.

    Currently, i have to do a within, and extend the search limit to a super high number, then run a custom functioning to find nearby, however this takes quite a long time.

    Thoughts on this? thanks in advance!

  • map marker clusters at scale

    map marker clusters at scale

    Is your feature request related to a problem? Please describe. Typical maps on mobile apps require clustering of markers due to the amount of points being displayed. A bunch of client-side libraries exist but scaling beyond ~50.000 points becomes a bottleneck when frequently loading, parsing, drawing markers updates from a single geojson source.

    Describe the solution you'd like While investigating, i came across this article: Serve Map Clusters 50x Faster Using Smarter Caching by @FlorianPfisterer. Caching for each zoom level seems to make sense...

    I also looked into mapbox/supercluster and its go-counterpart MadAppGang/gocluster which calculate cluster nodes efficiently. To say it with MadAppGang's words: 'The cluster use hierarchical greedy clustering approach. The same approach used by Dave Leaver with his fantastic Leaflet.markercluster plugin.'

    The benefit of integrating such functionality right into tile38 would be

    1. no more external servers, for example one running mapbox/supercluster
    2. performance! data is being processed right where it sits

    One solution that would work perfectly for my project could look like this...

    • a tile38 command to generate a point cluster for a given key; example:
      INTERSECTS key CLUSTER dest_key  ZOOMLEVEL 1 18 BOUNDS ... 
    

    with <dest_key> being generated by tile38 using some scheme, e.g. <dest_key>_{zoomlevel} => dest_key_1, dest_key_2, ... dest_key_18

    • my app adjusting marker layers to a specific dest_key depending on the zoom level of the map

    @tidwall is this a feature you would consider integrating into tile38?

  • Incorrect ordering of geofence events - Tile38 for historical analyses.

    Incorrect ordering of geofence events - Tile38 for historical analyses.

    Hi,

    I have a use case where I want to use my tile38 pipeline to also geofence historical data. In particular, there a about 100 geofences and 1 object that is moving in space with >100k locations a few seconds apart. When sending the locations sorted (by time) into tile38, then events are sent to a Kafka topic and polled by a consumer. When analysing the events in Kafka, they are not ordered in time anymore, thus entering and exiting a geofence are not logical.

    Reproduction Set up 100 geofences (non overlapping) with DETECT inside Send 10k + locations for same "agent" e.g. await tile38.set('agents', agent_id).fields({"ts":ts }).point(lat, lon).exec() SUBSCRIBE to geofences. Analyse "ts" of each event against the previous event -> they are not monotonously increasing

    When I add a time.sleep(0.001) between each SET, then tile38 seems to be completing the geofencing operations in time before the next SET du to the concurrent nature. However for the same agent I would expect them to happen in the same order as the SET operations.

    Operating System

    • OS: Mac OS
    • CPU: Apple M1
    • Version: Ventura
    • Container: Docker
  • Make the mock server available to other projects

    Make the mock server available to other projects

    Is your feature request related to a problem? Please describe. The project has its own mock server for running unit tests without an out-of-process server. Unfortunately it's not available as a package that can be used in other projects.

    Describe the solution you'd like Provide a mock server utility that can be used in other projects for running unit tests.

    Describe alternatives you've considered Currently the only alternative is to use redcon to manually fake Tile38 responses.

Go bindings for audio capture and playback with ALSA and libasound

Go ALSA bindings These bindings allow capture and playback of audio via ALSA using the alsa-lib library. Installation go get github.com/cocoonlife/goa

Nov 26, 2022
This is old and unmaintained code, ignore it. starfish is a simple, SDL based, 2D graphics and user input library for Go. If you intend to work on it, please fork from the 'devel' branch, not 'master'. Current release: 0.12.0

What is starfish? What starfish is: starfish is a simple 2D graphics and user input library for Go built on SDL. What starfish is not: While it is bui

Jun 4, 2019
darkroom - An image proxy with changeable storage backends and image processing engines with focus on speed and resiliency.
darkroom - An image proxy with changeable storage backends and image processing engines with focus on speed and resiliency.

Darkroom - Yet Another Image Proxy Introduction Darkroom combines the storage backend and the image processor and acts as an Image Proxy on your image

Dec 6, 2022
Go package captcha implements generation and verification of image and audio CAPTCHAs.
Go package captcha implements generation and verification of image and audio CAPTCHAs.

Package captcha ⚠️ Warning: this captcha can be broken by advanced OCR captcha breaking algorithms. import "github.com/dchest/captcha" Package captch

Dec 30, 2022
Fast and secure standalone server for resizing and converting remote images

imgproxy imgproxy is a fast and secure standalone server for resizing and converting remote images. The main principles of imgproxy are simplicity, sp

Jan 1, 2023
Publish Your GIS Data(Vector Data) to PostGIS and Geoserver
Publish Your GIS Data(Vector Data) to PostGIS and Geoserver

GISManager Publish Your GIS Data(Vector Data) to PostGIS and Geoserver How to install: go get -v github.com/hishamkaram/gismanager Usage: testdata fol

Sep 26, 2022
General purpose library for reading, writing and working with OpenStreetMap data

osm This package is a general purpose library for reading, writing and working with OpenStreetMap data in Go (golang). It has the ability to read OSM

Dec 30, 2022
Image processing library and rendering toolkit for Go.

blend Image processing library and rendering toolkit for Go. (WIP) Installation: This library is compatible with Go1. go get github.com/phrozen/blend

Nov 11, 2022
Struct for marshaling and unmarshaling glTF

glTF Struct for marshaling and unmarshaling glTF go get github.com/sturfeeinc/glTF/model It's autogenerated code from official work group's specs. Don

Apr 5, 2020
A lightning fast image processing and resizing library for Go

govips A lightning fast image processing and resizing library for Go This package wraps the core functionality of libvips image processing library by

Jan 8, 2023
Go bindings for libVLC and high-level media player interface
Go bindings for libVLC and high-level media player interface

Go bindings for libVLC 2.X/3.X/4.X and high-level media player interface. The package can be useful for adding multimedia capabilities to applications

Dec 31, 2022
Read and write Netpbm images from Go programs

netpbm Introduction netpbm is a package for the Go programming language that implements image decoders and encoders for the Netpbm image formats. The

Dec 29, 2022
A repository for plotting and visualizing data

Gonum Plot gonum/plot is the new, official fork of code.google.com/p/plotinum. It provides an API for building and drawing plots in Go. Note that this

Dec 27, 2022
A library to read, write, and transform Stereolithography (.stl) files in Go.

stl A library to read, write, and transform Stereolithography (.stl) files in Go. It is used in the command line STL manipulation tool stltool. Featur

Sep 26, 2022
ID3, MP4 and OGG/FLAC metadata parsing in Go

MP3/MP4/OGG/FLAC metadata parsing library This package provides MP3 (ID3v1,2.{2,3,4}) and MP4 (ACC, M4A, ALAC), OGG and FLAC metadata detection, parsi

Jan 7, 2023
Go package for decoding and encoding TARGA image format

tga tga is a Go package for decoding and encoding TARGA image format. It supports RLE and raw TARGA images with 8/15/16/24/32 bits per pixel, monochro

Sep 26, 2022
WebP decoder and encoder for Go (Zero Dependencies).
WebP decoder and encoder for Go (Zero Dependencies).

Go语言QQ群: 102319854, 1055927514 凹语言(凹读音“Wa”)(The Wa Programming Language): https://github.com/wa-lang/wa webp ██╗ ██╗███████╗██████╗ ██████╗ ██║

Dec 28, 2022
Go package for computer vision using OpenCV 4 and beyond.
Go package for computer vision using OpenCV 4 and beyond.

GoCV The GoCV package provides Go language bindings for the OpenCV 4 computer vision library. The GoCV package supports the latest releases of Go and

Jan 1, 2023
Storage and image processing server written in Go
Storage and image processing server written in Go

Mort An S3-compatible image processing server written in Go. Still in active development. Features HTTP server Resize, Rotate, SmartCrop Convert (JPEG

Jan 7, 2023