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_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_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")