#include "MapViewModel.h" #include namespace Pyxis { namespace MapView { namespace { const double EARTH_CIRCUMFERENCE_METERS = 40075016.68557849; const double DEGREES_TO_RADIANS = 0.017453292519943295; bool validLocation(const Telemetry::LocationTelemetry& location) { return location.latitude_e6 >= -90000000 && location.latitude_e6 <= 90000000 && location.longitude_e6 >= -180000000 && location.longitude_e6 <= 180000000; } bool validRequest(const Request& request) { if (!(std::isfinite(request.center.latitude) && std::isfinite(request.center.longitude) && request.center.latitude >= -90.0 && request.center.latitude <= 90.0 && request.width != 0 && request.height != 0 && MapProjection::isValidZoom(request.zoom) && request.peer_count <= MAX_MAP_PEERS && (request.peer_count == 0 || request.peers != nullptr))) { return false; } if (request.has_local_location && !validLocation(request.local_location)) return false; for (std::size_t index = 0; index < request.peer_count; ++index) { if (!validLocation(request.peers[index].location)) return false; } return true; } MapProjection::GeoPoint pointFromLocation( const Telemetry::LocationTelemetry& location) { MapProjection::GeoPoint point{}; point.latitude = static_cast(location.latitude_e6) / 1000000.0; point.longitude = static_cast(location.longitude_e6) / 1000000.0; return point; } void appendMarker(const Telemetry::LocationTelemetry& location, MarkerKind kind, const Telemetry::PeerId& peer, bool has_approx_radius, std::uint32_t approx_radius_meters, const MapProjection::Viewport& viewport, std::uint32_t zoom, Frame& output) { MapProjection::MarkerProjection projected{}; const MapProjection::GeoPoint point = pointFromLocation(location); if (MapProjection::projectMarker( point, viewport, zoom, projected) != MapProjection::Status::OK || !projected.visible) { return; } Marker& marker = output.markers[output.marker_count++]; marker.kind = kind; marker.peer = peer; marker.screen_x = projected.screen_x; marker.screen_y = projected.screen_y; marker.has_approx_radius = has_approx_radius; marker.approx_radius_meters = approx_radius_meters; marker.approx_radius_pixels = 0.0; if (has_approx_radius && approx_radius_meters != 0U) { const double world_pixels = static_cast(MapProjection::TILE_SIZE) * static_cast(MapProjection::tileCount(zoom)); const double meters_per_pixel = std::cos(MapProjection::clampLatitude(point.latitude) * DEGREES_TO_RADIANS) * EARTH_CIRCUMFERENCE_METERS / world_pixels; if (meters_per_pixel > 0.0 && std::isfinite(meters_per_pixel)) { marker.approx_radius_pixels = static_cast(approx_radius_meters) / meters_per_pixel; } } } } // namespace Result buildFrame(const Request& request, Frame& output) { if (!validRequest(request)) { return request.peer_count > MAX_MAP_PEERS ? Result::CAPACITY_EXCEEDED : Result::INVALID_ARGUMENT; } MapProjection::GlobalPixel center_pixel{}; if (MapProjection::latLonToGlobalPixel( request.center, request.zoom, center_pixel) != MapProjection::Status::OK) { return Result::INVALID_ARGUMENT; } MapProjection::Viewport viewport{}; viewport.left = center_pixel.x - static_cast(request.width) / 2.0; viewport.top = center_pixel.y - static_cast(request.height) / 2.0; viewport.width = request.width; viewport.height = request.height; std::size_t tile_count = 0; const MapProjection::Status tile_status = MapProjection::viewportTiles( viewport, request.zoom, request.include_tile_border, output.tiles, MapProjection::MAX_VIEWPORT_TILES, tile_count); if (tile_status != MapProjection::Status::OK) { return tile_status == MapProjection::Status::VIEWPORT_TOO_LARGE || tile_status == MapProjection::Status::CAPACITY_EXCEEDED ? Result::VIEWPORT_TOO_LARGE : Result::INVALID_ARGUMENT; } output.viewport = viewport; output.tile_count = tile_count; output.marker_count = 0; const Telemetry::PeerId empty_peer{}; if (request.has_local_location) { appendMarker(request.local_location, MarkerKind::LOCAL, empty_peer, false, 0, viewport, request.zoom, output); } for (std::size_t index = 0; index < request.peer_count; ++index) { const Telemetry::PeerLocationRecord& record = request.peers[index]; if (record.has_expiry && request.wall_now_millis >= record.expires_at_millis) { continue; } appendMarker(record.location, MarkerKind::PEER, record.peer, record.has_approx_radius, record.approx_radius_meters, viewport, request.zoom, output); } return Result::OK; } } // namespace MapView } // namespace Pyxis