std::unordered_map<std::string, std::vector<Feature>> Source::Impl::queryRenderedFeatures(const QueryParameters& parameters) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || parameters.geometry.empty()) { return result; } LineString<double> queryGeometry; for (const auto& p : parameters.geometry) { queryGeometry.push_back(TileCoordinate::fromScreenCoordinate( parameters.transformState, 0, { p.x, parameters.transformState.getSize().height - p.y }).p); } mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry); auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) { return a.id.canonical.z != b.id.canonical.z ? a.id.canonical.z < b.id.canonical.z : a.id.canonical.y != b.id.canonical.y ? a.id.canonical.y < b.id.canonical.y : a.id.wrap != b.id.wrap ? a.id.wrap < b.id.wrap : a.id.canonical.x < b.id.canonical.x; }; std::vector<std::reference_wrapper<const RenderTile>> sortedTiles; std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles), [](const auto& pair) { return std::ref(pair.second); }); std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles); for (const auto& renderTileRef : sortedTiles) { const RenderTile& renderTile = renderTileRef.get(); GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { continue; } GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { continue; } GeometryCoordinates tileSpaceQueryGeometry; tileSpaceQueryGeometry.reserve(queryGeometry.size()); for (const auto& c : queryGeometry) { tileSpaceQueryGeometry.push_back(TileCoordinate::toGeometryCoordinate(renderTile.id, c)); } renderTile.tile.queryRenderedFeatures(result, tileSpaceQueryGeometry, parameters.transformState, parameters.layerIDs); } return result; }
bool polygonIntersectsBox(const LineString<float>& polygon, const GridIndex<IndexedSubfeature>::BBox& bbox) { // This is just a wrapper that allows us to use the integer-based util::polygonIntersectsPolygon // Conversion limits our query accuracy to single-pixel resolution GeometryCoordinates integerPolygon; for (const auto& point : polygon) { integerPolygon.push_back(convertPoint<int16_t>(point)); } int16_t minX1 = bbox.min.x; int16_t maxY1 = bbox.max.y; int16_t minY1 = bbox.min.y; int16_t maxX1 = bbox.max.x; auto bboxPoints = GeometryCoordinates { { minX1, minY1 }, { maxX1, minY1 }, { maxX1, maxY1 }, { minX1, maxY1 } }; return util::polygonIntersectsPolygon(integerPolygon, bboxPoints); }
GeometryCollection VectorTileFeature::getGeometries() const { uint8_t cmd = 1; uint32_t length = 0; int32_t x = 0; int32_t y = 0; const float scale = float(util::EXTENT) / layer.extent; GeometryCollection lines; lines.emplace_back(); GeometryCoordinates* line = &lines.back(); auto g_itr = geometry_iter.begin(); while (g_itr != geometry_iter.end()) { if (length == 0) { uint32_t cmd_length = static_cast<uint32_t>(*g_itr++); cmd = cmd_length & 0x7; length = cmd_length >> 3; } --length; if (cmd == 1 || cmd == 2) { x += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); y += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); if (cmd == 1 && !line->empty()) { // moveTo lines.emplace_back(); line = &lines.back(); } line->emplace_back(::round(x * scale), ::round(y * scale)); } else if (cmd == 7) { // closePolygon if (!line->empty()) { line->push_back((*line)[0]); } } else { throw std::runtime_error("unknown command"); } }
GeometryCollection VectorTileFeature::getGeometries() const { pbf data(geometry_pbf); uint8_t cmd = 1; uint32_t length = 0; int32_t x = 0; int32_t y = 0; GeometryCollection lines; lines.emplace_back(); GeometryCoordinates* line = &lines.back(); while (data.data < data.end) { if (length == 0) { uint32_t cmd_length = data.varint(); cmd = cmd_length & 0x7; length = cmd_length >> 3; } --length; if (cmd == 1 || cmd == 2) { x += data.svarint(); y += data.svarint(); if (cmd == 1 && !line->empty()) { // moveTo lines.emplace_back(); line = &lines.back(); } line->emplace_back(x, y); } else if (cmd == 7) { // closePolygon if (!line->empty()) { line->push_back((*line)[0]); } } else { throw std::runtime_error("unknown command"); } }
static GeometryCoordinates fromClipperPath(const ClipperLib::Path& path) { GeometryCoordinates result; result.reserve(path.size() + 1); result.reserve(path.size()); for (const auto& p : path) { using Coordinate = GeometryCoordinates::coordinate_type; assert(p.x >= std::numeric_limits<Coordinate>::min()); assert(p.x <= std::numeric_limits<Coordinate>::max()); assert(p.y >= std::numeric_limits<Coordinate>::min()); assert(p.y <= std::numeric_limits<Coordinate>::max()); result.emplace_back(Coordinate(p.x), Coordinate(p.y)); } // Clipper does not repeat initial point, but our geometry model requires it. if (!result.empty()) { result.push_back(result.front()); } return result; }
optional<GeometryCoordinates> FeatureIndex::translateQueryGeometry( const GeometryCoordinates& queryGeometry, const std::array<float, 2>& translate, const style::TranslateAnchorType anchorType, const float bearing, const float pixelsToTileUnits) { if (translate[0] == 0 && translate[1] == 0) { return {}; } GeometryCoordinate translateVec(translate[0] * pixelsToTileUnits, translate[1] * pixelsToTileUnits); if (anchorType == style::TranslateAnchorType::Viewport) { translateVec = util::rotate(translateVec, -bearing); } GeometryCoordinates translated; for (const auto& p : queryGeometry) { translated.push_back(p - translateVec); } return translated; }
void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, const std::vector<Immutable<Layer::Impl>>&, const bool needsRendering, const bool, const TileParameters& parameters) { enabled = needsRendering; if (!needsRendering) { return; } auto transformState = parameters.transformState; std::swap(baseImpl, baseImpl_); auto coords = impl().getCoordinates(); std::shared_ptr<PremultipliedImage> image = impl().getImage(); if (!image || !image->valid()) { enabled = false; return; } // Compute the z0 tile coordinates for the given LatLngs TileCoordinatePoint nePoint = { -INFINITY, -INFINITY }; TileCoordinatePoint swPoint = { INFINITY, INFINITY }; std::vector<TileCoordinatePoint> tileCoordinates; for (LatLng latLng : coords) { auto point = TileCoordinate::fromLatLng(0, latLng).p; tileCoordinates.push_back(point); swPoint.x = std::min(swPoint.x, point.x); nePoint.x = std::max(nePoint.x, point.x); swPoint.y = std::min(swPoint.y, point.y); nePoint.y = std::max(nePoint.y, point.y); } // Calculate the optimum zoom level to determine the tile ids to use for transforms auto dx = nePoint.x - swPoint.x; auto dy = nePoint.y - swPoint.y; auto dMax = std::max(dx, dy); double zoom = std::max(0.0, std::floor(-util::log2(dMax))); // Only enable if the long side of the image is > 2 pixels. Resulting in a // display of at least 2 x 1 px image // A tile coordinate unit represents the length of one tile (tileSize) at a given zoom. // To convert a tile coordinate to pixels, multiply by tileSize. // Here dMax is in z0 tile units, so we also scale by 2^z to match current zoom. enabled = dMax * std::pow(2.0, transformState.getZoom()) * util::tileSize > 2.0; if (!enabled) { return; } auto imageBounds = LatLngBounds::hull(coords[0], coords[1]); imageBounds.extend(coords[2]); imageBounds.extend(coords[3]); auto tileCover = util::tileCover(imageBounds, zoom); tileIds.clear(); tileIds.push_back(tileCover[0]); bool hasVisibleTile = false; // Add additional wrapped tile ids if neccessary auto idealTiles = util::tileCover(transformState, transformState.getZoom()); for (auto tile : idealTiles) { if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) { tileIds.push_back({ tile.wrap, tileCover[0].canonical }); hasVisibleTile = true; } else if (!hasVisibleTile) { for (auto coveringTile: tileCover) { if(coveringTile.canonical == tile.canonical || coveringTile.canonical.isChildOf(tile.canonical) || tile.canonical.isChildOf(coveringTile.canonical)) { hasVisibleTile = true; } } } } enabled = hasVisibleTile; if (!enabled) { return; } // Calculate Geometry Coordinates based on tile cover at ideal zoom GeometryCoordinates geomCoords; for (auto tileCoords : tileCoordinates) { auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tileCoords); geomCoords.push_back(gc); } if (!bucket) { bucket = std::make_unique<RasterBucket>(image); } else { bucket->clear(); if (image != bucket->image) { bucket->setImage(image); } } // Set Bucket Vertices, Indices, and segments bucket->vertices.emplace_back( RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 })); bucket->vertices.emplace_back( RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 })); bucket->vertices.emplace_back( RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT })); bucket->vertices.emplace_back( RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT })); bucket->indices.emplace_back(0, 1, 2); bucket->indices.emplace_back(1, 2, 3); bucket->segments.emplace_back(0, 0, 4, 6); }