mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-21 23:53:08 +01:00
🍺 comitting current dev stage of various refactors
This commit is contained in:
parent
eb88d231a9
commit
da6c398e6d
7
main.js
7
main.js
@ -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();
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
52
src/TileSource.coffee
Normal 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
|
@ -48,6 +48,7 @@ utils =
|
||||
deg2rad: (angle) ->
|
||||
# (angle / 180) * Math.PI
|
||||
angle * 0.017453292519943295
|
||||
|
||||
rad2deg: (angle) ->
|
||||
angle / Math.PI * 180
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user