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"}
    
  • Missing

    Missing "fields" in version 1.30.2

    Describe the bug Missing fields when using JSET to update

    To Reproduce

    1. SET sup-gojek test POINT 106.66329051644803 10.769453594179312
    2. JSET sup-gojek test info dpv
    3. FSET sup-gojek test info-field 1
    4. GET sup-gojek test withfields -> {"ok":true,"object":{"type":"Point","coordinates":[10.769453594179312,106.66329051644803],"info":"dpv"},"fields":{"info-field":1},"elapsed":"15.554µs"}
    5. JSET sup-gojek test info dpvUpdated
    6. GET sup-gojek test withfields -> {"ok":true,"object":{"type":"Point","coordinates":[10.769453594179312,106.66329051644803],"info":"dpvUpdated"},"elapsed":"15.554µs"} Missing fields

    Expected behavior Same the old version (1.29.2), the fields should be returned

    Logs Version 1.30.2 Version 1.30.2

    Version 1.29.2 Version 1.29.2

    Operating System (please complete the following information):

    • OS: Linux
    • CPU: amd64
    • Version: 1.30.2
    • Container: Docker

    Additional context Add any other context about the problem here.

  • 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
BuntDB is an embeddable, in-memory key/value database for Go with custom indexing and geospatial support
BuntDB is an embeddable, in-memory key/value database for Go with custom indexing and geospatial support

BuntDB is a low-level, in-memory, key/value store in pure Go. It persists to disk, is ACID compliant, and uses locking for multiple readers and a sing

Dec 30, 2022
Scalable datastore for metrics, events, and real-time analytics

InfluxDB InfluxDB is an open source time series platform. This includes APIs for storing and querying data, processing it in the background for ETL or

Jan 5, 2023
Scalable datastore for metrics, events, and real-time analytics

InfluxDB InfluxDB is an open source time series platform. This includes APIs for storing and querying data, processing it in the background for ETL or

Jan 4, 2023
A GPU-powered real-time analytics storage and query engine.
A GPU-powered real-time analytics storage and query engine.

AresDB AresDB is a GPU-powered real-time analytics storage and query engine. It features low query latency, high data freshness and highly efficient i

Jan 7, 2023
The Prometheus monitoring system and time series database.

Prometheus Visit prometheus.io for the full documentation, examples and guides. Prometheus, a Cloud Native Computing Foundation project, is a systems

Dec 31, 2022
VictoriaMetrics: fast, cost-effective monitoring solution and time series database
VictoriaMetrics: fast, cost-effective monitoring solution and time series database

VictoriaMetrics VictoriaMetrics is a fast, cost-effective and scalable monitoring solution and time series database. It is available in binary release

Jan 8, 2023
LinDB is an open-source Time Series Database which provides high performance, high availability and horizontal scalability.
LinDB is an open-source Time Series Database which provides high performance, high availability and horizontal scalability.

LinDB is an open-source Time Series Database which provides high performance, high availability and horizontal scalability. LinDB stores all monitoring data of ELEME Inc, there is 88TB incremental writes per day and 2.7PB total raw data.

Jan 1, 2023
TalariaDB is a distributed, highly available, and low latency time-series database for Presto
TalariaDB is a distributed, highly available, and low latency time-series database for Presto

TalariaDB is a distributed, highly available, and low latency time-series database that stores real-time data. It's built on top of Badger DB.

Nov 16, 2022
Export output from pg_stat_activity and pg_stat_statements from Postgres into a time-series database that supports the Influx Line Protocol (ILP).

pgstat2ilp pgstat2ilp is a command-line program for exporting output from pg_stat_activity and pg_stat_statements (if the extension is installed/enabl

Dec 15, 2021
:handbag: Cache arbitrary data with an expiration time.

cache Cache arbitrary data with an expiration time. Features 0 dependencies About 100 lines of code 100% test coverage Usage // New cache c := cache.N

Jan 5, 2023
Time Series Database based on Cassandra with Prometheus remote read/write support

SquirrelDB SquirrelDB is a scalable high-available timeseries database (TSDB) compatible with Prometheus remote storage. SquirrelDB store data in Cass

Oct 20, 2022
🤔 A minimize Time Series Database, written from scratch as a learning project.
🤔 A minimize Time Series Database, written from scratch as a learning project.

mandodb ?? A minimize Time Series Database, written from scratch as a learning project. 时序数据库(TSDB: Time Series Database)大多数时候都是为了满足监控场景的需求,这里先介绍两个概念:

Jan 3, 2023
Nipo is a powerful, fast, multi-thread, clustered and in-memory key-value database, with ability to configure token and acl on commands and key-regexes written by GO

Welcome to NIPO Nipo is a powerful, fast, multi-thread, clustered and in-memory key-value database, with ability to configure token and acl on command

Dec 28, 2022
Owl is a db manager platform,committed to standardizing the data, index in the database and operations to the database, to avoid risks and failures.

Owl is a db manager platform,committed to standardizing the data, index in the database and operations to the database, to avoid risks and failures. capabilities which owl provides include Process approval、sql Audit、sql execute and execute as crontab、data backup and recover .

Nov 9, 2022
Being played at The Coffee House and try to find and play it on Spotify
Being played at The Coffee House and try to find and play it on Spotify

The Coffee House Muzik Follow the music that is being played at The Coffee House and try to find and play it on Spotify. Installation Clone this proje

May 25, 2022
Walrus - Fast, Secure and Reliable System Backup, Set up in Minutes.
Walrus - Fast, Secure and Reliable System Backup, Set up in Minutes.

Walrus is a fast, secure and reliable backup system suitable for modern infrastructure. With walrus, you can backup services like MySQL, PostgreSQL, Redis, etcd or a complete directory with a short interval and low overhead. It supports AWS S3, digitalocean spaces and any S3-compatible object storage service.

Jan 5, 2023
🔑A high performance Key/Value store written in Go with a predictable read/write performance and high throughput. Uses a Bitcask on-disk layout (LSM+WAL) similar to Riak.

bitcask A high performance Key/Value store written in Go with a predictable read/write performance and high throughput. Uses a Bitcask on-disk layout

Sep 26, 2022
Concurrency-safe Go caching library with expiration capabilities and access counters

cache2go Concurrency-safe golang caching library with expiration capabilities. Installation Make sure you have a working Go environment (Go 1.2 or hig

Dec 31, 2022
groupcache is a caching and cache-filling library, intended as a replacement for memcached in many cases.

groupcache Summary groupcache is a distributed caching and cache-filling library, intended as a replacement for a pool of memcached nodes in many case

Dec 29, 2022