def project( bbox: s2.LatLngRect, size: XY, offset: XY, latlnglines: List[List[s2.LatLng]]) -> List[List[Tuple[float, float]]]: min_x = lng2x(bbox.lng_lo().degrees) d_x = lng2x(bbox.lng_hi().degrees) - min_x while d_x >= 2: d_x -= 2 while d_x < 0: d_x += 2 min_y = lat2y(bbox.lat_lo().degrees) max_y = lat2y(bbox.lat_hi().degrees) d_y = abs(max_y - min_y) scale = size.x / d_x if size.x / size.y <= d_x / d_y else size.y / d_y offset = offset + 0.5 * (size - scale * XY(d_x, -d_y)) - scale * XY( min_x, min_y) lines = [] for latlngline in latlnglines: line = [] for latlng in latlngline: if bbox.contains(latlng): line.append((offset + scale * latlng2xy(latlng)).tuple()) else: if len(line) > 0: lines.append(line) line = [] if len(line) > 0: lines.append(line) return lines
def add_missing_flight(self, observer_name: str, injected_flight: InjectedFlight, rect: s2sphere.LatLngRect, injection_target: str, query: fetch.Query) -> None: flight_id = injected_flight.flight.details_responses[0].details.id timestamp = datetime.datetime.utcnow( ) #TODO: Use query timestamp instead span = injected_flight.flight.get_span() self.issues.append( Issue( test_code='MISSING_FLIGHT', relevant_requirements=['NET0610'], severity=Severity.Critical, injection_target=injection_target, observation_source=observer_name, subject=flight_id, summary='Expected flight not found', details= 'Flight {} (from {} to {}) was expected inside ({}, {})-({}, {}) when {} was queried at {}' .format(flight_id, span[0], span[1], rect.lo().lat().degrees, rect.lo().lng().degrees, rect.hi().lat().degrees, rect.hi().lng().degrees, observer_name, timestamp), queries=[query]))
def vertices_from_latlng_rect(rect: s2sphere.LatLngRect) -> List[Dict[str, float]]: return [ {'lat': rect.lat_lo().degrees, 'lng': rect.lng_lo().degrees}, {'lat': rect.lat_lo().degrees, 'lng': rect.lng_hi().degrees}, {'lat': rect.lat_hi().degrees, 'lng': rect.lng_hi().degrees}, {'lat': rect.lat_hi().degrees, 'lng': rect.lng_lo().degrees}, ]
def _determine_center(b: s2sphere.LatLngRect) -> s2sphere.LatLng: y1 = math.log((1 + math.sin(b.lat_lo().radians)) / (1 - math.sin(b.lat_lo().radians))) / 2 y2 = math.log((1 + math.sin(b.lat_hi().radians)) / (1 - math.sin(b.lat_hi().radians))) / 2 lat = math.atan(math.sinh((y1 + y2) / 2)) * 180 / math.pi lng = b.get_center().lng().degrees return s2sphere.LatLng.from_degrees(lat, lng)
def get_flights(resources: ResourceSet, flights_url: str, area: s2sphere.LatLngRect, include_recent_positions: bool) -> Dict: resp = resources.dss_client.get(flights_url, params={ 'view': '{},{},{},{}'.format( area.lat_lo().degrees, area.lng_lo().degrees, area.lat_hi().degrees, area.lng_hi().degrees, ), 'include_recent_positions': 'true' if include_recent_positions else 'false', }, scope=rid.SCOPE_READ) return _json_or_error(resp)
def make_polygon(coords: List[Tuple[float, float]] = None, latlngrect: s2sphere.LatLngRect = None) -> Dict: if coords is not None: return { "vertices": [{ 'lat': lat, 'lng': lng } for (lat, lng) in coords] } return { "vertices": [ { 'lat': latlngrect.lat_lo().degrees, 'lng': latlngrect.lng_lo().degrees }, { 'lat': latlngrect.lat_lo().degrees, 'lng': latlngrect.lng_hi().degrees }, { 'lat': latlngrect.lat_hi().degrees, 'lng': latlngrect.lng_hi().degrees }, { 'lat': latlngrect.lat_hi().degrees, 'lng': latlngrect.lng_lo().degrees }, ] }
def _evaluate_observation(injected_flights: List[InjectedFlight], observer: RIDSystemObserver, rect: s2sphere.LatLngRect, observation: Optional[ observation_api.GetDisplayDataResponse], query: fetch.Query, config: EvaluationConfiguration, findings: Findings) -> None: diagonal = rect.lo().get_distance( rect.hi()).degrees * geo.EARTH_CIRCUMFERENCE_KM / 360 if diagonal > rid.NetMaxDisplayAreaDiagonal: _evaluate_area_to_large_observation(observer, diagonal, query, findings) elif diagonal > rid.NetDetailsMaxDisplayAreaDiagonal: _evaluate_clusters_observation(findings) else: _evaluate_normal_observation(injected_flights, observer, rect, observation, query, config, findings)
def select_relevant_states( self, view: s2sphere.LatLngRect, t0: datetime.datetime, t1: datetime.datetime) -> List[rid.RIDAircraftState]: recent_states: List[rid.RIDAircraftState] = [] previously_outside = False previously_inside = False previous_telemetry = None for telemetry in self.telemetry: if (telemetry.timestamp.datetime < t0 or telemetry.timestamp.datetime > t1): # Telemetry not relevant based on time continue pt = s2sphere.LatLng.from_degrees(telemetry.position.lat, telemetry.position.lng) inside_now = view.contains(pt) if inside_now: if previously_outside: recent_states.append(previous_telemetry) recent_states.append(telemetry) previously_inside = True previously_outside = False else: if previously_inside: recent_states.append(telemetry) previously_outside = True previously_inside = False previous_telemetry = telemetry return recent_states
def observe_system( self, rect: s2sphere.LatLngRect ) -> Tuple[Optional[observation_api.GetDisplayDataResponse], fetch.Query]: initiated_at = datetime.datetime.utcnow() resp = self.session.get('/display_data?view={},{},{},{}'.format( rect.lo().lat().degrees, rect.lo().lng().degrees, rect.hi().lat().degrees, rect.hi().lng().degrees), scope=rid.SCOPE_READ) try: result = (ImplicitDict.parse( resp.json(), observation_api.GetDisplayDataResponse) if resp.status_code == 200 else None) except ValueError as e: result = None return (result, fetch.describe_query(resp, initiated_at))
def add_observation_failure(self, observer_name: str, rect: s2sphere.LatLngRect, query: fetch.Query) -> None: self.issues.append( Issue( test_code='OBSERVATION_FAILED', severity=Severity.Critical, injection_target='N/A', observation_source=observer_name, summary='Observation attempt failed', details= 'When queried for flights in ({}, {})-({}, {}), observer returned an invalid response with code {}' .format(rect.lo().lat().degrees, rect.lo().lng().degrees, rect.hi().lat().degrees, rect.hi().lng().degrees, query.status_code), queries=[query]))
def _make_flight_observation( flight: rid.RIDFlight, view: s2sphere.LatLngRect) -> observation_api.Flight: paths: List[List[observation_api.Position]] = [] current_path: List[observation_api.Position] = [] previous_position: Optional[observation_api.Position] = None lat_min = view.lat_lo().degrees lat_max = view.lat_hi().degrees lng_min = view.lng_lo().degrees lng_max = view.lng_hi().degrees # Decompose recent positions into a list of contiguous paths for p in flight.recent_positions: lat = p.position.lat lng = p.position.lng position_report = observation_api.Position(lat=lat, lng=lng, alt=p.position.alt) inside_view = lat_min <= lat <= lat_max and lng_min <= lng <= lng_max if inside_view: # This point is inside the view if not current_path and previous_position: # Positions were previously outside the view but this one is in current_path.append(previous_position) current_path.append(position_report) else: # This point is outside the view if current_path: # Positions were previously inside the view but this one is out current_path.append(position_report) paths.append(current_path) current_path = [] previous_position = position_report if current_path: paths.append(current_path) return observation_api.Flight( id=flight.id, most_recent_position=observation_api.Position( lat=flight.current_state.position.lat, lng=flight.current_state.position.lng, alt=flight.current_state.position.alt), recent_paths=[observation_api.Path(positions=path) for path in paths])
def flights(utm_client: infrastructure.DSSTestSession, flights_url: str, area: s2sphere.LatLngRect, include_recent_positions: bool) -> FetchedUSSFlights: result = fetch.query_and_describe( utm_client, 'GET', flights_url, params={ 'view': '{},{},{},{}'.format( area.lat_lo().degrees, area.lng_lo().degrees, area.lat_hi().degrees, area.lng_hi().degrees, ), 'include_recent_positions': 'true' if include_recent_positions else 'false', }, scope=rid.SCOPE_READ) return FetchedUSSFlights(result)
def latLngRectFromQueryRectangleInput( self, QueryRectangleRequest: 'QueryRectangleRequest'): queryRectangleRequest = QueryRectangleRequest minPoint = queryRectangleRequest.getMinPoint() maxPoint = queryRectangleRequest.getMaxPoint() latLngRect = None if minPoint is not None and maxPoint is not None: minLatLng = S2LatLng.from_degrees(minPoint.getLatitude(), minPoint.getLongitude()) maxLatLng = S2LatLng.from_degrees(maxPoint.getLatitude(), maxPoint.getLongitude()) latLngRect = S2LatLngRect.from_point_pair(minLatLng, maxLatLng) return latLngRect
def cover_square(lat, lng, width, level=15): offset = int(width / 2) g = Geodesic.WGS84 # @UndefinedVariable r = RegionCoverer() r.min_level, r.min_level = level, level g1 = g.Direct(lat, lng, 360, offset) g1 = g.Direct(g1['lat2'], g1['lon2'], 270, offset) p1 = LatLng.from_degrees(g1['lat2'], g1['lon2']) g2 = g.Direct(lat, lng, 180, offset) g2 = g.Direct(g2['lat2'], g2['lon2'], 90, offset) p2 = LatLng.from_degrees(g2['lat2'], g2['lon2']) cells = r.get_covering(LatLngRect.from_point_pair(p1, p2)) return cells
def cover_square(lat, lng, width, level=15): offset = int(width / 2) g = Geodesic.WGS84 # @UndefinedVariable r = RegionCoverer() r.min_level, r.min_level = level, level g1 = g.Direct(lat, lng, 360, offset) g1 = g.Direct(g1['lat2'],g1['lon2'],270,offset) p1 = LatLng.from_degrees(g1['lat2'],g1['lon2']) g2 = g.Direct(lat, lng, 180, offset) g2 = g.Direct(g2['lat2'],g2['lon2'], 90,offset) p2 = LatLng.from_degrees(g2['lat2'],g2['lon2']) cells = r.get_covering(LatLngRect.from_point_pair(p1, p2)) return cells
def make_polygon(coords: List[Tuple[float, float]] = None, latlngrect: s2sphere.LatLngRect = None) -> Polygon: if coords is not None: return Polygon( vertices=[LatLngPoint(lat=lat, lng=lng) for (lat, lng) in coords]) return Polygon(vertices=[ LatLngPoint(lat=latlngrect.lat_lo().degrees, lng=latlngrect.lng_lo().degrees), LatLngPoint(lat=latlngrect.lat_lo().degrees, lng=latlngrect.lng_hi().degrees), LatLngPoint(lat=latlngrect.lat_hi().degrees, lng=latlngrect.lng_hi().degrees), LatLngPoint(lat=latlngrect.lat_hi().degrees, lng=latlngrect.lng_lo().degrees), ])
def _bbox_polyfill( geo_json, res, ): """returns list of S2 ids""" lat, lon = _geo_json_to_extremes(geo_json) p1 = LatLng.from_degrees(min(lat), min(lon)) p2 = LatLng.from_degrees(max(lat), max(lon)) region = LatLngRect.from_point_pair(p1, p2) coverer = RegionCoverer() coverer.min_level = res coverer.max_level = res return coverer.get_covering(region)
def cover_region_s2(location1, location2): rc = RegionCoverer() rc.max_level = lvl_big rc.min_level = lvl_big rc.max_cells = 1000 locations = [] if location1[0] > location2[0]: lat1, lat2 = location2[0], location1[0] else: lat1, lat2 = location1[0], location2[0] if location1[1] > location2[1]: lng1, lng2 = location2[1], location1[1] else: lng1, lng2 = location1[1], location2[1] lng1 = (lng1 + 180) % 360 - 180 lng2 = (lng2 + 180) % 360 - 180 lngs = [] if lng2 > lng1: lngs.append((lng1, lng2)) elif lng2 < lng1: lngs.append((-180.0, lng2)) lngs.append((lng1, 180.0)) for lng1, lng2 in lngs: cids = rc.get_covering( LatLngRect(LatLng.from_degrees(lat1, lng1), LatLng.from_degrees(lat2, lng2))) for cid in cids: ll_cid = cid.to_lat_lng() locations.append((ll_cid.lat().degrees, ll_cid.lng().degrees)) return locations
def get_latlngrect_diagonal_km(rect: s2sphere.LatLngRect) -> float: """Compute the distance in km between two opposite corners of the rect""" return rect.lo().get_distance( rect.hi()).degrees * EARTH_CIRCUMFERENCE_KM / 360
def area_of_latlngrect(rect: s2sphere.LatLngRect) -> float: """Compute the approximate surface area within a lat-lng rectangle.""" return EARTH_AREA_M2 * rect.area() / (4 * math.pi)