Skip to content

Commit

Permalink
Testing linestring viz examples (further refinements pending)
Browse files Browse the repository at this point in the history
  • Loading branch information
akacarlyann committed Apr 16, 2018
1 parent 3d6b878 commit 03db8aa
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 105 deletions.
155 changes: 113 additions & 42 deletions examples/notebooks/linestring-viz.ipynb
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## GeoJSON linestring source"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"import sys\n",
"import os\n",
"import pandas as pd\n",
Expand All @@ -18,13 +24,16 @@
"# Must be a public token, starting with `pk`\n",
"token = os.getenv('MAPBOX_ACCESS_TOKEN')\n",
"\n",
"# JSON join-data object\n",
"data = [{\"elevation\": x, \"something\": random.randint(0,100)} for x in range(0, 21000, 10)]\n",
"\n",
"# GeoJSON data object\n",
"geojson = {\n",
" \"type\": \"FeatureCollection\",\n",
" \"features\": [{\n",
" \"type\": \"Feature\",\n",
" \"id\": \"01\", \n",
" \"properties\": {\"something\": 50, \"other\": 1}, \n",
" \"properties\": {\"property-1\": 50, \"property-2\": 1}, \n",
" \"geometry\": {\n",
" \"type\": \"LineString\",\n",
" \"coordinates\": [\n",
Expand All @@ -41,24 +50,64 @@
" }, {\n",
" \"type\": \"Feature\",\n",
" \"id\": \"02\",\n",
" \"properties\": {\"something\": 500, \"other\": 2},\n",
" \"properties\": {\"property-1\": 500, \"property-2\": 2},\n",
" \"geometry\": {\n",
" \"type\": \"LineString\",\n",
" \"coordinates\": [\n",
" [-122.4833858013153, 37.929607404976734],\n",
" [-122.4830961227417, 37.83],\n",
" ]\n",
" }\n",
" }, {\n",
" \"type\": \"Feature\",\n",
" \"properties\": {\"property-1\": 5000, \"property-2\": 1},\n",
" \"geometry\": {\n",
" \"type\": \"LineString\",\n",
" \"coordinates\": [\n",
" [-122.48369693756104, 37.83381888486939],\n",
" [-122.48348236083984, 37.83317489144141],\n",
" [-122.48339653015138, 37.83270036637107],\n",
" [-122.48356819152832, 37.832056363179625],\n",
" [-122.48404026031496, 37.83114119107971],\n",
" [-122.48404026031496, 37.83049717427869],\n",
" [-122.48348236083984, 37.829920943955045],\n",
" [-122.48356819152832, 37.82954808664175],\n",
" [-122.48507022857666, 37.82944639795659],\n",
" [-122.48610019683838, 37.82880236636284],\n",
" [-122.48695850372314, 37.82931081282506],\n",
" [-122.48700141906738, 37.83080223556934],\n",
" [-122.48751640319824, 37.83168351665737],\n",
" [-122.48803138732912, 37.832158048267786],\n",
" [-122.48888969421387, 37.83297152392784],\n",
" [-122.48987674713133, 37.83263257682617],\n",
" [-122.49043464660643, 37.832937629287755],\n",
" [-122.49125003814696, 37.832429207817725],\n",
" [-122.49163627624512, 37.832564787218985],\n",
" [-122.49223709106445, 37.83337825839438],\n",
" [-122.49378204345702, 37.83368330777276]\n",
" ]\n",
" }\n",
" }]\n",
"}\n",
"\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"# make viz with GeoJSON source\n",
"viz = LinestringViz(geojson, \n",
" color_property='something',\n",
" color_property='property-1',\n",
" color_stops=create_color_stops([0, 50, 100, 500, 1500], colors='YlOrRd'),\n",
" color_function_type='match',\n",
" color_function_type='interpolate',\n",
" color_default='red',\n",
" line_stroke='-',\n",
" line_width=1,\n",
" line_stroke='--',\n",
" line_width_property='property-2',\n",
" line_width_stops=create_radius_stops([0, 1, 2, 3, 4, 5], 0, 10),\n",
" opacity=0.8,\n",
" center=(-122.48, 37.83),\n",
" zoom=16,\n",
Expand All @@ -67,36 +116,19 @@
"viz.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Vector linestring source"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"import os\n",
"import pandas as pd\n",
"\n",
"from mapboxgl.viz import *\n",
"from mapboxgl.utils import *\n",
"\n",
"# Must be a public token, starting with `pk`\n",
"token = os.getenv('MAPBOX_ACCESS_TOKEN')\n",
"\n",
"data = [{\"elevation\": 0, \"something\": 0},\n",
" {\"elevation\": 10, \"something\": 200},\n",
" {\"elevation\": 20, \"something\": 200},\n",
" {\"elevation\": 30, \"something\": 30},\n",
" {\"elevation\": 40, \"something\": 200},\n",
" {\"elevation\": 50, \"something\": 20},\n",
" {\"elevation\": 60, \"something\": 200},\n",
" {\"elevation\": 70, \"something\": 40},\n",
" {\"elevation\": 80, \"something\": 200},\n",
" {\"elevation\": 90, \"something\": 200},\n",
" {\"elevation\": 100, \"something\": 200},\n",
" ]\n",
"# https://www.mapbox.com/mapbox-gl-js/example/vector-source/\n",
"\n",
"match_stops = [\n",
" [0, \"red\"], \n",
" [10, \"blue\"], \n",
Expand All @@ -117,19 +149,58 @@
" vector_join_property='ele',\n",
" data_join_property='elevation',\n",
" color_property='elevation',\n",
" color_stops=match_stops,\n",
" color_function_type='match',\n",
" color_default='rgb(255,255,0)',\n",
" color_stops=create_color_stops([0, 25, 50, 75, 100], colors='YlOrRd'),\n",
" # color_stops=match_stops,\n",
" color_function_type='match', \n",
" line_width_property='something',\n",
" line_width_stops=create_radius_stops([0, 50, 100], 2, 6),\n",
" line_stroke='-',\n",
" line_color='notused', ##########\n",
" line_width=1,\n",
" line_width_default=3,\n",
" opacity=0.8,\n",
" center=(-122.48, 37.83),\n",
" zoom=16,\n",
" below_layer='waterway-label'\n",
" )\n",
"viz.show()\n",
"\n"
"viz.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# use defaults with geojson source (bare minimum args)\n",
"viz = LinestringViz(geojson)\n",
"\n",
"# important for visibility for this vector\n",
"viz.center = (-122.48, 37.83)\n",
"viz.zoom = 16\n",
"\n",
"# make viz\n",
"viz.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# use defaults with vector source (bare minimum args)\n",
"viz = LinestringViz(data, \n",
" vector_url='mapbox://mapbox.mapbox-terrain-v2',\n",
" vector_layer_name='contour',\n",
" vector_join_property='ele',\n",
" data_join_property='elevation',\n",
" )\n",
"\n",
"# important for visibility for this vector\n",
"viz.center = (-122.48, 37.83)\n",
"viz.zoom = 16\n",
"\n",
"# make viz\n",
"viz.show()\n"
]
},
{
Expand Down
62 changes: 62 additions & 0 deletions mapboxgl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,68 @@ def color_map(lookup, color_stops, default_color='rgb(122,122,122)'):
return default_color


def numeric_map(lookup, numeric_stops, default=0.0):
"""Return a number value interpolated from given numeric_stops
"""
# if no numeric_stops, use default
if len(numeric_stops) == 0:
return default

# dictionary to lookup value from match-type numeric_stops
match_map = dict((x, y) for (x, y) in numeric_stops)

# if lookup matches stop exactly, return corresponding stop (first priority)
# (includes non-numeric numeric_stop "keys" for finding value by match)
if lookup in match_map.keys():
return match_map.get(lookup)

# if lookup value numeric, map value by interpolating from scale
if isinstance(lookup, (int, float, complex)):

# try ordering stops
try:
stops, values = zip(*sorted(numeric_stops))

# if not all stops are numeric, attempt looking up as if categorical stops
except TypeError:
return match_map.get(lookup, default)

# for interpolation, all stops must be numeric
if not all(isinstance(x, (int, float, complex)) for x in stops):
return default

# check if lookup value in stops bounds
if float(lookup) <= stops[0]:
return values[0]

elif float(lookup) >= stops[-1]:
return values[-1]

# check if lookup value matches any stop value
elif float(lookup) in stops:
return values[stops.index(lookup)]

# interpolation required
else:

# identify bounding stop values
lower = max([stops[0]] + [x for x in stops if x < lookup])
upper = min([stops[-1]] + [x for x in stops if x > lookup])

# values from bounding stops
lower_value = values[stops.index(lower)]
upper_value = values[stops.index(upper)]

# compute linear "relative distance" from lower bound to upper bound
distance = (lookup - lower) / (upper - lower)

# return interpolated value
return lower_value + distance * (upper_value - lower_value)

# default value catch-all
return default


def img_encode(arr, **kwargs):
"""Encode ndarray to base64 string image data
Expand Down
65 changes: 2 additions & 63 deletions mapboxgl/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,72 +8,11 @@
from mapboxgl.errors import TokenError
from mapboxgl.utils import color_map
from mapboxgl import templates
from mapboxgl.utils import img_encode
from mapboxgl.utils import img_encode, numeric_map


GL_JS_VERSION = 'v0.44.1'

def height_map(lookup, height_stops, default_height=0.0):
"""Return a height value (in meters) interpolated from given height_stops;
for use with vector-based visualizations using fill-extrusion layers
"""
# if no height_stops, use default height
if len(height_stops) == 0:
return default_height

# dictionary to lookup height from match-type height_stops
match_map = dict((x, y) for (x, y) in height_stops)

# if lookup matches stop exactly, return corresponding height (first priority)
# (includes non-numeric height_stop "keys" for finding height by match)
if lookup in match_map.keys():
return match_map.get(lookup)

# if lookup value numeric, map height by interpolating from height scale
if isinstance(lookup, (int, float, complex)):

# try ordering stops
try:
stops, heights = zip(*sorted(height_stops))

# if not all stops are numeric, attempt looking up as if categorical stops
except TypeError:
return match_map.get(lookup, default_height)

# for interpolation, all stops must be numeric
if not all(isinstance(x, (int, float, complex)) for x in stops):
return default_height

# check if lookup value in stops bounds
if float(lookup) <= stops[0]:
return heights[0]

elif float(lookup) >= stops[-1]:
return heights[-1]

# check if lookup value matches any stop value
elif float(lookup) in stops:
return heights[stops.index(lookup)]

# interpolation required
else:

# identify bounding height stop values
lower = max([stops[0]] + [x for x in stops if x < lookup])
upper = min([stops[-1]] + [x for x in stops if x > lookup])

# heights from bounding stops
lower_height = heights[stops.index(lower)]
upper_height = heights[stops.index(upper)]

# compute linear "relative distance" from lower bound height to upper bound height
distance = (lookup - lower) / (upper - lower)

# return string representing rgb height value
return lower_height + distance * (upper_height - lower_height)

# default height value catch-all
return default_height

class MapViz(object):

Expand Down Expand Up @@ -704,7 +643,7 @@ def generate_vector_width_map(self):
for row in self.data:

# map width to JSON feature using width_property
width = height_map(row[self.line_width_property], self.line_width_stops, self.line_width_default)
width = numeric_map(row[self.line_width_property], self.line_width_stops, self.line_width_default)

# link to vector feature using data_join_property (from JSON object)
vector_stops.append([row[self.data_join_property], width])
Expand Down

0 comments on commit 03db8aa

Please sign in to comment.