🎨 refactoring

This commit is contained in:
Michael Straßburger 2016-09-18 17:46:14 +02:00
parent d3ddc23de4
commit e52ae425f6

View File

@ -4,205 +4,225 @@ Protobuf = require 'pbf'
keypress = require 'keypress' keypress = require 'keypress'
fs = require 'fs' fs = require 'fs'
zlib = require 'zlib' zlib = require 'zlib'
mouse = require('term-mouse')() TermMouse = require('term-mouse')
keypress process.stdin class Termap
process.stdin.setRawMode(true) config:
process.stdin.resume() drawOrder: ["admin", "water", "landuse", "building", "road", "poi_label"]
mouse.start() icons:
car: "🚗"
school: "🏫"
marker: ""
'art-gallery': "🎨"
attraction: ""
stadium: "🏈"
toilet: "🚽"
cafe: ""
laundry: "👚"
bus: "🚌"
restaurant: "🍛"
lodging: "🛏"
'fire-station': "🚒"
shop: "🛍"
pharmacy: "💊"
beer: "🍺"
cinema: "🎦"
width = null layers:
height = null poi_label:
color: "red"
road:
color: "white"
landuse:
color: "green"
water:
color: "blue"
admin:
color: "red"
building:
color: 8
config = mouse: null
drawOrder: ["admin", "water", "landuse", "building", "road", "poi_label"] width: null
height: null
canvas: null
icons: isDrawing: false
car: "🚗" lastDrawAt: 0
school: "🏫"
marker: ""
'art-gallery': "🎨"
attraction: ""
stadium: "🏈"
toilet: "🚽"
cafe: ""
laundry: "👚"
bus: "🚌"
restaurant: "🍕"
lodging: "🏨"
'fire-station': "🚒"
shop: "🏬"
pharmacy: "💊"
beer: "🍺"
cinema: "🎦"
layers: mousePosition: [0, 0]
poi_label: mouseDragging: false
color: "red"
road:
color: "white"
landuse:
color: "green"
water:
color: "blue"
admin:
color: "red"
building:
color: 8
canvas = null view: [-400, -80]
scale: 4
init = -> constructor: ->
width = Math.floor((process.stdout.columns-1)/2)*2*2 @_initControls()
height = Math.ceil(process.stdout.rows/4)*4*4 @_initCanvas()
canvas = new Canvas width, height
init()
features = {} @_onResize =>
data = fs.readFileSync __dirname+"/tiles/regensburg.pbf.gz" @_initCanvas()
zlib.gunzip data, (err, buffer) -> @_draw()
throw new Error err if err
tile = new VectorTile new Protobuf buffer _initControls: ->
keypress process.stdin
process.stdin.setRawMode true
process.stdin.resume()
# Load all layers and preparse the included geometries process.stdin.on 'keypress', (ch, key) => @_onKey key
for name,layer of tile.layers
if config.layers[name]
features[name] = []
for i in [0...layer.length] @mouse = TermMouse()
feature = layer.feature i @mouse.start()
features[name].push
type: [undefined, "point", "line", "polygon"][feature.type]
id: feature.id
properties: feature.properties
points: feature.loadGeometry()
draw() @mouse.on 'click', (event) => @_onClick event
@mouse.on 'scroll', (event) => @_onMouseScroll event
@mouse.on 'move', (event) => @_onMouseMove event
view = [-400, -80] _initCanvas: ->
scale = 4 @width = Math.floor((process.stdout.columns-1)/2)*2*2
@height = Math.ceil(process.stdout.rows/4)*4*4
@canvas = new Canvas @width, @height
flush = -> _onResize: (cb) ->
process.stdout.write canvas._canvas.frame() process.stdout.on 'resize', cb
lastDraw = null _onClick: (event) ->
drawing = false if @mouseDragging and event.button is "left"
draw = -> @view[0] -= (@mouseDragging.x-@mousePosition.x)*2
return if drawing @view[1] -= (@mouseDragging.y-@mousePosition.y)*4
lastDraw = Date.now() @_draw()
drawing = true
canvas.clearRect(0, 0, width, height)
canvas.save() @mouseDragging = false
canvas.translate view[0], view[1] _onMouseScroll: (event) ->
for layer in config.drawOrder # TODO: handle .x/y for directed zoom
continue unless features[layer] @zoomBy .5 * if event.button is "up" then 1 else -1
@_draw()
canvas.strokeStyle = canvas.fillStyle = config.layers[layer].color _onMouseMove: (event) ->
# only continue if x/y are valid
return unless event.x <= process.stdout.columns and event.y <= process.stdout.rows
for feature in features[layer] # start dragging
for points in feature.points if not @mouseDragging and event.button is "left"
@mouseDragging = x: event.x, y: event.y
visible = false # update internal mouse tracker
points = for point in points @mousePosition = x: event.x, y: event.y
p = [point.x/scale, point.y/scale]
if not visible and p[0]+view[0]>=4 and p[0]+view[0]<width-4 and p[1]+view[1]>=0 and p[1]+view[1]<height
visible = true
p
continue unless visible
switch feature.type _onKey: (key) ->
when "polygon", "line" # check if the pressed key is configured
canvas.beginPath() draw = switch key?.name
canvas.moveTo points.shift()... when "q"
canvas.lineTo point... for point in points process.exit 0
canvas.stroke()
when "point" when "a" then @zoomBy(.5)
canvas.fillText (config.icons[feature.properties.maki] or "X"), point... for point in points when "z" then @zoomBy(-.5)
when "left" then @view[0] += 5
when "right" then @view[0] -= 5
when "up" then @view[1]+= 5
when "down" then @view[1]-= 5
canvas.restore() else
false
flush()
process.stdout.write getStatus()
drawing = false
getStatus = ->
"TerMap up and running!"
notify = (text) ->
return if drawing
process.stdout.write "\r\x1B[K#{getStatus()} #{text}"
# moving = null
# process.stdin.on 'mousepress', (info) ->
# # TODO: file bug @keypress, fails after x>95 / sequence: '\u001b[M#B'
# if info.x > 2048
# info.x = 100
#
# if info.button is "left"
# moving = info
#
# else if moving and info.release
#
# draw()
zoomBy = (step) ->
return unless scale+step > 0
before = scale
scale += step
view[0] = view[0]*before/scale + if step > 0 then 8 else -8
view[1] = view[1]*before/scale + if step > 0 then 8 else -8
process.stdin.on 'keypress', (ch, key) ->
result = switch key?.name
when "q"
process.exit 0
when "a" then zoomBy(.5)
when "z" then zoomBy(-.5)
when "left" then view[0] += 5
when "right" then view[0] -= 5
when "up" then view[1]+= 5
when "down" then view[1]-= 5
if draw
@_draw()
else else
false # display debug info for unhandled keys
@notify JSON.stringify key
if result _parseTile: (buffer) ->
draw() # extract, decode and parse a given tile buffer
else new VectorTile new Protobuf zlib.gunzipSync data
notify JSON.stringify key
process.stdout.on 'resize', -> _getFeatures: (tile) ->
init() features = {}
draw() for name,layer of tile.layers
continue unless @config.layers[name]
moving = null features[name] = for i in [0...layer.length]
mousePosition = null feature = layer.feature i
mouse.on 'click', (event) -> type: [undefined, "point", "line", "polygon"][feature.type]
if moving and event.button is "left" id: feature.id
view[0] -= (moving.x-mousePosition.x)*2 properties: feature.properties
view[1] -= (moving.y-mousePosition.y)*4 points: feature.loadGeometry()
draw()
moving = null features
mouse.on 'scroll', (event) -> _draw: ->
# TODO: handle .x/y for directed zoom return if @isDrawing
zoomBy .5 * if event.button is "up" then 1 else -1 @isDrawing = true
draw() @lastDrawAt = Date.now()
mouse.on 'move', (event) -> @canvas.clearRect 0, 0, @width, @height
return unless event.x <= process.stdout.columns and event.y <= process.stdout.rows #@_write @canvas._canvas.frame()
if not moving and event.button is "left"
moving = x: event.x, y: event.y
mousePosition = x: event.x, y: event.y @canvas.save()
@canvas.translate @view[0], @view[1]
for layer in @config.drawOrder
continue unless @features?[layer]
@canvas.strokeStyle = @canvas.fillStyle = @config.layers[layer].color
for feature in @features[layer]
for points in feature.points
visible = false
points = for point in points
p = [point.x/@scale, point.y/@scale]
if not visible and
p[0]+@view[0]>=4 and
p[0]+@view[0]<@width-4 and
p[1]+@view[1]>=0 and
p[1]+@view[1]<@height
visible = true
p
continue unless visible
switch feature.type
when "polygon", "line"
@canvas.beginPath()
@canvas.moveTo points.shift()...
@canvas.lineTo point... for point in points
@canvas.stroke()
when "point"
@canvas.fillText (@config.icons[feature.properties.maki] or "X"), point... for point in points
@canvas.restore()
@_write @canvas._canvas.frame()
@_write @_getFooter()
@isDrawing = false
_write: (text) ->
process.stdout.write text
_getFooter: ->
"TerMap up and running!"
notify: ->
return if @isDrawing
@_write "\r\x1B[K#{@_getFooter()} #{text}"
zoomBy: (step) ->
return unless @scale+step > 0
before = @scale
@scale += step
@view[0] = @view[0]*before/@scale + if step > 0 then 8 else -8
@view[1] = @view[1]*before/@scale + if step > 0 then 8 else -8
termap = new Termap()
# TODO: abstracing this class, create loader class
data = fs.readFileSync __dirname+"/tiles/regensburg.pbf.gz"
termap.features = termap._getFeatures termap._parseTile data
termap._draw()