🌲 using r-tree spatial indexing to declutter the labels

This commit is contained in:
Michael Straßburger 2016-09-20 20:52:49 +02:00
parent 8f3de4a159
commit 014eab2b70
5 changed files with 57 additions and 12 deletions

View File

@ -30,6 +30,7 @@ Discover the world in your console!
* [`node-mbtiles`](https://github.com/mapbox/node-mbtiles) for MBTiles parsing * [`node-mbtiles`](https://github.com/mapbox/node-mbtiles) for MBTiles parsing
* [`pbf`](https://github.com/mapbox/pbf) for Protobuf decoding * [`pbf`](https://github.com/mapbox/pbf) for Protobuf decoding
* [`vector-tile-js`](https://github.com/mapbox/vector-tile-js) for [VectorTile](https://github.com/mapbox/vector-tile-spec/tree/master/2.1) parsing * [`vector-tile-js`](https://github.com/mapbox/vector-tile-js) for [VectorTile](https://github.com/mapbox/vector-tile-spec/tree/master/2.1) parsing
* [`rbush`](https://github.com/mourner/rbush) for 2D spatial indexing
* [`sphericalmercator`](https://github.com/mapbox/node-sphericalmercator) for EPSG:3857 <> WGS84 conversions * [`sphericalmercator`](https://github.com/mapbox/node-sphericalmercator) for EPSG:3857 <> WGS84 conversions
## Wishlist ## Wishlist
@ -41,8 +42,11 @@ Discover the world in your console!
* [ ] mapping of view to tiles to show * [ ] mapping of view to tiles to show
* [ ] label drawing * [ ] label drawing
* [x] support for point labels * [x] support for point labels
* [ ] dynamic decluttering of labels * [x] dynamic decluttering of labels
* [ ] centering text labels
* [ ] lat/lng-center + zoom based viewport * [ ] lat/lng-center + zoom based viewport
* [ ] bbox awareness
* [ ] zoom -> scale calculation
* [ ] TileSource class (abstracting URL, mbtiles, single vector tile source) * [ ] TileSource class (abstracting URL, mbtiles, single vector tile source)
* [ ] tile request system * [ ] tile request system
* [ ] from local mbtiles * [ ] from local mbtiles

View File

@ -22,6 +22,7 @@
"drawille-canvas-blessed-contrib": "^0.1.3", "drawille-canvas-blessed-contrib": "^0.1.3",
"keypress": "^0.2.1", "keypress": "^0.2.1",
"pbf": "^3.0.0", "pbf": "^3.0.0",
"rbush": "^2.0.1",
"sphericalmercator": "^1.0.5", "sphericalmercator": "^1.0.5",
"term-mouse": "^0.1.1", "term-mouse": "^0.1.1",
"vector-tile": "^1.3.0" "vector-tile": "^1.3.0"

27
src/LabelBuffer.coffee Normal file
View File

@ -0,0 +1,27 @@
rbush = require 'rbush'
module.exports = class LabelBuffer
tree: null
margin: 1
constructor: (@width, @height) ->
@tree = rbush()
project: (x, y) ->
[Math.floor(x/2), Math.floor(y/4)]
writeIfPossible: (text, x, y) ->
point = @project x, y
return false unless @_hasSpace text, point[0], point[1]
@tree.insert @_calculateArea text, point[0], point[1]
true
_hasSpace: (text, x, y) ->
not @tree.collides @_calculateArea text, x, y
_calculateArea: (text, x, y) ->
minX: x-@margin
minY: y-@margin
maxX: x+@margin+text.length
maxY: y+@margin

View File

@ -6,6 +6,7 @@ fs = require 'fs'
zlib = require 'zlib' zlib = require 'zlib'
TermMouse = require 'term-mouse' TermMouse = require 'term-mouse'
mercator = new (require('sphericalmercator'))() mercator = new (require('sphericalmercator'))()
LabelBuffer = require __dirname+'/src/LabelBuffer'
utils = utils =
deg2rad: (angle) -> deg2rad: (angle) ->
@ -16,12 +17,10 @@ utils =
digits: (number, digits) -> digits: (number, digits) ->
Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits) Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits)
metersPerPixel: (zoom, lat = 0) -> metersPerPixel: (zoom, lat = 0) ->
utils.rad2deg(40075017*Math.cos(utils.deg2rad(lat))/Math.pow(2, zoom+8)) utils.rad2deg(40075017*Math.cos(utils.deg2rad(lat))/Math.pow(2, zoom+8))
console.log utils.metersPerPixel(16, 180)
process.exit 0
class Termap class Termap
config: config:
@ -84,7 +83,7 @@ class Termap
lat: 49.019855 lat: 49.019855
lng: 12.096956 lng: 12.096956
zoom: 0 zoom: 2
view: [-400, -80] view: [-400, -80]
scale: 4 scale: 4
@ -198,6 +197,9 @@ class Termap
scale = Math.pow 2, @zoom scale = Math.pow 2, @zoom
drawn = []
labelBuffer = new LabelBuffer()
for layer in @config.drawOrder for layer in @config.drawOrder
continue unless @features?[layer] continue unless @features?[layer]
@ -212,25 +214,30 @@ class Termap
visible = false visible = false
points = for point in points points = for point in points
p = [point.x/scale, point.y/scale] p = [point.x/scale, point.y/scale]
if not visible and if not visible and @_isOnScreen p
p[0]+@view[0]>=4 and
p[0]+@view[0]<@width-4 and
p[1]+@view[1]>=0 and
p[1]+@view[1]<@height
visible = true visible = true
p p
continue unless visible continue unless visible
wasDrawn = false
switch feature.type switch feature.type
when "polygon", "line" when "polygon", "line"
@canvas.beginPath() @canvas.beginPath()
@canvas.moveTo points.shift()... @canvas.moveTo points.shift()...
@canvas.lineTo point... for point in points @canvas.lineTo point... for point in points
@canvas.stroke() @canvas.stroke()
wasDrawn = true
when "point" when "point"
text = feature.properties.house_num or @config.icons[feature.properties.maki] or "" text = feature.properties.house_num or @config.icons[feature.properties.maki] or ""
@canvas.fillText text, point... for point in points
for point in points
if labelBuffer.writeIfPossible text, point...
@canvas.fillText text, point...
wasDrawn = true
if wasDrawn
drawn.push feature
@canvas.restore() @canvas.restore()
@ -239,6 +246,12 @@ class Termap
@isDrawing = false @isDrawing = false
_isOnScreen: (point) ->
point[0]+@view[0]>=4 and
point[0]+@view[0]<@width-4 and
point[1]+@view[1]>=0 and
point[1]+@view[1]<@height
_write: (text) -> _write: (text) ->
process.stdout.write text process.stdout.write text

BIN
tiles/europe.pbf.gz Normal file

Binary file not shown.