Skip to content

Commit

Permalink
Add FSA to deck.gl Polygon
Browse files Browse the repository at this point in the history
  • Loading branch information
betodealmeida committed Jan 4, 2019
1 parent 5e3d5a9 commit 600d580
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 3 deletions.
25 changes: 25 additions & 0 deletions superset/assets/src/explore/components/controls/SpatialControl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const spatialTypes = {
delimited: 'delimited',
geohash: 'geohash',
zipcode: 'zipcode',
fsa: 'fsa',
};

const propTypes = {
Expand Down Expand Up @@ -47,6 +48,7 @@ export default class SpatialControl extends React.Component {
reverseCheckbox: v.reverseCheckbox || false,
geohashCol: v.geohashCol || defaultCol,
zipcodeCol: v.zipcodeCol || defaultCol,
fsaCol: v.fsaCol || defaultCol,
value: null,
errors: [],
};
Expand Down Expand Up @@ -87,6 +89,12 @@ export default class SpatialControl extends React.Component {
if (!value.zipcodeCol) {
errors.push(errMsg);
}
} else if (type === spatialTypes.fsa) {
value.fsaCol = this.state.fsaCol;
value.reverseCheckbox = this.state.reverseCheckbox;
if (!value.fsaCol) {
errors.push(errMsg);
}
}
this.setState({ value, errors });
this.props.onChange(value, errors);
Expand All @@ -112,6 +120,8 @@ export default class SpatialControl extends React.Component {
return `${this.state.geohashCol}`;
} else if (this.state.type === spatialTypes.zipcode) {
return `${this.state.zipcodeCol}`;
} else if (this.state.type === spatialTypes.fsa) {
return `${this.state.fsaCol}`;
}
return null;
}
Expand Down Expand Up @@ -206,6 +216,21 @@ export default class SpatialControl extends React.Component {
</Col>
</Row>
</PopoverSection>
<PopoverSection
title={t('FSA')}
isSelected={this.state.type === spatialTypes.fsa}
onSelect={this.setType.bind(this, spatialTypes.fsa)}
>
<Row>
<Col md={6}>
Column
{this.renderSelect('fsaCol', spatialTypes.fsa)}
</Col>
<Col md={6}>
{this.renderReverseCheckbox()}
</Col>
</Row>
</PopoverSection>
<div className="clearfix">
<Button
bsSize="small"
Expand Down
1 change: 1 addition & 0 deletions superset/assets/src/explore/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2150,6 +2150,7 @@ export const controls = {
['json', 'JSON'],
['geohash', 'geohash (square)'],
['zipcode', 'ZIP code'],
['fsa', 'FSA'],
],
},

Expand Down
8 changes: 7 additions & 1 deletion superset/assets/src/visualizations/deckgl/layers/common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ export function commonLayerProps(formData, setTooltip, onSelect) {
let tooltipContentGenerator;
if (fd.js_tooltip) {
tooltipContentGenerator = sandboxedEval(fd.js_tooltip);
} else if (fd.line_column && (fd.line_type === 'geohash' || fd.line_type === 'zipcode')) {
} else if (
fd.line_column && (
fd.line_type === 'geohash' ||
fd.line_type === 'zipcode' ||
fd.line_type === 'fsa'
)
) {
tooltipContentGenerator = o => (
<div>
<div>{fd.line_column}: <strong>{o.object[fd.line_column]}</strong></div>
Expand Down
63 changes: 61 additions & 2 deletions superset/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,8 @@ def get_spatial_columns(self, key):
return [spatial.get('geohashCol')]
elif spatial.get('type') == 'zipcode':
return [spatial.get('zipcodeCol')]
elif spatial.get('type') == 'fsa':
return [spatial.get('fsaCol')]

@staticmethod
def parse_coordinates(s):
Expand All @@ -2118,6 +2120,11 @@ def reverse_zipcode_decode(zipcode):
raise NotImplementedError(
'No mapping from ZIP code to single latitude/longitude')

@staticmethod
def reverse_fsa_decode(fsa):
raise NotImplementedError(
'No mapping from FSA code to single latitude/longitude')

@staticmethod
def reverse_latlong(df, key):
df[key] = [
Expand Down Expand Up @@ -2146,6 +2153,9 @@ def process_spatial_data_obj(self, key, df):
elif spatial.get('type') == 'zipcode':
df[key] = df[spatial.get('zipcodeCol')].map(self.reverse_zipcode_decode)
del df[spatial.get('zipcodeCol')]
elif spatial.get('type') == 'fsa':
df[key] = df[spatial.get('fsaCol')].map(self.reverse_fsa_decode)
del df[spatial.get('fsaCol')]

if spatial.get('reverseCheckbox'):
self.reverse_latlong(df, key)
Expand Down Expand Up @@ -2343,6 +2353,14 @@ def deser(zipcode):
return deser


def fsa_deser(fsas):
geojson = fsas_to_json(fsas)
def deser(fsa):
if fsa in geojson:
return geojson[fsa][0][0]
return deser


def zipcodes_to_json(zipcodes):
user = os.environ.get('CREDENTIALS_LYFTPG_USER', '')
password = os.environ.get('CREDENTIALS_LYFTPG_PASSWORD', '')
Expand All @@ -2366,7 +2384,7 @@ def zipcodes_to_json(zipcodes):
if not missing:
return out

# fetch missing geojson from lyftpg
# fetch missing geojson from
in_clause = ', '.join(['%s'] * len(missing))
query = (
'SELECT zipcode, geojson FROM zip_codes WHERE zipcode IN ({0})'
Expand All @@ -2386,6 +2404,43 @@ def zipcodes_to_json(zipcodes):
return out


def fsas_to_json(fsas):
url = 'presto://prestoproxy.lyft.net:8443/hive'

out = {}
missing = set()
for fsa in fsas:
cache_key = 'fsa_geojson_{}'.format(fsa)
geojson = cache and cache.get(cache_key)
if geojson:
out[fsa] = geojson
else:
missing.add(fsa)

if not missing:
return out

# fetch missing geojson from lyftpg
in_clause = ', '.join(['%s'] * len(missing))
query = (
'SELECT fsa, geo_json FROM jbridgem.test_geo_json_fsa WHERE fsa IN ({0})'
.format(in_clause))
conn = sqlalchemy.create_engine(url, connect_args={'protocol': 'https'})
results = conn.execute(query, tuple(missing)).fetchall()

for fsa, geojson in results:
geojson = json.loads(geojson)
out[fsa] = geojson
if cache and len(results) < 10000: # avoid storing too much
cache_key = 'fsa_geojson_{}'.format(fsa)
try:
cache.set(cache_key, geojson, timeout=86400)
except Exception:
pass

return out


class DeckPathViz(BaseDeckGLViz):

"""deck.gl's PathLayer"""
Expand All @@ -2399,6 +2454,7 @@ class DeckPathViz(BaseDeckGLViz):
'polyline': polyline.decode,
'geohash': geohash_to_json,
'zipcode': None, # per request
'fsa': None, # per request
}

def query_obj(self):
Expand All @@ -2424,7 +2480,7 @@ def get_properties(self, d):
if fd.get('reverse_long_lat'):
path = [(o[1], o[0]) for o in path]
d[self.deck_viz_key] = path
if line_type not in ['geohash', 'zipcode']:
if line_type not in ['geohash', 'zipcode', 'fsa']:
del d[line_column]
d['__timestamp'] = d.get(DTTM_ALIAS) or d.get('__time')
return d
Expand All @@ -2435,6 +2491,9 @@ def get_data(self, df):
if line_type == 'zipcode':
zipcodes = df[fd['line_column']].unique()
self.deser_map['zipcode'] = zipcode_deser(zipcodes)
elif line_type == 'fsa':
fsas = df[fd['line_column']].unique()
self.deser_map['fsa'] = fsa_deser(fsas)

self.metric_label = self.get_metric_label(self.metric)
return super(DeckPathViz, self).get_data(df)
Expand Down

0 comments on commit 600d580

Please sign in to comment.