def make_min_size_region(self, region_dict): """ widens a coordinate pair based on maximum distance between points this makes a square (on a mercator projection) bounding box region. it will have different real-world distances in the east west direction between the northern and southern sides """ min_distance = self.max_geo_range * 0.05 gm = GlobalMercator() # measure the north south distance mid_lat = (region_dict['min_lat'] + region_dict['max_lat']) / 2 mid_lon = (region_dict['min_lon'] + region_dict['max_lon']) / 2 ns_diag_dist = gm.distance_on_unit_sphere(region_dict['min_lat'], mid_lon, region_dict['max_lat'], mid_lon) ew_diag_dist = gm.distance_on_unit_sphere(mid_lat, region_dict['min_lon'], mid_lat, region_dict['max_lon']) if ns_diag_dist < min_distance: # the north-south distance is too small, so widen it. # first, find a point south of the mid lat, at the right distance new_lat_s = gm.get_point_by_distance_from_point(mid_lat, mid_lon, (min_distance / 2), 180) # second, find a point north of the mid lat, at the right distance new_lat_n = gm.get_point_by_distance_from_point(mid_lat, mid_lon, (min_distance / 2), 0) region_dict['min_lat'] = new_lat_s['lat'] region_dict['max_lat'] = new_lat_n['lat'] if ew_diag_dist < min_distance: # the east-west distance is too small, so widen it. # first, find a point south of the mid lat, at the right distance new_lon_w = gm.get_point_by_distance_from_point(mid_lat, mid_lon, (min_distance / 2), 270) # second, find a point north of the mid lat, at the right distance new_lon_e = gm.get_point_by_distance_from_point(mid_lat, mid_lon, (min_distance / 2), 90) region_dict['min_lon'] = new_lon_w['lon'] region_dict['max_lon'] = new_lon_e['lon'] return region_dict
def make_geo_meta(self, project_uuid, sub_projs=False): output = False self.project_uuid = project_uuid if sub_projs is False: # check if there are subjects in this project pr = ProjectRels() sub_projs = pr.get_sub_projects(project_uuid) if sub_projs is False: uuids = [project_uuid] else: uuids = [] for sub_proj in sub_projs: uuids.append(sub_proj.uuid) uuids.append(project_uuid) self.get_geo_range(uuids) if self.geo_range is False: pass if self.print_progress: print('Range fail: ' + str(self.geo_range) ) else: if self.print_progress: print('Working on range: ' + str(self.geo_range) ) min_lon_lat = [self.geo_range['longitude__min'], self.geo_range['latitude__min']] max_lon_lat = [self.geo_range['longitude__max'], self.geo_range['latitude__max']] min_point = np.fromiter(min_lon_lat, np.dtype('float')) max_point = np.fromiter(max_lon_lat, np.dtype('float')) gm = GlobalMercator() self.max_geo_range = gm.distance_on_unit_sphere(min_point[1], min_point[0], max_point[1], max_point[0]) if self.print_progress: print('Max geo range: ' + str(self.max_geo_range)) if self.max_geo_range == 0: # only 1 geopoint known for the project proc_centroids = [] proc_centroid = {} proc_centroid['index'] = 0 proc_centroid['id'] = 1 proc_centroid['num_points'] = 1 proc_centroid['cent_lon'] = self.geo_range['longitude__min'] proc_centroid['cent_lat'] = self.geo_range['latitude__max'] proc_centroid['box'] = False proc_centroids.append(proc_centroid) else: # need to cluster geo data proc_centroids = self.cluster_geo(uuids) self.make_geo_objs(proc_centroids) output = True return output
def check_ok_cluster(self, proc_centroid, centroids, uuids): """ checks to see if the proc_centroid is an OK cluster, based on the number of items it contains or if it is far from all other centroids """ ok_cluster = True # default to this being a good cluster if proc_centroid['num_points'] < 2: # the cluster has only 1 point, meaning it may be too small db_multiple = self.check_ok_cluster_for_lone_point(uuids, proc_centroid['max_lon'], proc_centroid['max_lat']) if db_multiple is False: # not many records, # OK now check if it is far from other points single_far = True for o_centroid in centroids: o_lon = o_centroid[0] o_lat = o_centroid[1] if o_lon != proc_centroid['cent_lon'] \ and o_lat != proc_centroid['cent_lat']: # not the same centroid, so check distance gm = GlobalMercator() cent_dist = gm.distance_on_unit_sphere(o_lat, o_lon, proc_centroid['cent_lat'], proc_centroid['cent_lon']) if cent_dist < 1000: if cent_dist < self.MIN_CLUSTER_SIZE_KM \ or cent_dist < (self.max_geo_range * .1): # we found a case where this point is close # to another centroid single_far = False if single_far is False: ok_cluster = False if self.print_progress and ok_cluster is False: message = 'Cluster Loop: ' + str(proc_centroid['cluster_loop']) + ', cluster: ' + str(proc_centroid['index']) + ' ' message += ' has few items, too close with other centroids.' print(message) return ok_cluster
def _distance_determine_aggregation_depth(self, valid_tile_tuples): """Uses distance between to recalibrate aggregation depth in tiles""" if len(valid_tile_tuples) < 2: return self.default_aggregation_depth lons = [] lats = [] gm = GlobalMercator() for tile, _ in valid_tile_tuples: geo_coords = gm.quadtree_to_geojson_lon_lat(tile) # Remember geojson ordering of the coordinates (lon, lat) lons.append(geo_coords[0]) lats.append(geo_coords[1]) max_distance = gm.distance_on_unit_sphere( min(lats), min(lons), max(lats), max(lons), ) # Converts the maximum distance between points into a zoom level # appropriate for tile aggregation. Seems to work well. return gm.ZoomForPixelSize(max_distance) + 3
def get_geotile_scope(self, solr_tiles): """ find the most specific tile shared by the whole dataset """ geo_tiles = [] bound_list = [] max_distance = 0 lat_lon_min_max = [{ 'min': None, 'max': None }, { 'min': None, 'max': None }] for tile_key in solr_tiles[::2]: if tile_key[:6] != '211111': # a bit of a hack to exclude display of # erroroneous data without spatial reference geo_tiles.append(tile_key) gm = GlobalMercator() bounds = gm.quadtree_to_lat_lon(tile_key) lat_lon_min_max = self.get_min_max(lat_lon_min_max, bounds) bound_list.append(bounds) if len(geo_tiles) > 0: test_tile = geo_tiles[0] # we compare against the first tile tile_len = len(test_tile) # size of the tile in_all_geotiles = True i = 0 while (i < tile_len and in_all_geotiles): if i > 0: test_val = str(test_tile[0:i]) else: test_val = str(test_tile[0]) for geo_tile in geo_tiles: if i > len(geo_tile): in_all_geotiles = False else: if i > 0: geo_tile_part = geo_tile[0:i] else: geo_tile_part = geo_tile[0] if test_val != geo_tile_part: in_all_geotiles = False if in_all_geotiles: # ok! we have somthing that is still in all tiles self.geotile_scope = test_val i += 1 if isinstance(self.geotile_scope, str): self.aggregation_depth += round(len(self.geotile_scope) * 1, 0) self.aggregation_depth = int(self.aggregation_depth) if self.aggregation_depth < 6: self.aggregation_depth = 6 if self.aggregation_depth >= 6 and len(bound_list) >= 2: gm = GlobalMercator() max_distance = gm.distance_on_unit_sphere( lat_lon_min_max[0]['min'], lat_lon_min_max[1]['min'], lat_lon_min_max[0]['max'], lat_lon_min_max[1]['max']) if max_distance == 0: self.aggregation_depth = 10 else: # converts the maximum distance between points into a zoom level # appropriate for tile aggregation. seems to work well. self.aggregation_depth = gm.ZoomForPixelSize(max_distance) + 3 # print('now: ' + str(self.aggregation_depth) + ' for ' + str(max_distance)) if self.aggregation_depth > self.max_depth: self.aggregation_depth = self.max_depth if self.aggregation_depth < 6: self.aggregation_depth = 6 return self.geotile_scope