This example is a bit more complex than the previous example.
I started by downloading english-places-shape.zip from:
http://mapcruzin.com/free-england-arcgis-maps-shapefiles.htm.
This is a shapefile containing 21057 English places.
I imported the shapefile into a new empty spatialite database using SpatialiteGUI - each place now represented by a Point type column in the database.
As part of the import process i also created a spatial index on the Point type column - we'll see how such an index can improve database performance in the example project.
The aim of the B4A project was to use the B4A
HttpServer library to create a map tile server that runs on the android device.
The tile server's original task was to:
- Accept requests for map tiles over HTTP.
- Create a blank 256 pixel square Bitmap for each request received.
- Query the database for places that are within the bounds of the tile to be served.
- Draw each place from the query result on to the Bitmap.
- Return the Bitmap as a JPG or PNG image via HTTP.
Once i got that working i added a 'web service' to the tile server.
So as well as processing requests for map tiles, the tile server also:
- Accepts requests for JSON data.
- Each JSON request contains a latitude, longitude and zoom parameter.
The tile server queries the database for places near to the latitude, longitude position.
- The query result is converted from a List of Map objects to a string using the JSON library.
- Finally this string is returned via HTTP.
So in the example project i create a basic Google Map, and add a TileOverlay to it.
The TileOverlay is configured to request tiles over HTTP from my tile server running on the device.
The map displays the TileOverlay on top of the built in 'road map' map type.
(The tile server creates transparent PNG tiles - where no places are drawn on the tiles, the PNG is transparent).
Pan and zoom the map and the tile server receives and responds to requests for tiles.
Admittedly on a low power device this solution is not really practical, but on a reasonable spec device it all works pretty smoothly.
Tap the map and the tapped position's latitude and longitude coordinates along with the map zoom level are sent in an HTTP request to the tile server.
The tile server detects if the tap was a tap on one or more places and returns a JSON string that lists any places that were within the 'tap area'.
The example project then simply displays each place as a Toast message.
The source code for the TileServer service contains two versions of the query that gets places from the database to be drawn on a Tile.
One version makes no use of the spatial index created earlier, the other version makes use of the spatial index.
I started the app and let the map request the tiles from the tile server required to display the initial map view.
I did this twice - once with the query that doesn't use the spatial index and once with the query that does use the spatial index.
With no spatial index the tile rendering times were:
Places count: 37, tile creation took 1098 ticks
Places count: 35, tile creation took 622 ticks
Places count: 42, tile creation took 521 ticks
Places count: 13, tile creation took 684 ticks
Places count: 19, tile creation took 480 ticks
Places count: 28, tile creation took 606 ticks
Total rendering time: 4011 ticks
With the spatial index used, the tile rendering times were:
Places count: 13, tile creation took 204 ticks
Places count: 37, tile creation took 177 ticks
Places count: 28, tile creation took 125 ticks
Places count: 35, tile creation took 137 ticks
Places count: 19, tile creation took 180 ticks
Places count: 42, tile creation took 221 ticks
Total rendering time: 1044 ticks
Using the spatial index was nearly 4 times faster than not using the spatial index.
This is tested on a rather old HTC Desire S running a custom android 4.2.2 ROM - a 1GHz single core CPU.
In theory the tile server can accept more than once request at a time so i'd expect performance on multi core CPU devices to be far better - more cores can be in use at any one time.
On my Galaxy S3 the rendering times when using the spatial index were:
Places count: 13, tile creation took 136 ticks
Places count: 28, tile creation took 103 ticks
Places count: 21, tile creation took 128 ticks
Places count: 36, tile creation took 124 ticks
Places count: 42, tile creation took 84 ticks
Places count: 37, tile creation took 143 ticks
Performance on the S3 is very smooth - it's very useable.
So example project #1 demonstrates getting Polygons from a database that contain a Point - the Point being part of the query.
This example demonstrates getting Points from a database where the Points are within a Polygon - the Polygon being part of the query.
This project is 1.7MBs in size - the bulk of that being the spatialite database - that's too big to attach to a forum post so instead you can download it from:
http://b4a.martinpearman.co.uk/spatialite/, look for the archive 'TileServer-GoogleMaps.zip'.
Note that in order to compile this example you will need these additional libraries:
You will also need to choose a private signing key and update the manifest with your Google Maps API key.
I hope to create a similar example that uses OSMDroid instead of Google Maps - i'll add a new post to this thread when i have that working.
Martin.