mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-25 01:23:58 +01:00
Merge pull request #19 from rastapasta/stringwidth
Support for wide characters (Chinese/Japanese/...), cache, speed and general optimization
This commit is contained in:
commit
82c05d6932
18
README.md
18
README.md
@ -10,6 +10,8 @@ A node.js based [Vector Tile](http://wiki.openstreetmap.org/wiki/Vector_tiles) t
|
||||
$ telnet mapscii.me
|
||||
```
|
||||
|
||||
If you're on Windows, use the open source telnet client [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) to connect.
|
||||
|
||||
## Features
|
||||
|
||||
* Use your mouse to drag and zoom in and out!
|
||||
@ -106,8 +108,22 @@ If your terminal supports mouse events you can drag the map and use your scroll
|
||||
* [lukasmartinelli](https://github.com/lukasmartinelli) & [manuelroth](https://github.com/manuelroth) for all their work on [OSM2VectorTiles](https://github.com/osm2vectortiles) (global vector tiles from [OSM Planet](https://wiki.openstreetmap.org/wiki/Planet.osm))
|
||||
* [mourner](https://github.com/mourner) for all his work on mindblowing GIS algorithms (like the used [earcut](https://github.com/mapbox/earcut), [rbush](https://github.com/mourner/rbush), [simplify-js](https://github.com/mourner/simplify-js), ..)
|
||||
|
||||
## License
|
||||
## Licenses
|
||||
|
||||
### Map data
|
||||
|
||||
#### The Open Data Commons Open Database License (oDbl)
|
||||
|
||||
[OpenStreetMap](https://www.openstreetmap.org) is open data, licensed under the [Open Data Commons Open Database License](http://opendatacommons.org/licenses/odbl/) (ODbL) by the [OpenStreetMap Foundation](http://osmfoundation.org/) (OSMF).
|
||||
|
||||
You are free to copy, distribute, transmit and adapt our data, as long as you credit OpenStreetMap and its contributors. If you alter or build upon our data, you may distribute the result only under the same licence. The full [legal code](http://opendatacommons.org/licenses/odbl/1.0/) explains your rights and responsibilities.
|
||||
|
||||
The cartography in our map tiles, and our documentation, are licenced under the [Creative Commons Attribution-ShareAlike 2.0](http://creativecommons.org/licenses/by-sa/2.0/) licence (CC BY-SA).
|
||||
|
||||
### MapSCII
|
||||
|
||||
#### The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Michael Straßburger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
@ -38,6 +38,7 @@
|
||||
"pbf": "^3.0.0",
|
||||
"rbush": "^2.0.1",
|
||||
"simplify-js": "^1.2.1",
|
||||
"string-width": "^2.0.0",
|
||||
"term-mouse": "^0.1.1",
|
||||
"userhome": "^1.0.0",
|
||||
"vector-tile": "^1.3.0",
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
Will either be merged into node-drawille or become an own module at some point
|
||||
###
|
||||
stringWidth = require 'string-width'
|
||||
config = require './config'
|
||||
|
||||
module.exports = class BrailleBuffer
|
||||
characterMap: [[0x1, 0x8],[0x2, 0x10],[0x4, 0x20],[0x40, 0x80]]
|
||||
@ -78,20 +80,34 @@ module.exports = class BrailleBuffer
|
||||
frame: ->
|
||||
output = []
|
||||
currentColor = null
|
||||
delimeter = "\n"
|
||||
skip = 0
|
||||
|
||||
for idx in [0...@pixelBuffer.length]
|
||||
output.push delimeter if idx and (idx % (@width/2)) is 0
|
||||
for y in [0...@height/4]
|
||||
skip = 0
|
||||
|
||||
if currentColor isnt colorCode = @_termColor @foregroundBuffer[idx], @backgroundBuffer[idx]
|
||||
output.push currentColor = colorCode
|
||||
for x in [0...@width/2]
|
||||
idx = y*@width/2 + x
|
||||
|
||||
output.push if @charBuffer[idx]
|
||||
@charBuffer[idx]
|
||||
else
|
||||
String.fromCharCode 0x2800+@pixelBuffer[idx]
|
||||
if idx and not x
|
||||
output.push config.delimeter
|
||||
|
||||
output.push @termReset+delimeter
|
||||
if currentColor isnt colorCode = @_termColor @foregroundBuffer[idx], @backgroundBuffer[idx]
|
||||
output.push currentColor = colorCode
|
||||
|
||||
output.push if char = @charBuffer[idx]
|
||||
skip += stringWidth(char)-1
|
||||
if skip+x >= @width/2
|
||||
''
|
||||
else
|
||||
char
|
||||
else
|
||||
if not skip
|
||||
String.fromCharCode 0x2800+@pixelBuffer[idx]
|
||||
else
|
||||
skip--
|
||||
''
|
||||
|
||||
output.push @termReset+config.delimeter
|
||||
output.join ''
|
||||
|
||||
setChar: (char, x, y, color) ->
|
||||
|
@ -5,8 +5,8 @@
|
||||
Using 2D spatial indexing to avoid overlapping labels and markers
|
||||
and to find labels underneath a mouse cursor's position
|
||||
###
|
||||
|
||||
rbush = require 'rbush'
|
||||
stringWidth = require 'string-width'
|
||||
|
||||
module.exports = class LabelBuffer
|
||||
tree: null
|
||||
@ -41,5 +41,5 @@ module.exports = class LabelBuffer
|
||||
_calculateArea: (text, x, y, margin = 0) ->
|
||||
minX: x-margin
|
||||
minY: y-margin/2
|
||||
maxX: x+margin+text.length
|
||||
maxX: x+margin+stringWidth(text)
|
||||
maxY: y+margin/2
|
||||
|
@ -55,6 +55,7 @@ module.exports = class Mapscii
|
||||
|
||||
.then =>
|
||||
@_draw()
|
||||
.then => @notify("Welcome to MapSCII! Use your cursors to navigate, a/z to zoom, q to quit.")
|
||||
|
||||
_initTileSource: ->
|
||||
@tileSource = new TileSource()
|
||||
@ -62,7 +63,7 @@ module.exports = class Mapscii
|
||||
|
||||
_initKeyboard: ->
|
||||
keypress config.input
|
||||
config.input.setRawMode true
|
||||
config.input.setRawMode true if config.input.setRawMode
|
||||
config.input.resume()
|
||||
|
||||
config.input.on 'keypress', (ch, key) => @_onKey key
|
||||
@ -108,9 +109,11 @@ module.exports = class Mapscii
|
||||
|
||||
z = utils.baseZoom @zoom
|
||||
center = utils.ll2tile @center.lon, @center.lat, z
|
||||
@mousePosition = utils.tile2ll center.x+(dx/size), center.y+(dy/size), z
|
||||
|
||||
@mousePosition = utils.normalize utils.tile2ll center.x+(dx/size), center.y+(dy/size), z
|
||||
|
||||
_onClick: (event) ->
|
||||
return if event.x < 0 or event.x > @width/2 or event.y < 0 or event.y > @height/4
|
||||
@_updateMousePosition event
|
||||
|
||||
if @mouseDragging and event.button is "left"
|
||||
@ -127,6 +130,9 @@ module.exports = class Mapscii
|
||||
@_draw()
|
||||
|
||||
_onMouseMove: (event) ->
|
||||
return if event.x < 0 or event.x > @width/2 or event.y < 0 or event.y > @height/4
|
||||
return if config.mouseCallback and not config.mouseCallback event
|
||||
|
||||
# start dragging
|
||||
if event.button is "left"
|
||||
if @mouseDragging
|
||||
@ -153,16 +159,20 @@ module.exports = class Mapscii
|
||||
@notify @_getFooter()
|
||||
|
||||
_onKey: (key) ->
|
||||
if config.keyCallback and not config.keyCallback key
|
||||
return
|
||||
|
||||
# check if the pressed key is configured
|
||||
draw = switch key?.name
|
||||
when "q"
|
||||
process.exit 0
|
||||
|
||||
when "w" then @zoomy = 1
|
||||
when "s" then @zoomy = -1
|
||||
if config.quitCallback
|
||||
config.quitCallback()
|
||||
else
|
||||
process.exit 0
|
||||
|
||||
when "a" then @zoomBy config.zoomStep
|
||||
when "z" then @zoomBy -config.zoomStep
|
||||
when "z", "y"
|
||||
@zoomBy -config.zoomStep
|
||||
|
||||
when "left" then @moveBy 0, -8/Math.pow(2, @zoom)
|
||||
when "right" then @moveBy 0, 8/Math.pow(2, @zoom)
|
||||
@ -174,9 +184,6 @@ module.exports = class Mapscii
|
||||
|
||||
if draw isnt null
|
||||
@_draw()
|
||||
else
|
||||
# display debug info for unhandled keys
|
||||
@notify JSON.stringify key
|
||||
|
||||
_draw: ->
|
||||
@renderer
|
||||
@ -186,13 +193,6 @@ module.exports = class Mapscii
|
||||
@notify @_getFooter()
|
||||
.catch =>
|
||||
@notify "renderer is busy"
|
||||
.then =>
|
||||
if @zoomy
|
||||
if (@zoomy > 0 and @zoom < config.maxZoom) or (@zoomy < 0 and @zoom > @minZoom)
|
||||
@zoom += @zoomy * config.zoomStep
|
||||
else
|
||||
@zoomy *= -1
|
||||
setImmediate => @_draw()
|
||||
|
||||
_getFooter: ->
|
||||
# tile = utils.ll2tile @center.lon, @center.lat, @zoom
|
||||
@ -203,6 +203,7 @@ module.exports = class Mapscii
|
||||
"mouse: #{utils.digits @mousePosition.lat, 3}, #{utils.digits @mousePosition.lon, 3} "
|
||||
|
||||
notify: (text) ->
|
||||
config.onUpdate() if config.onUpdate
|
||||
@_write "\r\x1B[K"+text unless config.headless
|
||||
|
||||
_write: (output) ->
|
||||
@ -218,11 +219,4 @@ module.exports = class Mapscii
|
||||
@setCenter @center.lat+lat, @center.lon+lon
|
||||
|
||||
setCenter: (lat, lon) ->
|
||||
lon += 360 if lon < -180
|
||||
lon -= 360 if lon > 180
|
||||
|
||||
lat = 85.0511 if lat > 85.0511
|
||||
lat = -85.0511 if lat < -85.0511
|
||||
|
||||
@center.lat = lat
|
||||
@center.lon = lon
|
||||
@center = utils.normalize lon: lon, lat: lat
|
||||
|
@ -140,10 +140,7 @@ module.exports = class Renderer
|
||||
@_drawFeature tile, feature, layer.scale
|
||||
|
||||
labels.sort (a, b) ->
|
||||
if a.feature.properties.localrank
|
||||
a.feature.properties.localrank-b.feature.properties.localrank
|
||||
else
|
||||
a.feature.properties.scalerank-b.feature.properties.scalerank
|
||||
a.feature.sorty-b.feature.sort
|
||||
|
||||
for label in labels
|
||||
@_drawFeature label.tile, label.feature, label.scale
|
||||
@ -178,10 +175,8 @@ module.exports = class Renderer
|
||||
@canvas.polygon points, feature.color
|
||||
|
||||
when "symbol"
|
||||
text = feature.properties["name_"+config.language] or
|
||||
feature.properties["name_en"] or
|
||||
feature.properties["name"] or
|
||||
feature.properties.house_num or
|
||||
genericSymbol = null
|
||||
text = feature.label or
|
||||
genericSymbol = "◉"
|
||||
|
||||
return false if @_seen[text] and not genericSymbol
|
||||
|
@ -13,6 +13,7 @@ rbush = require 'rbush'
|
||||
x256 = require 'x256'
|
||||
earcut = require 'earcut'
|
||||
|
||||
config = require "./config"
|
||||
utils = require "./utils"
|
||||
|
||||
class Tile
|
||||
@ -74,22 +75,34 @@ class Tile
|
||||
# use feature.loadGeometry() again as soon as we got a 512 extent tileset
|
||||
geometries = feature.loadGeometry() #@_reduceGeometry feature, 8
|
||||
|
||||
sort = feature.properties.localrank or feature.properties.scalerank
|
||||
label = if style.type is "symbol"
|
||||
feature.properties["name_"+config.language] or
|
||||
feature.properties.name_en or
|
||||
feature.properties.name or
|
||||
feature.properties.house_num
|
||||
else
|
||||
undefined
|
||||
|
||||
if style.type is "fill"
|
||||
nodes.push @_addBoundaries true,
|
||||
id: feature.id
|
||||
# id: feature.id
|
||||
layer: name
|
||||
style: style
|
||||
properties: feature.properties
|
||||
label: label
|
||||
sort: sort
|
||||
points: geometries
|
||||
color: colorCode
|
||||
|
||||
else
|
||||
|
||||
for points in geometries
|
||||
nodes.push @_addBoundaries false,
|
||||
id: feature.id
|
||||
# id: feature.id
|
||||
layer: name
|
||||
style: style
|
||||
properties: feature.properties
|
||||
label: label
|
||||
sort: sort
|
||||
points: points
|
||||
color: colorCode
|
||||
|
||||
|
@ -25,6 +25,9 @@ catch
|
||||
|
||||
module.exports = class TileSource
|
||||
cache: {}
|
||||
cacheSize: 16
|
||||
cached: []
|
||||
|
||||
modes:
|
||||
MBTiles: 1
|
||||
VectorTile: 2
|
||||
@ -67,6 +70,10 @@ module.exports = class TileSource
|
||||
if cached = @cache[[z,x,y].join("-")]
|
||||
return Promise.resolve cached
|
||||
|
||||
if @cached.length > @cacheSize
|
||||
for tile in @cached.splice 0, Math.abs(@cacheSize-@cached.length)
|
||||
delete @cache[tile]
|
||||
|
||||
switch @mode
|
||||
when @modes.MBTiles then @_getMBTile z, x, y
|
||||
when @modes.HTTP then @_getHTTP z, x, y
|
||||
@ -93,7 +100,10 @@ module.exports = class TileSource
|
||||
resolve @_createTile z, x, y, buffer
|
||||
|
||||
_createTile: (z, x, y, buffer) ->
|
||||
tile = @cache[[z,x,y].join("-")] = new Tile @styler
|
||||
name = [z,x,y].join("-")
|
||||
@cached.push name
|
||||
|
||||
tile = @cache[name] = new Tile @styler
|
||||
tile.load buffer
|
||||
|
||||
_initPersistence: ->
|
||||
|
@ -33,3 +33,5 @@ module.exports =
|
||||
output: process.stdout
|
||||
|
||||
headless: false
|
||||
|
||||
delimeter: "\n\r"
|
@ -55,5 +55,13 @@ utils =
|
||||
digits: (number, digits) ->
|
||||
Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits)
|
||||
|
||||
normalize: (ll) ->
|
||||
ll.lon += 360 if ll.lon < -180
|
||||
ll.lon -= 360 if ll.lon > 180
|
||||
|
||||
ll.lat = 85.0511 if ll.lat > 85.0511
|
||||
ll.lat = -85.0511 if ll.lat < -85.0511
|
||||
|
||||
ll
|
||||
|
||||
module.exports = utils
|
||||
|
Loading…
Reference in New Issue
Block a user