GeometryCollection clipLines(const GeometryCollection &lines, const int16_t x1, const int16_t y1, const int16_t x2, const int16_t y2) { GeometryCollection clippedLines; for (auto& line : lines) { if (line.empty()) continue; auto end = line.end() - 1; for (auto it = line.begin(); it != end; it++) { GeometryCoordinate p0 = *(it); GeometryCoordinate p1 = *(it + 1); if (p0.x < x1 && p1.x < x1) { continue; } else if (p0.x < x1) { p0 = { x1, static_cast<int16_t>(::round(p0.y + (p1.y - p0.y) * ((float)(x1 - p0.x) / (p1.x - p0.x)))) }; } else if (p1.x < x1) { p1 = { x1, static_cast<int16_t>(::round(p0.y + (p1.y - p0.y) * ((float)(x1 - p0.x) / (p1.x - p0.x)))) }; } if (p0.y < y1 && p1.y < y1) { continue; } else if (p0.y < y1) { p0 = { static_cast<int16_t>(::round(p0.x + (p1.x - p0.x) * ((float)(y1 - p0.y) / (p1.y - p0.y)))), y1 }; } else if (p1.y < y1) { p1 = { static_cast<int16_t>(::round(p0.x + (p1.x - p0.x) * ((float)(y1 - p0.y) / (p1.y - p0.y)))), y1 }; } if (p0.x >= x2 && p1.x >= x2) { continue; } else if (p0.x >= x2) { p0 = { x2, static_cast<int16_t>(::round(p0.y + (p1.y - p0.y) * ((float)(x2 - p0.x) / (p1.x - p0.x)))) }; } else if (p1.x >= x2) { p1 = { x2, static_cast<int16_t>(::round(p0.y + (p1.y - p0.y) * ((float)(x2 - p0.x) / (p1.x - p0.x)))) }; } if (p0.y >= y2 && p1.y >= y2) { continue; } else if (p0.y >= y2) { p0 = { static_cast<int16_t>(::round(p0.x + (p1.x - p0.x) * ((float)(y2 - p0.y) / (p1.y - p0.y)))), y2 }; } else if (p1.y >= y2) { p1 = { static_cast<int16_t>(::round(p0.x + (p1.x - p0.x) * ((float)(y2 - p0.y) / (p1.y - p0.y)))), y2 }; } if (clippedLines.empty() || (!clippedLines.back().empty() && !(p0 == clippedLines.back().back()))) { clippedLines.emplace_back(); clippedLines.back().push_back(p0); } clippedLines.back().push_back(p1); } } return clippedLines; }
optional<GeometryCollection> FeatureIndex::translateQueryGeometry( const GeometryCollection& queryGeometry, const std::array<float, 2>& translate, const 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 == TranslateAnchorType::Viewport) { translateVec = util::rotate(translateVec, -bearing); } GeometryCollection translated; for (auto& ring : queryGeometry) { translated.emplace_back(); auto& translatedRing = translated.back(); for (auto& p : ring) { translatedRing.push_back(p - translateVec); } } return translated; }
optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) { if (offset == 0) return {}; GeometryCollection newRings; Point<double> zero(0, 0); for (const auto& ring : rings) { newRings.emplace_back(); auto& newRing = newRings.back(); for (auto i = ring.begin(); i != ring.end(); i++) { auto& p = *i; Point<double> aToB = i == ring.begin() ? zero : util::perp(util::unit(convertPoint<double>(p - *(i - 1)))); Point<double> bToC = i + 1 == ring.end() ? zero : util::perp(util::unit(convertPoint<double>(*(i + 1) - p))); Point<double> extrude = util::unit(aToB + bToC); const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; extrude *= (1.0 / cosHalfAngle); newRing.push_back(convertPoint<int16_t>(extrude * offset) + p); } } return newRings; }
static void processPolynodeBranch(ClipperLib::PolyNode* polynode, GeometryCollection& rings) { // Exterior ring. rings.push_back(fromClipperPath(polynode->Contour)); assert(signedArea(rings.back()) > 0); // Interior rings. for (auto * ring : polynode->Childs) { rings.push_back(fromClipperPath(ring->Contour)); assert(signedArea(rings.back()) < 0); } // PolyNodes may be nested in the case of a polygon inside a hole. for (auto * ring : polynode->Childs) { for (auto * subRing : ring->Childs) { processPolynodeBranch(subRing, rings); } } }
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"); } }