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; }
Anchors getAnchors(const GeometryCoordinates &line, float spacing, const float maxAngle, const float textLeft, const float textRight, const float iconLeft, const float iconRight, const float glyphSize, const float boxScale, const float overscaling) { if (line.empty()) return {}; // Resample a line to get anchor points for labels and check that each // potential label passes text-max-angle check and has enough froom to fit // on the line. const float angleWindowSize = (textLeft - textRight) != 0.0f ? 3.0f / 5.0f * glyphSize * boxScale : 0; const float labelLength = fmax(textRight - textLeft, iconRight - iconLeft); // Is the line continued from outside the tile boundary? const bool continuedLine = (line[0].x == 0 || line[0].x == util::EXTENT || line[0].y == 0 || line[0].y == util::EXTENT); // Is the label long, relative to the spacing? // If so, adjust the spacing so there is always a minimum space of `spacing / 4` between label edges. if (spacing - labelLength * boxScale < spacing / 4) { spacing = labelLength * boxScale + spacing / 4; } // Offset the first anchor by: // Either half the label length plus a fixed extra offset if the line is not continued // Or half the spacing if the line is continued. // For non-continued lines, add a bit of fixed extra offset to avoid collisions at T intersections. const float fixedExtraOffset = glyphSize * 2; const float offset = !continuedLine ? std::fmod((labelLength / 2 + fixedExtraOffset) * boxScale * overscaling, spacing) : std::fmod(spacing / 2 * overscaling, spacing); return resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength * boxScale, continuedLine, false); }