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