mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-21 23:53:08 +01:00
🌲 using r-tree spatial indexing to declutter the labels
This commit is contained in:
parent
8f3de4a159
commit
014eab2b70
@ -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
|
||||
|
@ -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
27
src/LabelBuffer.coffee
Normal 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
|
@ -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
|
||||
visible = true
|
||||
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
BIN
tiles/europe.pbf.gz
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user