def children(request): """ Implement the "/geo/api/location/children" API call. """ loc_code = get_param(request, "location") if loc_code != None: # We've been asked to generate a list of all children of the given # location. try: location = Location.objects.get(code=loc_code) except Location.DoesNotExist: return error_response(code=400, message="Missing or invalid location code", callback=get_param(request, "callback")) children = [] for child in location.children.all(): children.append(child.code) results = json.dumps({'children' : children}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json") # If we get here, we don't have a parent location -> assemble a list of all # the top-level (country) locations. countryLevel = Level.objects.get(level=1) countries = [] for country in Location.objects.filter(level=countryLevel): countries.append(country.code) results = json.dumps({'children' : countries}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")
def locate(request): """ Implement the "/locate" API call. """ if request.method == "GET": params = request.GET elif request.method == "POST": params = request.POST else: params = {} code,response = geolocator.geolocate(params) if code == 200: # We got a normal response -> bundle it up and return it to the caller. response = json.dumps(response) if "callback" in params: # Wrap the response in a JSONP callback function. response = params['callback'] + "(" + response + ")" return HttpResponse(response, mimetype="application/json") else: # We got an error response -> return the error back to the caller. return error_response(code, response, params.get("callback"))
def parents(request): """ Implement the "parents" API call. """ loc_code = get_param(request, "location") try: location = Location.objects.get(code=loc_code) except Location.DoesNotExist: return error_response(code=400, message="Missing or invalid location code", callback=get_param(request, "callback")) parents = [] for parent in location.parents.all(): parents.append(parent.code) results = json.dumps({'parents' : parents}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")
def main(request): """ Respond to the "/" URL. This is the main entry point for the tableau_api application. """ # Extract our CGI parameters. location = get_param(request, "location") if location == None: return error_response(code=400, message="Missing 'location' parameter.", callback=get_param(request, "callback")) name = get_param(request, "name") ok = True # initially. width = get_param(request, "width") if width == None: ok = False else: try: width = int(width) except ValueError: ok = False if ok: if width < 10 or width > 2000: ok = False if not ok: return error_response(code=400, message="Missing or invalid 'width' parameter.", callback=get_param(request, "callback")) ok = True # initially. height = get_param(request, "height") if height == None: ok = False else: try: height = int(height) except ValueError: ok = False if ok: if height < 10 or height > 2000: ok = False if not ok: return error_response(code=400, message="Missing or invalid 'height' parameter.", callback=get_param(request, "callback")) # See which tableau we need to generate. tableau = tableauGenerator.select_tableau(location, name) if tableau == None: return error_response(code=500, message="No tableau found.", callback=get_param(request, "callback")) # See if this tableau already exists in the tableau cache for the requested # size. If so, we can use the cached JSON data directly. if tableauCache != None: json_data = tableauCache.get(tableau.id, width, height) else: json_data = None # No cache module -> nothing is cached. # If we didn't have a cache entry for this tableau and size, we have to # generate it. if json_data == None: try: generated_tableau = tableauGenerator.generate_tableau(tableau, width, height) except: # Something went wrong. Print the traceback into the Django log # and return an error message back to the caller. traceback.print_exc() return error_response(code=500, message="Internal server error.", callback=get_param(request, "callback")) json_data = json.dumps({'tableau' : generated_tableau}) # If we have a tableau cache, save the generated tableau into the cache # so that we can retrieve it quickly next time. if tableauCache != None: tableauCache.save(tableau.id, width, height, json_data) # Finally, return the results back to the caller. callback = get_param(request, "callback") if callback != None: json_data = callback + "(" + json_data + ")" # Add JSONP callback. return HttpResponse(json_data, mimetype="application/json")
def search_by_latlong(request): """ Implement the "latlong" API call. """ # Process our CGI parameters. try: latitude = float(request.GET["lat"]) except: return error_response( code=400, message="Missing or invalid 'lat' parameter.", callback=get_param(request, "callback") ) try: longitude = float(request.GET["long"]) except: return error_response( code=400, message="Missing or invalid 'long' parameter.", callback=get_param(request, "callback") ) try: strategy = request.GET["strategy"] except KeyError: return error_response( code=400, message="Missing 'strategy' parameter.", callback=get_param(request, "callback") ) if strategy == "all": pass elif strategy == "level": try: levels_param = request.GET["levels"] except KeyError: return error_response( code=400, message="Missing 'levels' parameter.", callback=get_param(request, "callback") ) levels = [] # List of desired Level objects. for level_name in levels_param.split(","): level = name_to_level(level_name) if level != None: levels.append(level) else: return error_response( code=400, message="Unknown level: " + repr(level_name), callback=get_param(request, "callback") ) elif strategy == "parent": try: loc_code = request.GET["location"] except KeyError: return error_response( code=400, message="Missing 'location' parameter.", callback=get_param(request, "callback") ) if loc_code == "WORLD": location = None else: try: location = Location.objects.get(code=loc_code) except Location.DoesNotExist: return error_response( code=400, message="Unknown location " + repr(loc_code), callback=get_param(request, "callback") ) else: return error_response( code=400, message="Invalid strategy " + repr(strategy), callback=get_param(request, "callback") ) # Calculate the matching locations, using the specified strategy. pt = Point(longitude, latitude, srid=4326) locations = [] if strategy == "all": for outline in Outline.objects.filter(outline__bbcontains=pt): if outline.outline.contains(pt): locations.append(outline.location.code) elif strategy == "level": for level in levels: for outline in Outline.objects.filter(location__level=level, outline__bbcontains=pt): if outline.outline.contains(pt): locations.append(outline.location.code) elif strategy == "parent": # Check the starting location's children. outlineIDs = [] if location != None: for child in location.children.all(): outline = get_outline(child) if outline != None: outlineIDs.append(outline.id) else: # We're at the top level -> check the countries. for child in Location.objects.filter(level__level=1): outline = get_outline(child) if outline != None: outlineIDs.append(outline.id) for outline in Outline.objects.filter(id__in=outlineIDs, outline__bbcontains=pt): if outline.outline.contains(pt): locations.append(outline.location.code) break if len(locations) == 0: # We didn't find a child location -> search for locations starting # at the same level as the parent, going up to the country level. if location != None: for level in range(location.level.level, 0, -1): query = Outline.objects.filter(outline__bbcontains=pt, location__level__level=level) for outline in query: if outline.outline.contains(pt): locations.append(outline.location.code) break if len(locations) > 0: break # Only find the first matching location. # Finally, return the found locations back to the caller. results = json.dumps({"locations": locations}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")
def search_by_name(request): """ Implement the "/name" API call. """ # Extract our parameters. levels = [] level_param = get_param(request, "level") if level_param != None: level = name_to_level(level_param) if level == None: return error_response( code=400, message="Unknown level: " + repr(level_param), callback=get_param(request, "callback") ) levels.append(level) else: levels_param = get_param(request, "levels") if levels_param != None: for level_name in levels_param.split(","): level = name_to_level(level_name) if level == None: return error_response( code=400, message="Unknown level: " + repr(level_param), callback=get_param(request, "callback") ) levels.append(level) text = get_param(request, "text") if text == None: return error_response(code=400, message="Missing 'text' parameter.", callback=get_param(request, "callback")) if len(text) < 3: return error_response(code=400, message="'text' parameter too short.", callback=get_param(request, "callback")) type = get_param(request, "type") if type != None: type = type.lower() if not type in [ "exact", "contains", "startswith", "endswith", "iexact", "icontains", "istartswith", "iendswith", ]: return error_response( code=400, message="Invalid 'type' parameter: " + repr(type), callback=get_param(request, "callback") ) else: type = "istartswith" # Default search type. # Perform the appropriate type of name-based search. args = {"name__" + str(type): text} if len(levels) > 0: args["level__in"] = levels query = Name.objects.filter(**args).order_by("level__level", "name") results = {} num_matches = query.count() results["numMatches"] = num_matches if num_matches <= 20: matches = [] matched_locs = set() # Used to only include each location once. for name in query: for loc_name in name.locationname_set.all(): location = loc_name.location if location in matched_locs: # Only include each location once. continue matches.append( { "level": level_to_name(location.level), "code": location.code, "foundName": name.name, "locationName": location.name, "displayName": location.display_name, "abbreviation": location.abbreviation, "context": get_context(location), } ) matched_locs.add(location) results["locations"] = matches # Finally, return the results back to the caller. results = json.dumps(results) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")
def search_by_bbox(request): """ Implement the "bbox" API call. """ # Extract our parameters. levels = [] # List of desired Level objects. levels_param = get_param(request, "levels") if levels_param != None: for level_name in levels_param.split(","): level = name_to_level(level_name) if level == None: return error_response( code=400, message="Unknown level: " + repr(level_name), callback=get_param(request, "callback") ) else: levels.append(level) minlat = get_param(request, "minlat") maxlat = get_param(request, "maxlat") minlong = get_param(request, "minlong") maxlong = get_param(request, "maxlong") try: minlat = float(minlat) except: return error_response( code=400, message="Invalid minlat: " + repr(minlat), callback=get_param(request, "callback") ) try: maxlat = float(maxlat) except: return error_response( code=400, message="Invalid maxlat: " + repr(maxlat), callback=get_param(request, "callback") ) try: minlong = float(minlong) except: return error_response( code=400, message="Invalid minlong: " + repr(minlong), callback=get_param(request, "callback") ) try: maxlong = float(maxlong) except: return error_response( code=400, message="Invalid maxlong: " + repr(maxlong), callback=get_param(request, "callback") ) # Create a Polygon geometry that encompasses the specified bounding box. bounds = Polygon.from_bbox((minlong, minlat, maxlong, maxlat)) # Perform a spatial query to return all objects that *might* be in our # bounding box. Note that, because of limitations in MySQL, we have to # then check each object individually. locations = [] # List of all location codes within our desired bounds. for outline in Outline.objects.filter(location__level__in=levels, outline__intersects=bounds): mpoly = outline.outline if mpoly.intersects(bounds): locations.append(outline.location.code) # Finally, return the results back to the caller. results = json.dumps({"locations": locations}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")
def outline(request): """ Implement the "outline" API call. """ # Get the list of locations to return the outline for. locations = get_param(request, "locations") if locations == None: location = get_param(request, "location") if location == None: return error_response(code=400, message="Missing 'locations' or 'location'" +" parameter.", callback=get_param(request, "callback")) locations = [location] else: try: locations = json.loads(locations) except: return error_response(code=400, message="Invalid 'locations' parameter.", callback=get_param(request, "callback")) # Get the desired output format. format = get_param(request, "format") if format not in ["wkt", "gml", "geojson", "kml"]: return error_response(code=400, message="Missing or invalid 'format'" +" parameter: " + repr(format), callback=get_param(request, "callback")) # Extract the desired outlines. results = [] for loc_code in locations: try: location = Location.objects.get(code=loc_code) except Location.DoesNotExist: return error_response(code=400, message="There is no location with code " + repr(loc_code), callback=get_param(request, "callback")) try: outline = Outline.objects.get(location=location) if True: orig_size = len(outline.outline.wkt) outline = outline.outline.simplify(0.3515/2, True) simplified_size = len(outline.wkt) # print "%d -> %d" % (orig_size, simplified_size) else: outline = outline.outline except Outline.DoesNotExist: outline = None result = {'location' : loc_code} if outline != None: if format == "wkt": result['outline'] = outline.wkt elif format == "gml": result['outline'] = outline.ogr.gml elif format == "geojson": result['outline'] = outline.geojson elif format == "kml": result['outline'] = outline.kml results.append(result) results = json.dumps({'outlines' : results}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")
def get(request): """ Implement the "get" API call. """ locations = get_param(request, "locations") if locations == None: location = get_param(request, "location") if location == None: return error_response(code=400, message="Missing 'locations' or 'location' " +"parameter.", callback=get_param(request, "callback")) locations = [location] has_multi = False else: try: locations = json.loads(locations) except: return error_response(code=400, message="Invalid 'locations' parameter.", callback=get_param(request, "callback")) has_multi = True results = [] for loc_code in locations: try: location = Location.objects.get(code=loc_code) except Location.DoesNotExist: return error_response(code=400, message="Missing or invalid location code", callback=get_param(request, "callback")) loc_data = {} loc_data['code'] = location.code loc_data['level'] = level_to_name(location.level) loc_data['name'] = location.name loc_data['displayName'] = location.display_name loc_data['abbreviation'] = location.abbreviation loc_data['minZoomLat'] = float(location.min_zoom_lat) loc_data['maxZoomLat'] = float(location.max_zoom_lat) loc_data['minZoomLong'] = float(location.min_zoom_long) loc_data['maxZoomLong'] = float(location.max_zoom_long) loc_data['context'] = get_context(location) if location.population != None: loc_data['population'] = location.population if location.area != None: loc_data['area'] = location.area if location.averageIncome != None: loc_data['averageIncome'] = int(location.averageIncome) results.append(loc_data) if has_multi: results = json.dumps({'locations' : results}) else: results = json.dumps({'location' : results[0]}) callback = get_param(request, "callback") if callback != None: results = callback + "(" + results + ")" # Add JSONP callback. return HttpResponse(results, mimetype="application/json")