🚱 bringing polygon rendering back, todo: holes

This commit is contained in:
Michael Straßburger 2016-11-06 22:13:50 +01:00
parent cfd8e24342
commit e69321dfb6
6 changed files with 98 additions and 166 deletions

View File

@ -1,14 +1,15 @@
# MapSCII - the whole world in your console. # MapSCII - The Whole World In Your Console.
No web browser around? Don't worry - and discover the planet in your console! MapSCII is node.js based [Vector Tile](https://github.com/mapbox/vector-tile-spec) to [Braille](http://www.fileformat.info/info/unicode/block/braille_patterns/utf8test.htm) renderer for [xterm](https://en.wikipedia.org/wiki/Xterm)-compatible terminals.
<img src="http://i.imgur.com/yYVt7No.png" width="100%" /> <img src="http://i.imgur.com/yYVt7No.png" width="100%" />
* Discover the globe or zoom in to explore your neighbourhood * Discover the globe or zoom in to explore your neighbourhood
* See Point-of-Interest around any given location * See Point-of-Interest around any given location
* Highly customizable styling (reuse your [mapbox-gl-styles](https://github.com/mapbox/mapbox-gl-styles)) * Highly customizable layer styling with [Mapbox Styles](https://www.mapbox.com/mapbox-gl-style-spec/)
* Compatible with Linux and OSX terminals, Windows support via [PuTTY](http://www.putty.org/) * Compatible with Linux and OSX terminals, Windows support via [PuTTY](http://www.putty.org/)
* Use the default or your own map server - or work offline with VectorTile/MBTiles * Connect to any vector tile server - or just use my custom [OpenStreetMap](https://en.wikipedia.org/wiki/OpenStreetMap) based one
* Work offline and discover local VectorTile/MBTiles
* 100% pure Coffee-/JavaScript! :sunglasses: * 100% pure Coffee-/JavaScript! :sunglasses:
## How to install ## How to install
@ -123,7 +124,7 @@ If your terminal supports mouse events you can drag the map and use your scroll
* [x] filled polygons * [x] filled polygons
* [x] convert polygons to triangles * [x] convert polygons to triangles
* [x] use triangulation for filling * [x] use triangulation for filling
* [ ] respect fill/line style file based setting * [x] respect fill/line style file based setting
* Tile * Tile
* [x] directly throw away features that aren't covered by any style * [x] directly throw away features that aren't covered by any style

View File

@ -51,24 +51,18 @@ module.exports = class Canvas
xs = {} xs = {}
ys = {} ys = {}
#
# for points in polylines
# if vertices.length
# continue
# holes.push vertices.length/2
for points in polylines for point in polylines
if vertices.length vertices = vertices.concat point
continue
holes.push vertices.length/2
lastPoint = [-1, -1]
for point in points
vertices = vertices.concat point[0], point[1]
xs[point[0]] = ys[point[1]] = true xs[point[0]] = ys[point[1]] = true
# Check if we actually got a valid polygon after projection and clamping # Check if we actually got a valid polygon after projection and clamping
if Object.keys(xs).length is 1 or Object.keys(ys).length is 1 if Object.keys(xs).length is 1 or Object.keys(ys).length is 1
if vertices.length
# TODO: a line-hole - skip it for now
continue
else
# TODO: a line instead of a polygon - skip it for now
return false return false
try try

View File

@ -5,7 +5,6 @@
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' tilebelt = require 'tilebelt'
Promise = require 'bluebird' Promise = require 'bluebird'
@ -98,6 +97,7 @@ module.exports = class Renderer
@isDrawing = true @isDrawing = true
@labelBuffer.clear() @labelBuffer.clear()
@_seen = {}
if color = @styler.styleById['background']?.paint['background-color'] if color = @styler.styleById['background']?.paint['background-color']
@canvas.setBackground x256 utils.hex2rgb color @canvas.setBackground x256 utils.hex2rgb color
@ -173,6 +173,7 @@ module.exports = class Renderer
maxY: (@height-position.y)*scale maxY: (@height-position.y)*scale
features = {} features = {}
for layer in @config.drawOrder for layer in @config.drawOrder
continue unless tile.data.layers?[layer] continue unless tile.data.layers?[layer]
features[layer] = tile.data.layers[layer].search box features[layer] = tile.data.layers[layer].search box
@ -186,7 +187,6 @@ module.exports = class Renderer
for layer in @config.drawOrder for layer in @config.drawOrder
for tile in tiles for tile in tiles
continue unless tile.features[layer]?.length continue unless tile.features[layer]?.length
for feature in tile.features[layer] for feature in tile.features[layer]
# continue if feature.id and drawn[feature.id] # continue if feature.id and drawn[feature.id]
# drawn[feature.id] = true # drawn[feature.id] = true
@ -208,10 +208,12 @@ module.exports = class Renderer
@config.tileSize / @config.projectSize / Math.pow(2, zoom-baseZoom) @config.tileSize / @config.projectSize / Math.pow(2, zoom-baseZoom)
_drawFeature: (tile, feature) -> _drawFeature: (tile, feature) ->
return false if feature.style.minzoom and tile.zoom < feature.style.minzoom if feature.style.minzoom and tile.zoom < feature.style.minzoom
return false
toDraw = @_scaleAndReduce tile, feature points = @_scaleAndReduce tile, feature
return false unless toDraw.length unless points.length
return false
color = color =
feature.style.paint['line-color'] or feature.style.paint['line-color'] or
@ -227,12 +229,12 @@ module.exports = class Renderer
switch feature.style.type switch feature.style.type
when "line" when "line"
width = feature.style.paint['line-width']?.base*1.4 or 1 width = feature.style.paint['line-width']?.base*1.4 or 1
@canvas.polyline points, colorCode, width for points in toDraw @canvas.polyline points, colorCode, width
when "fill" when "fill"
@canvas.polygon toDraw, colorCode @canvas.polygon points, colorCode
when "symbol" when "symbola"
text = feature.properties["name_"+@config.language] or text = feature.properties["name_"+@config.language] or
feature.properties["name_en"] or feature.properties["name_en"] or
feature.properties["name"] or feature.properties["name"] or
@ -240,8 +242,6 @@ module.exports = class Renderer
#@config.icons[feature.properties.maki] or #@config.icons[feature.properties.maki] or
"" ""
# TODO: check in definition if points can actually own multiple geometries
for points in toDraw
for point in points for point in points
x = point[0] - text.length x = point[0] - text.length
margin = @config.layers[feature.layer]?.margin or @config.labelMargin margin = @config.layers[feature.layer]?.margin or @config.labelMargin
@ -251,17 +251,14 @@ module.exports = class Renderer
else if @config.layers[feature.layer]?.cluster and @labelBuffer.writeIfPossible "X", point[0], point[1], feature, 3 else if @config.layers[feature.layer]?.cluster and @labelBuffer.writeIfPossible "X", point[0], point[1], feature, 3
@canvas.text "", point[0], point[1], colorCode @canvas.text "", point[0], point[1], colorCode
_seen: {}
_scaleAndReduce: (tile, feature) -> _scaleAndReduce: (tile, feature) ->
reduced = []
for points in feature.points
seen = {}
lastX = null lastX = null
lastY = null lastY = null
outside = false
outside = null
scaled = [] scaled = []
for point in points for point in feature.points
x = Math.floor tile.position.x+point.x/tile.scale x = Math.floor tile.position.x+point.x/tile.scale
y = Math.floor tile.position.y+point.y/tile.scale y = Math.floor tile.position.y+point.y/tile.scale
@ -270,30 +267,28 @@ module.exports = class Renderer
lastY = y lastY = y
lastX = x lastX = x
#
if x < -@tilePadding or # if x < -@tilePadding or
y < -@tilePadding or # y < -@tilePadding or
x > @width+@tilePadding or # x > @width+@tilePadding or
y > @height+@tilePadding # y > @height+@tilePadding
continue if outside # continue if outside
outside = true # outside = true
else # else
if outside # if outside
outside = null # outside = null
scaled.push [lastX, lastY] # scaled.push [lastX, lastY]
scaled.push [x, y] scaled.push [x, y]
if scaled.length is 2 if scaled.length is 2
if seen[ka = scaled[0].concat(scaled[1]).join '-'] or if @_seen[ka = scaled[0].concat(scaled[1]).join '-'] or
seen[kb = scaled[1].concat(scaled[0]).join '-'] @_seen[kb = scaled[1].concat(scaled[0]).join '-']
continue return []
seen[ka] = seen[kb] = true @_seen[ka] = @_seen[kb] = true
unless scaled.length > 1 or feature.type is "symbol" unless scaled.length > 1 or feature.type is "symbol"
continue return []
reduced.push scaled scaled
reduced

View File

@ -24,9 +24,9 @@ module.exports = class Termap
initialZoom: null initialZoom: null
maxZoom: 18 maxZoom: 18
zoomStep: 0.1 zoomStep: 0.25
headless: false headless: false
# size: # size:
# width: 40*2 # width: 40*2
# height: 10*4 # height: 10*4

View File

@ -41,29 +41,37 @@ class Tile
layers = {} layers = {}
for name, layer of tile.layers for name, layer of tile.layers
tree = rbush() tree = rbush()
features = for i in [0...layer.length] for i in [0...layer.length]
# TODO: caching of similar attributes to avoid looking up the style each time # TODO: caching of similar attributes to avoid looking up the style each time
#continue if @styler and not @styler.getStyleFor layer, feature #continue if @styler and not @styler.getStyleFor layer, feature
feature = layer.feature i feature = layer.feature i
feature.properties.$type = [undefined, "Point", "LineString", "Polygon"][feature.type] feature.properties.$type = type = [undefined, "Point", "LineString", "Polygon"][feature.type]
if @styler if @styler
style = @styler.getStyleFor name, feature style = @styler.getStyleFor name, feature
continue unless style continue unless style
# TODO: monkey patching test case for tiles with a reduced extent # TODO: monkey patching test case for tiles with a reduced extent 4096 / 8 -> 512
points = @_reduceGeometry feature, 8 # use feature.loadGeometry() again as soon as we got a 512 extent tileset
geometries = @_reduceGeometry feature, 8
data = if style.type is "fill"
style: style @_addToTree tree,
points: points
properties: feature.properties
id: feature.id id: feature.id
layer: name layer: name
style: style
properties: feature.properties
points: geometries[0]
@_addToTree tree, data else
data for points in geometries
@_addToTree tree,
id: feature.id
layer: name
style: style
properties: feature.properties
points: points
layers[name] = tree layers[name] = tree
@ -71,8 +79,8 @@ class Tile
_addToTree: (tree, data) -> _addToTree: (tree, data) ->
[minX, maxX, minY, maxY] = [Infinity, -Infinity, Infinity, -Infinity] [minX, maxX, minY, maxY] = [Infinity, -Infinity, Infinity, -Infinity]
for outer in data.points
for p in outer for p in data.points
minX = p.x if p.x < minX minX = p.x if p.x < minX
maxX = p.x if p.x > maxX maxX = p.x if p.x > maxX
minY = p.y if p.y < minY minY = p.y if p.y < minY
@ -89,7 +97,7 @@ class Tile
for points, i in feature.loadGeometry() for points, i in feature.loadGeometry()
reduced = [] reduced = []
last = null last = null
for point, j in points for point in points
p = p =
x: Math.floor point.x/factor x: Math.floor point.x/factor
y: Math.floor point.y/factor y: Math.floor point.y/factor

View File

@ -33,45 +33,6 @@
"background-color": "@background" "background-color": "@background"
} }
}, },
{
"type": "fill",
"id": "landuse_overlay_national_park",
"paint": {
"fill-color": "#d8e8c8"
},
"source-layer": "landuse_overlay",
"filter": [
"==",
"class",
"national_park"
]
},
{
"type": "fill",
"id": "landuse_park",
"paint": {
"fill-color": "#d8e8c8"
},
"source-layer": "landuse",
"filter": [
"==",
"class",
"park"
]
},
{
"type": "fill",
"id": "landuse_cemetery",
"paint": {
"fill-color": "#e0e4dd"
},
"source-layer": "landuse",
"filter": [
"==",
"class",
"cemetery"
]
},
{ {
"type": "fill", "type": "fill",
"id": "landuse_hospital", "id": "landuse_hospital",
@ -86,33 +47,6 @@
] ]
}, },
{ {
"type": "fill",
"id": "landuse_school",
"paint": {
"fill-color": "#f0e8f8"
},
"source-layer": "landuse",
"filter": [
"==",
"class",
"school"
]
},
{
"type": "fill",
"id": "landuse_wood",
"paint": {
"fill-color": "#6a4"
},
"source-layer": "landuse",
"filter": [
"==",
"class",
"wood"
]
},
{
"hide": true,
"type": "line", "type": "line",
"id": "waterway", "id": "waterway",
"paint": { "paint": {