forked from codeforamerica/US-Census-Area-API
-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.py
154 lines (114 loc) · 4.62 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from sys import stderr
from os import environ
from urlparse import urlparse
from StringIO import StringIO
from zipfile import ZipFile, ZIP_DEFLATED
from time import time
from flask import Flask
from flask import request
from flask import Response
from flask import render_template
from osgeo import ogr
from util import json_encode, bool
from geo import features_geojson, QueryError
from geo import get_intersecting_features, get_matching_features
from census import census_url, get_features as census_features
cors = 'Access-Control-Allow-Origin'
app = Flask(__name__)
def is_census_datasource(environ):
''' Return true if the environment specifies the U.S. Census datasource.
'''
return environ.get('GEO_DATASOURCE', None) == census_url
def get_datasource(environ):
''' Return an environment-appropriate datasource.
For local data, this will be an OGR Datasource object.
'''
if is_census_datasource(environ):
# Use the value of the environment variable directly,
datasource = environ['GEO_DATASOURCE']
else:
# Or just open datasource.shp with OGR.
datasource = ogr.Open('datasource.shp')
return datasource
@app.route('/')
def hello():
host_port = urlparse(request.base_url).netloc.encode('utf-8')
is_downloadable = not is_census_datasource(environ)
is_us_census = is_census_datasource(environ)
return render_template('index.html', **locals())
@app.route('/.well-known/status')
def status():
datasource = get_datasource(environ)
status = {
'status': 'ok' if bool(datasource) else 'Bad datasource: %s' % repr(datasource),
'updated': int(time()),
'dependencies': [],
'resources': {}
}
body = json_encode(status)
return Response(body, headers={'Content-type': 'application/json', cors: '*'})
@app.route("/areas")
def areas():
''' Retrieve geographic areas.
'''
is_census = is_census_datasource(environ)
lat = float(request.args['lat'])
lon = float(request.args['lon'])
include_geom = bool(request.args.get('include_geom', True))
json_callback = request.args.get('callback', None)
layer_names = is_census and request.args.get('layers', '')
layer_names = layer_names and set(layer_names.split(','))
# This. Is. Python.
ogr.UseExceptions()
point = ogr.Geometry(wkt='POINT(%f %f)' % (lon, lat))
if is_census:
features = census_features(point, include_geom, layer_names)
else:
datasource = get_datasource(environ)
features = get_intersecting_features(datasource, point, include_geom)
body, mime = features_geojson(features, json_callback)
return Response(body, headers={'Content-type': mime, cors: '*'})
@app.route('/select')
def select():
''' Retrieve features.
'''
if is_census_datasource(environ):
error = "Can't select individual features from " + census_url
return Response(render_template('error.html', error=error), status=404)
where_clause = request.args.get('where', None)
where_clause = where_clause and str(where_clause)
page_number = int(request.args.get('page', 1))
include_geom = bool(request.args.get('include_geom', True))
json_callback = request.args.get('callback', None)
# This. Is. Python.
ogr.UseExceptions()
try:
datasource = get_datasource(environ)
features = get_matching_features(datasource, where_clause, page_number, include_geom)
except QueryError, e:
body, mime = json_encode({'error': str(e)}), 'application/json'
if json_callback:
body = '%s(%s);\n' % (json_callback, body)
mime = 'text/javascript'
return Response(body, status=400, headers={'Content-type': mime, cors: '*'})
else:
body, mime = features_geojson(features, json_callback)
return Response(body, headers={'Content-type': mime, cors: '*'})
@app.errorhandler(404)
def error_404(error):
return render_template('error.html', error=str(error))
@app.route('/datasource.zip')
def download_zip():
if is_census_datasource(environ):
error = "Can't download all of " + census_url
return Response(render_template('error.html', error=error), status=404)
buffer = StringIO()
archive = ZipFile(buffer, 'w', ZIP_DEFLATED)
archive.write('datasource.shp')
archive.write('datasource.shx')
archive.write('datasource.dbf')
archive.write('datasource.prj')
archive.close()
return Response(buffer.getvalue(), headers={'Content-Type': 'application/zip'})
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)