mirror of
https://github.com/rastapasta/mapscii.git
synced 2025-02-16 09:29:13 +01:00
🎭 more async refactoring in preperation for multitile support
This commit is contained in:
parent
da6c398e6d
commit
3895fd1fd3
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ node_modules
|
||||
bundle*
|
||||
*.log
|
||||
tmp
|
||||
mbtiles
|
||||
|
@ -26,6 +26,7 @@
|
||||
"earcut": "^2.1.1",
|
||||
"gl-matrix": "^2.3.2",
|
||||
"keypress": "^0.2.1",
|
||||
"mbtiles": "^0.9.0",
|
||||
"pbf": "^3.0.0",
|
||||
"rbush": "^2.0.1",
|
||||
"sphericalmercator": "^1.0.5",
|
||||
|
@ -16,6 +16,7 @@ Tile = require './Tile'
|
||||
utils = require './utils'
|
||||
|
||||
module.exports = class Renderer
|
||||
cache: {}
|
||||
config:
|
||||
baseZoom: 4
|
||||
fillPolygons: true
|
||||
@ -83,7 +84,7 @@ module.exports = class Renderer
|
||||
setSize: (@width, @height) ->
|
||||
@canvas = new Canvas @width, @height
|
||||
|
||||
draw: (@center, @zoom, @degree) ->
|
||||
draw: (center, zoom, rotation) ->
|
||||
return Promise.reject() if @isDrawing
|
||||
@isDrawing = true
|
||||
|
||||
@ -98,27 +99,75 @@ module.exports = class Renderer
|
||||
|
||||
# TODO: tiles = @_tilesInBBox @_getBBox()
|
||||
|
||||
z = Math.max 0, Math.floor @zoom
|
||||
xyz = tilebelt.pointToTileFraction @center.lon, @center.lat, z
|
||||
z = Math.max 0, Math.floor zoom
|
||||
xyz = tilebelt.pointToTileFraction center.lon, center.lat, z
|
||||
tile =
|
||||
size: tileSize
|
||||
x: Math.floor xyz[0]
|
||||
y: Math.floor xyz[1]
|
||||
z: z
|
||||
|
||||
tileSize = @config.tileSize / @_scaleAtZoom()
|
||||
tileSize = @config.tileSize / @_scaleAtZoom(zoom)
|
||||
position = [
|
||||
@width/2-(xyz[0]-Math.floor(xyz[0]))*tileSize
|
||||
@height/2-(xyz[1]-Math.floor(xyz[1]))*tileSize
|
||||
@width/2-(xyz[0]-tile.x)*tileSize
|
||||
@height/2-(xyz[1]-tile.y)*tileSize
|
||||
]
|
||||
|
||||
@_renderTile tile, position
|
||||
.then =>
|
||||
@_getTile tile
|
||||
.then (data) =>
|
||||
@_renderTile data, zoom, position
|
||||
@_writeFrame()
|
||||
|
||||
@isDrawing = false
|
||||
@lastDrawAt = Date.now()
|
||||
|
||||
_getTile: (tile) ->
|
||||
cacheKey = [tile.z, tile.x, tile.y].join "-"
|
||||
|
||||
# if data = @cache[cacheKey]
|
||||
# console.log cacheKey
|
||||
# console.log data
|
||||
# return Promise.resolve data
|
||||
|
||||
@tileSource
|
||||
.getTile tile.z, tile.x, tile.y
|
||||
.then (data) =>
|
||||
@cache[cacheKey] = data
|
||||
|
||||
_renderTile: (tile, zoom, position) ->
|
||||
@canvas.reset()
|
||||
@canvas.translate position[0], position[1]
|
||||
|
||||
scale = @_scaleAtZoom zoom
|
||||
|
||||
box =
|
||||
minX: -position[0]*scale
|
||||
minY: -position[1]*scale
|
||||
maxX: (@width-position[0])*scale
|
||||
maxY: (@height-position[1])*scale
|
||||
# console.log box
|
||||
# process.exit 0
|
||||
|
||||
for layer in @config.drawOrder
|
||||
if layer.indexOf(':') isnt -1
|
||||
[layer, filter] = layer.split /:/
|
||||
[filterField, filterValue] = filter.split /=/
|
||||
else
|
||||
filter = false
|
||||
|
||||
continue unless tile.layers?[layer]
|
||||
|
||||
if @config.layers[layer]?.minZoom and zoom > @config.layers[layer].minZoom
|
||||
continue
|
||||
|
||||
#features = tile.layers[layer].tree.search box
|
||||
|
||||
#@notify "rendering #{features.length} #{layer} features.."
|
||||
for feature in tile.layers[layer].features
|
||||
feature = data: feature
|
||||
if not filter or feature.data.properties[filterField] is filterValue
|
||||
@_drawFeature layer, feature, scale, zoom
|
||||
|
||||
_writeFrame: ->
|
||||
unless @lastDrawAt
|
||||
@_clearScreen()
|
||||
@ -129,7 +178,7 @@ module.exports = class Renderer
|
||||
featuresAt: (x, y) ->
|
||||
@labelBuffer.featuresAt x, y
|
||||
|
||||
_getBBox: (center = @center, zoom = @zoom) ->
|
||||
_getBBox: (center, zoom) ->
|
||||
[x, y] = utils.ll2xy center.lon, center.lat
|
||||
meterPerPixel = utils.metersPerPixel zoom, center.lat
|
||||
|
||||
@ -145,7 +194,7 @@ module.exports = class Renderer
|
||||
.inverse([west+1, south])
|
||||
.concat mercator.inverse([east-1, north])
|
||||
|
||||
_tilesInBBox: (bbox, zoom = @zoom) ->
|
||||
_tilesInBBox: (bbox, zoom) ->
|
||||
tiles = {}
|
||||
[tiles.minX, tiles.minY] = utils.ll2tile bbox[0], bbox[1], Math.floor zoom
|
||||
[tiles.maxX, tiles.maxY] = utils.ll2tile bbox[2], bbox[3], Math.floor zoom
|
||||
@ -157,49 +206,11 @@ module.exports = class Renderer
|
||||
_write: (output) ->
|
||||
@output.write output
|
||||
|
||||
_renderTile: (tile, position) ->
|
||||
@tileSource
|
||||
.getTile tile.z, tile.x, tile.y
|
||||
.then (tile) =>
|
||||
@canvas.reset()
|
||||
@canvas.translate position[0], position[1]
|
||||
|
||||
scale = @_scaleAtZoom()
|
||||
|
||||
box =
|
||||
minX: -position[0]*scale
|
||||
minY: -position[1]*scale
|
||||
maxX: (@width-position[0])*scale
|
||||
maxY: (@height-position[1])*scale
|
||||
# console.log box
|
||||
# process.exit 0
|
||||
|
||||
for layer in @config.drawOrder
|
||||
if layer.indexOf(':') isnt -1
|
||||
[layer, filter] = layer.split /:/
|
||||
[filterField, filterValue] = filter.split /=/
|
||||
else
|
||||
filter = false
|
||||
|
||||
continue unless tile?[layer]
|
||||
|
||||
if @config.layers[layer]?.minZoom and @zoom > @config.layers[layer].minZoom
|
||||
continue
|
||||
|
||||
features = tile[layer].tree.search box
|
||||
|
||||
@notify "rendering #{features.length} #{layer} features.."
|
||||
for feature in features
|
||||
if not filter or feature.data.properties[filterField] is filterValue
|
||||
@_drawFeature layer, feature, scale
|
||||
|
||||
#@draw @center, @zoom+.3, @degree
|
||||
|
||||
_scaleAtZoom: (zoom = @zoom) ->
|
||||
_scaleAtZoom: (zoom) ->
|
||||
baseZoom = Math.floor Math.max 0, zoom
|
||||
(@config.tileSize/@config.projectSize)/Math.pow(2, zoom-baseZoom)
|
||||
|
||||
_drawFeature: (layer, data, scale) ->
|
||||
_drawFeature: (layer, data, scale, zoom) ->
|
||||
feature = data.data
|
||||
|
||||
# TODO: this is ugly :) need to be fixed @style
|
||||
@ -207,7 +218,7 @@ module.exports = class Renderer
|
||||
feature.type = "LineString" if layer is "building" or layer is "road"
|
||||
|
||||
# TODO: zoom level
|
||||
unless style = @styler.getStyleFor layer, feature, 19-@zoom
|
||||
unless style = @styler.getStyleFor layer, feature, 19-zoom
|
||||
return false
|
||||
|
||||
toDraw = (@_scaleAndReduce points, scale for points in feature.points)
|
||||
|
@ -19,7 +19,7 @@ module.exports = class Termap
|
||||
input: process.stdin
|
||||
output: process.stdout
|
||||
|
||||
source: __dirname+"/../tiles/planet.z0-z8.mbtiles"
|
||||
source: __dirname+"/../mbtiles/regensburg.mbtiles"
|
||||
styleFile: __dirname+"/../styles/bright.json"
|
||||
zoomStep: 0.2
|
||||
|
||||
@ -33,15 +33,19 @@ module.exports = class Termap
|
||||
|
||||
tileSource: null
|
||||
|
||||
degree: 0
|
||||
center:
|
||||
#lat: 49.0189
|
||||
#lon: 12.0990
|
||||
lat: 54.133028
|
||||
lon: 10.609505
|
||||
|
||||
zoom: 0
|
||||
view: [0, 0]
|
||||
rotation: 0
|
||||
center:
|
||||
# sf
|
||||
# lat: 37.787946
|
||||
# lon: -122.407522
|
||||
# iceland
|
||||
# lat: 64.124229
|
||||
# lon: -21.811552
|
||||
# rgbg
|
||||
lat: 49.0189
|
||||
lon: 12.0990
|
||||
|
||||
|
||||
minZoom: null
|
||||
|
||||
@ -105,8 +109,9 @@ module.exports = class Termap
|
||||
|
||||
_onClick: (event) ->
|
||||
if @mouseDragging and event.button is "left"
|
||||
@view[0] -= (@mouseDragging.x-@mousePosition.x)<<1
|
||||
@view[1] -= (@mouseDragging.y-@mousePosition.y)<<2
|
||||
# TODO lat/lng based drag&drop
|
||||
# @view[0] -= (@mouseDragging.x-@mousePosition.x)<<1
|
||||
# @view[1] -= (@mouseDragging.y-@mousePosition.y)<<2
|
||||
@_draw()
|
||||
|
||||
@mouseDragging = false
|
||||
@ -123,8 +128,9 @@ module.exports = class Termap
|
||||
# start dragging
|
||||
if event.button is "left"
|
||||
if @mouseDragging
|
||||
@view[0] -= (@mouseDragging.x-event.x)<<1
|
||||
@view[1] -= (@mouseDragging.y-event.y)<<2
|
||||
# TODO lat/lng based drag&drop
|
||||
# @view[0] -= (@mouseDragging.x-event.x)<<1
|
||||
# @view[1] -= (@mouseDragging.y-event.y)<<2
|
||||
|
||||
if not @renderer.isDrawing and @renderer.lastDrawAt < Date.now()-100
|
||||
@_draw()
|
||||
@ -146,8 +152,8 @@ module.exports = class Termap
|
||||
when "a" then @zoomBy @config.zoomStep
|
||||
when "z" then @zoomBy -@config.zoomStep
|
||||
|
||||
when "k" then @degree += 15
|
||||
when "l" then @degree -= 15
|
||||
when "k" then @rotation += 15
|
||||
when "l" then @rotation -= 15
|
||||
|
||||
when "left" then @center.lon -= 1
|
||||
when "right" then @center.lon += 1
|
||||
@ -165,7 +171,7 @@ module.exports = class Termap
|
||||
|
||||
_draw: ->
|
||||
@renderer
|
||||
.draw @center, @zoom, @degree
|
||||
.draw @center, @zoom, @rotation
|
||||
.then =>
|
||||
@renderer.notify @_getFooter()
|
||||
|
||||
|
@ -7,32 +7,41 @@
|
||||
|
||||
VectorTile = require('vector-tile').VectorTile
|
||||
Protobuf = require 'pbf'
|
||||
Promise = require 'bluebird'
|
||||
zlib = require 'zlib'
|
||||
Rbush = require 'rbush'
|
||||
rbush = require 'rbush'
|
||||
|
||||
module.exports = class Tile
|
||||
tree: null
|
||||
class Tile
|
||||
layers: {}
|
||||
|
||||
constructor: (buffer, @styler = null) ->
|
||||
@tree = new Rbush()
|
||||
@tile = @_loadTile buffer
|
||||
|
||||
@_loadFeatures()
|
||||
load: (buffer) ->
|
||||
@_unzipIfNeeded buffer
|
||||
.then (data) => @_loadTile data
|
||||
.then (tile) => @_loadLayers tile
|
||||
.then => this
|
||||
|
||||
_loadTile: (buffer) ->
|
||||
buffer = zlib.gunzipSync buffer if @_isGzipped buffer
|
||||
new VectorTile new Protobuf buffer
|
||||
@tile = new VectorTile new Protobuf buffer
|
||||
|
||||
_unzipIfNeeded: (buffer) ->
|
||||
new Promise (resolve, reject) =>
|
||||
if @_isGzipped buffer
|
||||
zlib.gunzip buffer, (err, data) ->
|
||||
return reject err if err
|
||||
resolve data
|
||||
else
|
||||
resolve buffer
|
||||
|
||||
_isGzipped: (buffer) ->
|
||||
buffer.slice(0,2).indexOf(Buffer.from([0x1f, 0x8b])) is 0
|
||||
|
||||
_loadFeatures: ->
|
||||
for name, layer of @tile.layers
|
||||
tree = new Rbush()
|
||||
_loadLayers: (tile) ->
|
||||
layers = {}
|
||||
for name, layer of tile.layers
|
||||
tree = rbush()
|
||||
features = for i in [0...layer.length]
|
||||
# TODO: caching of similar attributes to avoid looking up the style each time
|
||||
continue if @styler and not @styler.getStyleFor layer, feature
|
||||
#continue if @styler and not @styler.getStyleFor layer, feature
|
||||
|
||||
feature = layer.feature i
|
||||
|
||||
@ -48,7 +57,9 @@ module.exports = class Tile
|
||||
@_addToTree tree, data
|
||||
data
|
||||
|
||||
@layers[name] = tree: tree, features: features
|
||||
layers[name] = tree: tree, features: features
|
||||
|
||||
@layers = layers
|
||||
|
||||
_addToTree: (tree, data) ->
|
||||
[minX, maxX, minY, maxY] = [Infinity, -Infinity, Infinity, -Infinity]
|
||||
@ -65,3 +76,5 @@ module.exports = class Tile
|
||||
minY: minY
|
||||
maxY: maxY
|
||||
data: data
|
||||
|
||||
module.exports = Tile
|
||||
|
@ -13,12 +13,12 @@ MBTiles = require 'mbtiles'
|
||||
Tile = require './Tile'
|
||||
|
||||
module.exports = class TileSource
|
||||
cache: {}
|
||||
modes:
|
||||
MBTiles: 1
|
||||
VectorTile: 2
|
||||
|
||||
mode: null
|
||||
cache: {}
|
||||
|
||||
mbtiles: null
|
||||
|
||||
@ -32,8 +32,8 @@ module.exports = class TileSource
|
||||
loadMBtils: (source) ->
|
||||
new Promise (resolve, reject) =>
|
||||
new MBTiles source, (err, @mbtiles) =>
|
||||
return reject err if err
|
||||
resolve()
|
||||
if err then reject err
|
||||
else resolve()
|
||||
|
||||
getTile: (z, x, y) ->
|
||||
unless @mode
|
||||
@ -41,12 +41,16 @@ module.exports = class TileSource
|
||||
|
||||
z = Math.max 0, Math.floor z
|
||||
|
||||
cacheKey = [z, x, y].join "-"
|
||||
if cached = @cache[[z,x,y].join("-")]
|
||||
return Promise.resolve cached
|
||||
|
||||
return if cached = @cache[cacheKey]
|
||||
Promise.resolve cached
|
||||
else if @mode is @modes.MBTiles
|
||||
new Promise (resolve, reject) =>
|
||||
@mbtiles.getTile z, x, y, (err, tileData) =>
|
||||
return reject err if err
|
||||
resolve @cache[cacheKey] = new Tile tileData
|
||||
if @mode is @modes.MBTiles
|
||||
@_getMBTile z, x, y
|
||||
|
||||
_getMBTile: (z, x, y) ->
|
||||
new Promise (resolve, reject) =>
|
||||
@mbtiles.getTile z, x, y, (err, tileData) =>
|
||||
return reject err if err
|
||||
|
||||
tile = @cache[[z,x,y].join("-")] = new Tile()
|
||||
resolve tile.load tileData
|
||||
|
Loading…
Reference in New Issue
Block a user