🍺 comitting current dev stage of various refactors

This commit is contained in:
Michael Straßburger 2016-11-02 13:19:46 +01:00
parent eb88d231a9
commit da6c398e6d
6 changed files with 186 additions and 87 deletions

View File

@ -15,9 +15,4 @@ const Termap = require(__dirname+'/src/Termap');
const Tile = require(__dirname+'/src/Tile') const Tile = require(__dirname+'/src/Tile')
termap = new Termap(); termap = new Termap();
termap.init();
// TODO: abstracing this class, create loader class
data = fs.readFileSync(__dirname+"/tiles/world.pbf.gz");
tile = new Tile(data);
termap.renderer.features = tile.layers
termap._draw();

View File

@ -20,6 +20,7 @@
"author": "Michael Straßburger <codepoet@cpan.org>", "author": "Michael Straßburger <codepoet@cpan.org>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bluebird": "^3.4.6",
"bresenham": "0.0.4", "bresenham": "0.0.4",
"coffee-script": "^1.10.0", "coffee-script": "^1.10.0",
"earcut": "^2.1.1", "earcut": "^2.1.1",

View File

@ -5,10 +5,14 @@
The Console Vector Tile renderer - bäm! The Console Vector Tile renderer - bäm!
### ###
x256 = require 'x256' x256 = require 'x256'
mercator = new (require('sphericalmercator'))()
tilebelt = require 'tilebelt'
MBTiles = require 'mbtiles'
Canvas = require './Canvas' Canvas = require './Canvas'
LabelBuffer = require './LabelBuffer' LabelBuffer = require './LabelBuffer'
Styler = require './Styler' Styler = require './Styler'
Tile = require './Tile'
utils = require './utils' utils = require './utils'
module.exports = class Renderer module.exports = class Renderer
@ -68,8 +72,9 @@ module.exports = class Renderer
lastDrawAt: 0 lastDrawAt: 0
labelBuffer: null labelBuffer: null
tileSource: null
constructor: (@output) -> constructor: (@output, @tileSource) ->
@labelBuffer = new LabelBuffer() @labelBuffer = new LabelBuffer()
loadStyleFile: (file) -> loadStyleFile: (file) ->
@ -78,8 +83,8 @@ module.exports = class Renderer
setSize: (@width, @height) -> setSize: (@width, @height) ->
@canvas = new Canvas @width, @height @canvas = new Canvas @width, @height
draw: (@view, @zoom, @degree) -> draw: (@center, @zoom, @degree) ->
return if @isDrawing return Promise.reject() if @isDrawing
@isDrawing = true @isDrawing = true
@notify "rendering..." @notify "rendering..."
@ -90,30 +95,85 @@ module.exports = class Renderer
@canvas.setBackground x256 utils.hex2rgb color @canvas.setBackground x256 utils.hex2rgb color
@canvas.clear() @canvas.clear()
@canvas.reset()
@canvas.translate @view[0], @view[1] # TODO: tiles = @_tilesInBBox @_getBBox()
@_renderLayers()
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()
position = [
@width/2-(xyz[0]-Math.floor(xyz[0]))*tileSize
@height/2-(xyz[1]-Math.floor(xyz[1]))*tileSize
]
@_renderTile tile, position
.then =>
@_writeFrame()
@isDrawing = false
@lastDrawAt = Date.now()
_writeFrame: ->
unless @lastDrawAt unless @lastDrawAt
@_clearScreen() @_clearScreen()
@output.write "\x1B[?6h" @output.write "\x1B[?6h"
@output.write @canvas.frame() @output.write @canvas.frame()
@isDrawing = false
@lastDrawAt = Date.now()
featuresAt: (x, y) -> featuresAt: (x, y) ->
@labelBuffer.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: -> _clearScreen: ->
@output.write "\x1B[2J" @output.write "\x1B[2J"
_write: (output) -> _write: (output) ->
@output.write output @output.write output
_renderLayers: -> _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 for layer in @config.drawOrder
if layer.indexOf(':') isnt -1 if layer.indexOf(':') isnt -1
[layer, filter] = layer.split /:/ [layer, filter] = layer.split /:/
@ -121,25 +181,24 @@ module.exports = class Renderer
else else
filter = false filter = false
continue unless @features?[layer] continue unless tile?[layer]
scale = (@config.tileSize/@config.projectSize)/Math.pow(2, @zoom)
if @config.layers[layer]?.minZoom and @zoom > @config.layers[layer].minZoom if @config.layers[layer]?.minZoom and @zoom > @config.layers[layer].minZoom
continue continue
box = features = tile[layer].tree.search box
minX: -@view[0]*scale
minY: -@view[1]*scale
maxX: (@width-@view[0])*scale
maxY: (@height-@view[1])*scale
features = @features[layer].tree.search box
@notify "rendering #{features.length} #{layer} features.." @notify "rendering #{features.length} #{layer} features.."
for feature in features for feature in features
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
#@draw @center, @zoom+.3, @degree
_scaleAtZoom: (zoom = @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) ->
feature = data.data feature = data.data

View File

@ -7,10 +7,11 @@
keypress = require 'keypress' keypress = require 'keypress'
TermMouse = require 'term-mouse' TermMouse = require 'term-mouse'
Promise = require 'bluebird'
mercator = new (require('sphericalmercator'))() mercator = new (require('sphericalmercator'))()
Renderer = require './Renderer' Renderer = require './Renderer'
TileSource = require './TileSource'
utils = require './utils' utils = require './utils'
module.exports = class Termap module.exports = class Termap
@ -18,6 +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"
styleFile: __dirname+"/../styles/bright.json" styleFile: __dirname+"/../styles/bright.json"
zoomStep: 0.2 zoomStep: 0.2
@ -29,12 +31,14 @@ module.exports = class Termap
mousePosition: [0, 0] mousePosition: [0, 0]
mouseDragging: false mouseDragging: false
tileSource: null
degree: 0 degree: 0
center: center:
lat: 49.0189 #lat: 49.0189
lon: 12.0990 #lon: 12.0990
#lat: 0 #26.7 lat: 54.133028
#lon: 0 #20.2 lon: 10.609505
zoom: 0 zoom: 0
view: [0, 0] view: [0, 0]
@ -44,11 +48,27 @@ module.exports = class Termap
constructor: (options) -> constructor: (options) ->
@config[key] = val for key, val of options @config[key] = val for key, val of options
init: ->
Promise
.resolve()
.then =>
@_initKeyboard() @_initKeyboard()
@_initMouse() @_initMouse()
console.log "loading tilesource"
@_initTileSource()
.then =>
console.log "loaded"
@_initRenderer() @_initRenderer()
.then =>
@_draw()
_initTileSource: ->
@tileSource = new TileSource()
@tileSource.init @config.source
_initKeyboard: -> _initKeyboard: ->
keypress @config.input keypress @config.input
@config.input.setRawMode true @config.input.setRawMode true
@ -65,7 +85,7 @@ module.exports = class Termap
@mouse.on 'move', (event) => @_onMouseMove event @mouse.on 'move', (event) => @_onMouseMove event
_initRenderer: -> _initRenderer: ->
@renderer = new Renderer @config.output @renderer = new Renderer @config.output, @tileSource
@renderer.loadStyleFile @config.styleFile @renderer.loadStyleFile @config.styleFile
@config.output.on 'resize', => @config.output.on 'resize', =>
@ -129,10 +149,10 @@ module.exports = class Termap
when "k" then @degree += 15 when "k" then @degree += 15
when "l" then @degree -= 15 when "l" then @degree -= 15
when "left" then @view[0] += 5 when "left" then @center.lon -= 1
when "right" then @view[0] -= 5 when "right" then @center.lon += 1
when "up" then @view[1]+= 5 when "up" then @center.lat += 1
when "down" then @view[1]-= 5 when "down" then @center.lat -= 1
else else
null null
@ -143,41 +163,12 @@ module.exports = class Termap
# display debug info for unhandled keys # display debug info for unhandled keys
@renderer.notify JSON.stringify key @renderer.notify JSON.stringify key
_draw: -> _draw: ->
@renderer.draw @view, @zoom, @degree @renderer
.draw @center, @zoom, @degree
.then =>
@renderer.notify @_getFooter() @renderer.notify @_getFooter()
_getTiles: ->
_getBBox: (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) ->
tile = utils.ll2tile bbox[0], bbox[1], Math.floor zoom
tiles =
minX: Math.max 0, tile[0]
minY: Math.max 0, tile[1]
tile = utils.ll2tile bbox[2], bbox[3], Math.floor zoom
tiles.maxX = Math.max 0, tile[0]
tiles.maxY = Math.max 0, tile[1]
tiles
_getFooter: -> _getFooter: ->
# features = @renderer.featuresAt @mousePosition.x-1-(@view[0]>>1), @mousePosition.y-1-(@view[1]>>2) # features = @renderer.featuresAt @mousePosition.x-1-(@view[0]>>1), @mousePosition.y-1-(@view[1]>>2)
# "features: ["+features.map((f) -> # "features: ["+features.map((f) ->
@ -188,11 +179,11 @@ module.exports = class Termap
# ).join(", ")+"] "+ # ).join(", ")+"] "+
# "#{@mousePosition.x} #{@mousePosition.y}" # "#{@mousePosition.x} #{@mousePosition.y}"
#"center: [#{utils.digits @center.lat, 2}, #{utils.digits @center.lng, 2}]}" #"center: [#{utils.digits @center.lat, 2}, #{utils.digits @center.lng, 2}]}"
bbox = @_getBBox() # bbox = @_getBBox()
# tiles = @_tilesInBBox(bbox)
"zoom: #{utils.digits @zoom, 2} "+ "zoom: #{utils.digits @zoom, 2} "
#{}"bbox: [#{bbox.map((z) -> utils.digits(z, 2)).join(', ')}]"+ #{}"bbox: [#{bbox.map((z) -> utils.digits(z, 2)).join(', ')}]"+
"tiles: "+(v for k,v of @_tilesInBBox(bbox) when typeof v is "number").join(",") # "tiles: "+("#{k}: #{v}" for k,v of @_tilesInBBox(bbox) when typeof v is "number").join(",")
#features.map((f) -> JSON.stringify f.feature.properties).join(" - ") #features.map((f) -> JSON.stringify f.feature.properties).join(" - ")

52
src/TileSource.coffee Normal file
View File

@ -0,0 +1,52 @@
###
termap - Terminal Map Viewer
by Michael Strassburger <codepoet@cpan.org>
Source for VectorTiles - supports
* remote TileServer
* local MBTiles and VectorTiles
###
Promise = require 'bluebird'
MBTiles = require 'mbtiles'
Tile = require './Tile'
module.exports = class TileSource
modes:
MBTiles: 1
VectorTile: 2
mode: null
cache: {}
mbtiles: null
init: (source) ->
if source.endsWith ".mbtiles"
@mode = @modes.MBTiles
@loadMBtils source
else
throw new Error "source type isn't supported yet"
loadMBtils: (source) ->
new Promise (resolve, reject) =>
new MBTiles source, (err, @mbtiles) =>
return reject err if err
resolve()
getTile: (z, x, y) ->
unless @mode
throw new Error "no TileSource defined"
z = Math.max 0, Math.floor z
cacheKey = [z, x, y].join "-"
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

View File

@ -48,6 +48,7 @@ utils =
deg2rad: (angle) -> deg2rad: (angle) ->
# (angle / 180) * Math.PI # (angle / 180) * Math.PI
angle * 0.017453292519943295 angle * 0.017453292519943295
rad2deg: (angle) -> rad2deg: (angle) ->
angle / Math.PI * 180 angle / Math.PI * 180