def DoSearch(self, search_query, response_type): """Performs the coordinate search. Args: search_query: A string containing the search coordinates as entered by the user. response_type: Response type can be KML or JSONP, depending on the client. Returns: search_results: A KML/JSONP formatted string which contains search results. Raises: BadQueryException: if the search query is invalid. """ coordinate_type = "" search_results = "" input_coordinates = [] decimal_degrees_coordinates = [] search_tokens = self.utils.SearchTokensFromString(search_query) self.logger.debug("coordinates: %s", ",".join(search_tokens)) input_coordinates = self._transform.GetInputCoordinates( ",".join(search_tokens)) number_of_coordinates = len(input_coordinates) if number_of_coordinates == 0: msg = "Incomplete search query %s submitted" % search_query self.logger.error(msg) raise exceptions.BadQueryException(msg) coordinate_type = self._transform.GetInputType(input_coordinates) self.logger.debug("Coordinate type is %s.", coordinate_type) if coordinate_type in self.coordinates_in_lat_lng_format_: reqd_num_of_coordinates = CoordinateSearch.NUM_OF_COORDS_IN_LAT_LNG_FORMAT else: reqd_num_of_coordinates = CoordinateSearch.NUM_OF_COORDS_IN_MGRS_FORMAT if number_of_coordinates > reqd_num_of_coordinates: self.logger.warning( "extra search parameters ignored: %s", ",".join( input_coordinates[reqd_num_of_coordinates:])) input_coordinates = input_coordinates[:reqd_num_of_coordinates] elif number_of_coordinates < reqd_num_of_coordinates: msg = "Incomplete search query %s submitted" % search_query self.logger.error(msg) raise exceptions.BadQueryException(msg) decimal_degrees_coordinates = self._transform.TransformToDecimalDegrees( coordinate_type, input_coordinates) search_results = self.ConstructResponse( response_type, decimal_degrees_coordinates) search_status = True if search_results else False return search_status, search_results
def GetInputType(self, coordinates): """Check the coordinate type. Valid types are DD, DMS, DDM, UTM, MGRS. Args: coordinates: List containing latitude and longitude pair (both should be in same format). Returns: coordinate_type: coordinate type i.e., DD, DMS, DDM, UTM or MGRS. Raises: BadQueryException: for invalid coordinate type. """ coordinate_type = "" pattern_matched = [] for pattern, input_type in self._input_pattern_types: pattern_obj = re.compile(pattern) pattern_matched = [ pattern_obj.match(z) is not None for z in coordinates ] if self.IsValidInputType(pattern_matched): coordinate_type = input_type break if not coordinate_type: msg = ("Invalid coordinate type. Supported formats are " "DD, DMS, DDM, UTM and MGRS.") self._logger.error(msg) raise exceptions.BadQueryException(msg) return coordinate_type
def HandleSearchRequest(self, environ): """Fetches the search tokens from form and performs the coordinate search. Args: environ: A list of environment variables as supplied by the WSGI interface to the coordinate search application interface. Returns: search_results: A KML/JSONP formatted string which contains search results. Raises: BadQueryException: if the search query is invalid. """ search_results = "" # Fetch all the attributes provided by the user. parameters = self.utils.GetParameters(environ) response_type = self.utils.GetResponseType(environ) # Retrieve the function call back name for JSONP response. self.f_callback = self.utils.GetCallback(parameters) original_query = self.utils.GetValue(parameters, "q") if not original_query: msg = "Empty search query received." self.logger.error(msg) raise exceptions.BadQueryException(msg) search_status, search_results = self.DoSearch(original_query, response_type) if not search_status: folder_name = "Search returned no results." search_results = self.utils.NoSearchResults( folder_name, self._style, response_type, self.f_callback) return (search_results, response_type)
def TransformToDecimalDegrees(self, coordinate_type, coordinates): """Performs a coordinate transformation from DMS, DDM, MGRS, UTM to DD. Args: coordinate_type: Format of coordinates, can be either of DD, DMS, DDM, UTM, MGRS. coordinates: List of coordinates in coordinate_type format. Returns: decimal_degrees_coordinates: List of coordinates in DD(Decimal Degrees) format. Raises: BadQueryException: if the coordinates are invalid. """ conversion_status = False decimal_degrees_coordinates = [] if coordinate_type == "DMS": conversion_status, decimal_degrees_coordinates = self.TransformDMSToDD( coordinates) elif coordinate_type == "DDM": conversion_status, decimal_degrees_coordinates = self.TransformDDMToDD( coordinates) elif coordinate_type == "MGRS": conversion_status, decimal_degrees_coordinates = self.TransformMGRSToDD( coordinates) elif coordinate_type == "UTM": conversion_status, decimal_degrees_coordinates = self.TransformUTMToDD( coordinates) elif coordinate_type == "DD": conversion_status, decimal_degrees_coordinates = self.TransformDDToDD( coordinates) else: self._logger.error( "Invalid type %s for coordinates %s.", coordinate_type, coordinates) self._logger.debug( "Transformation of %s to DD format %s", coordinates, ("successful" if conversion_status else "Failed")) if (not conversion_status or len(decimal_degrees_coordinates) != 2 or not self.IsValidCoordinatePair( float(decimal_degrees_coordinates[0]), float(decimal_degrees_coordinates[1]))): msg = "Couldn't transform coordinates: %s of type: %s" % ( coordinates, coordinate_type) self._logger.error(msg) raise exceptions.BadQueryException(msg) return decimal_degrees_coordinates
def HandleSearchRequest(self, environ): """Retrieves search tokens from form and performs the custom POI search. Args: environ: A list of environment variables as supplied by the WSGI interface to the custom POI search application interface. Returns: A XML/JSON formatted string with search results. Raises: BadQueryException: When all the mandatory parameters are not provided to the Google Places database. """ # Retrieve "response_type", "location", "radius", "key" and # "callback" parameters from the form. parameters = self.utils.GetParameters(environ) response_type = self.utils.GetResponseType(environ) location = self.utils.GetValue(parameters, "location") radius = self.utils.GetValue(parameters, "radius") server_key = self.utils.GetValue(parameters, "key") # Default "callback" value is "handleSearchResults". f_callback = self.utils.GetCallback(parameters) if location and server_key and radius: output_type = self.GetOutputType(response_type) # Create Google Places database URL here. url = self._places_api_url % (output_type, location, radius, server_key) self.logger.debug("Google Places database URL is %s.", url) url_fetch = urllib2.urlopen(url) if response_type == "KML": return self.FormatKMLResponse(url_fetch) elif response_type == "JSONP": # Prepare a "jsonp" message as per the requirements of # GEE search services for maps. return "%s(%s);" % (f_callback, self.FormatJSONResponse(url_fetch)) else: error = ("location, radius or key information not " "received by the server.") raise exceptions.BadQueryException(error)
def __ParseSQLResults(self, search_expression, poi_info, response_type, bbox): """Performs the poi search and returns the results. Args: search_expression: Normalized search expression. poi_info: Tuple containing sql_query, number of columns, style id and poi id as extracted from "poi_table" table of "gesearch" database. response_type: Either KML/JSONP, depending on the client. bbox: The bounding box string. Returns: poi_query_status: Whether data could be queried from "gepoi" database. poi_query_results: Query results as a list. Raises: psycopg2.pool.PoolError in case of error while getting a connection from the pool. """ poi_query_status = False poi_query_results = [] query_results = [] poi_query = "" sql_query = poi_info[0] num_of_input_fields = poi_info[1] try: search_tokens = self.utils.SearchTokensFromString(search_expression) except Exception as e: raise exceptions.BadQueryException( "Couldn't parse search term. Error: %s" % e) self.logger.debug("Parsed search tokens: %s", search_tokens) params = ["%" + entry + "%" for entry in search_tokens] num_params = len(params) if num_params == 0: raise exceptions.BadQueryException("No search term.") if num_params > num_of_input_fields: params = params[:num_of_input_fields] elif num_params < num_of_input_fields: if num_params == 1: params.extend([params[0]] * (num_of_input_fields - num_params)) else: params.extend(["^--IGNORE--"] * (num_of_input_fields - num_params)) accum_func = self.utils.GetAccumFunc(response_type) # sql queries retrieved from "gesearch" database has "?" for # input arguments, but postgresql supports "%s". # Hence, replacing "?" with "%s". sql_stmt = sql_query.replace("?", "%s") # Extract the displayable and searchable POI fields from the POI # queries retrieved from "poi_table". matched = re.match(r"(\w*)\s*(Encode.*geom)(.*)(FROM.*)(WHERE )(\(.*\))", sql_stmt) if matched: (sub_query1, unused_sub_query2, sub_query3, sub_query4, sub_query5, sub_query6) = matched.groups() # sub_query2 need to be replaced with a new query # as per the response_type. # PYLINT throws warning that sub_query2 has not been used, it's # not required and can be ignored. geom_stmt = "%s(the_geom) AS the_geom" % (accum_func) poi_query = "%s %s%s%s%s%s" % (sub_query1, geom_stmt, sub_query3, sub_query4, sub_query5, sub_query6) poi_query += " AND ( the_geom && %s )" % bbox # Displayable POI fields appear between SELECT and FROM # clause of the POI queries, as in below example. # SELECT ST_AsKML(the_geom) AS the_geom, "rpoly_", "fnode_" # FROM gepoi_7 WHERE ( lower("rpoly_") LIKE %s OR lower("name") LIKE %s ). # "rpoly_" and "fnode_" are display fields in the above example. display_fields = [field.replace("\"", "").strip() for field in filter(len, sub_query3.split(","))] # Insert geom at the 0th index for easy retrieval. display_fields.insert(0, "geom") # Searchable POI fields appear after the WHERE # clause of the POI queries, as in below example. # SELECT ST_AsKML(the_geom) AS the_geom, "rpoly_", "fnode_" # FROM gepoi_7 WHERE ( lower("rpoly_") LIKE %s OR lower("name") LIKE %s ). # "rpoly_" and "name" are searchable fields in the above example. searchable_fields = [ entry.strip().strip("OR").strip().strip("lower").strip("\\(\\)\"") for entry in filter(len, sub_query6.strip("\\(\\) ").split("LIKE %s")) ] # Insert geom at the 0th index for easy retrieval. searchable_fields.insert(0, "geom") if poi_query: poi_query_status, query_results = self.__RunPGSQLQuery( self._poi_pool, poi_query, params) # Create a map which will have the POI fields values # retrieved from the gepoi_X tables and # other information like displayable or searchable or both # based on the display and search labels retrieved above. # Some sample maps are as below. # 1) { # 'field_value': 'State Route 55', # 'field_name': 'name', # 'is_search_display': True, # 'is_searchable': True, # 'is_displayable': True #} # 2) {'field_value': '0', # 'field_name': 'rpoly_', # 'is_search_display': True, # 'is_searchable': True, # 'is_displayable': True}. # 3) {'field_value': '22395', # 'field_name': 'fnode_', # 'is_search_display': True, # 'is_searchable': True, # 'is_displayable': True}. # These maps would be used when creating the KML and JSONP responses # The different flags (is_displayable,is_searchable etc) allow for # easier retrieval of data based on our requirements. for entry in query_results: field_name_value = [] for field_name, field_value in zip(display_fields, entry): temp = {} temp["field_name"] = field_name temp["field_value"] = field_value temp["is_searchable"] = (field_name in searchable_fields) # "is_displayable" is always True as we are iterating over # the display(only) POI fields. temp["is_displayable"] = True temp["is_search_display"] = ( temp["is_displayable"] and temp["is_searchable"]) field_name_value.append(temp) for field_name in searchable_fields: temp = {} if field_name not in display_fields: temp["field_name"] = field_name temp["field_value"] = "" # "is_searchable" is always True as we are iterating over # the search(only) POI fields. temp["is_searchable"] = True temp["is_displayable"] = (field_name in display_fields) temp["is_search_display"] = ( temp["is_displayable"] and temp["is_searchable"]) field_name_value.append(temp) poi_query_results.append(field_name_value) return poi_query_status, poi_query_results