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

View File

@ -22,6 +22,7 @@
"drawille-canvas-blessed-contrib": "^0.1.3",
"keypress": "^0.2.1",
"pbf": "^3.0.0",
"rbush": "^2.0.1",
"sphericalmercator": "^1.0.5",
"term-mouse": "^0.1.1",
"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'
TermMouse = require 'term-mouse'
mercator = new (require('sphericalmercator'))()
LabelBuffer = require __dirname+'/src/LabelBuffer'
utils =
deg2rad: (angle) ->
@ -16,12 +17,10 @@ utils =
digits: (number, digits) ->
Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits)
metersPerPixel: (zoom, lat = 0) ->
utils.rad2deg(40075017*Math.cos(utils.deg2rad(lat))/Math.pow(2, zoom+8))
console.log utils.metersPerPixel(16, 180)
process.exit 0
class Termap
config:
@ -84,7 +83,7 @@ class Termap
lat: 49.019855
lng: 12.096956
zoom: 0
zoom: 2
view: [-400, -80]
scale: 4
@ -198,6 +197,9 @@ class Termap
scale = Math.pow 2, @zoom
drawn = []
labelBuffer = new LabelBuffer()
for layer in @config.drawOrder
continue unless @features?[layer]
@ -212,25 +214,30 @@ class Termap
visible = false
points = for point in points
p = [point.x/scale, point.y/scale]
if not visible and
p[0]+@view[0]>=4 and
p[0]+@view[0]<@width-4 and
p[1]+@view[1]>=0 and
p[1]+@view[1]<@height
if not visible and @_isOnScreen p
visible = true
p
continue unless visible
wasDrawn = false
switch feature.type
when "polygon", "line"
@canvas.beginPath()
@canvas.moveTo points.shift()...
@canvas.lineTo point... for point in points
@canvas.stroke()
wasDrawn = true
when "point"
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()
@ -239,6 +246,12 @@ class Termap
@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) ->
process.stdout.write text

BIN
tiles/europe.pbf.gz Normal file

Binary file not shown.