mirror of
https://github.com/rastapasta/mapscii.git
synced 2025-04-30 10:54:27 +02: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')
|
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();
|
|
||||||
|
@ -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",
|
||||||
|
@ -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,55 +95,109 @@ 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) ->
|
||||||
for layer in @config.drawOrder
|
@tileSource
|
||||||
if layer.indexOf(':') isnt -1
|
.getTile tile.z, tile.x, tile.y
|
||||||
[layer, filter] = layer.split /:/
|
.then (tile) =>
|
||||||
[filterField, filterValue] = filter.split /=/
|
@canvas.reset()
|
||||||
else
|
@canvas.translate position[0], position[1]
|
||||||
filter = false
|
|
||||||
|
|
||||||
continue unless @features?[layer]
|
scale = @_scaleAtZoom()
|
||||||
|
|
||||||
scale = (@config.tileSize/@config.projectSize)/Math.pow(2, @zoom)
|
|
||||||
|
|
||||||
if @config.layers[layer]?.minZoom and @zoom > @config.layers[layer].minZoom
|
|
||||||
continue
|
|
||||||
|
|
||||||
box =
|
box =
|
||||||
minX: -@view[0]*scale
|
minX: -position[0]*scale
|
||||||
minY: -@view[1]*scale
|
minY: -position[1]*scale
|
||||||
maxX: (@width-@view[0])*scale
|
maxX: (@width-position[0])*scale
|
||||||
maxY: (@height-@view[1])*scale
|
maxY: (@height-position[1])*scale
|
||||||
|
# console.log box
|
||||||
|
# process.exit 0
|
||||||
|
|
||||||
features = @features[layer].tree.search box
|
for layer in @config.drawOrder
|
||||||
@notify "rendering #{features.length} #{layer} features.."
|
if layer.indexOf(':') isnt -1
|
||||||
for feature in features
|
[layer, filter] = layer.split /:/
|
||||||
if not filter or feature.data.properties[filterField] is filterValue
|
[filterField, filterValue] = filter.split /=/
|
||||||
@_drawFeature layer, feature, scale
|
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) ->
|
_drawFeature: (layer, data, scale) ->
|
||||||
feature = data.data
|
feature = data.data
|
||||||
|
@ -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,10 +48,26 @@ module.exports = class Termap
|
|||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
@config[key] = val for key, val of options
|
@config[key] = val for key, val of options
|
||||||
|
|
||||||
@_initKeyboard()
|
init: ->
|
||||||
@_initMouse()
|
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: ->
|
_initKeyboard: ->
|
||||||
keypress @config.input
|
keypress @config.input
|
||||||
@ -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,40 +163,11 @@ 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
|
||||||
@renderer.notify @_getFooter()
|
.draw @center, @zoom, @degree
|
||||||
|
.then =>
|
||||||
_getTiles: ->
|
@renderer.notify @_getFooter()
|
||||||
|
|
||||||
_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)
|
||||||
@ -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
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) ->
|
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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user