🍺 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')
termap = new Termap();
// 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();
termap.init();

View File

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

View File

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

View File

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

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) ->
# (angle / 180) * Math.PI
angle * 0.017453292519943295
rad2deg: (angle) ->
angle / Math.PI * 180