예제 #1
0
template<> NValue NValue::callUnary<FUNC_VOLT_POLYGONFROMTEXT>() const
{
    bool is_shell = true;
    if (isNull()) {
        return NValue::getNullValue(VALUE_TYPE_GEOGRAPHY);
    }

    int32_t textLength;
    const char* textData = getObject_withoutNull(&textLength);
    const std::string wkt(textData, textLength);

    // Discard whitespace, but return commas or parentheses as tokens
    Tokenizer tokens(wkt, boost::char_separator<char>(" \f\n\r\t\v", ",()"));
    Tokenizer::iterator it = tokens.begin();
    Tokenizer::iterator end = tokens.end();

    if (! boost::iequals(*it, "polygon")) {
        throwInvalidWktPoly("does not start with POLYGON keyword");
    }
    ++it;

    if (! boost::iequals(*it, "(")) {
        throwInvalidWktPoly("missing left parenthesis after POLYGON keyword");
    }
    ++it;

    std::size_t length = Polygon::serializedLengthNoLoops();
    std::vector<std::unique_ptr<S2Loop> > loops;
    while (it != end) {
        loops.push_back(std::unique_ptr<S2Loop>(new S2Loop()));
        readLoop(is_shell, wkt, it, end, loops.back().get());
        // Only the first loop is a shell.
        is_shell = false;
        length += Loop::serializedLength(loops.back()->num_vertices());
        if (*it == ",") {
            ++it;
        }
        else if (*it == ")") {
            ++it;
            break;
        }
        else {
            throwInvalidWktPoly("unexpected token: '" + (*it) + "'");
        }
    }

    if (it != end) {
        // extra stuff after input
        throwInvalidWktPoly("unrecognized input after WKT: '" + (*it) + "'");
    }

    NValue nval = ValueFactory::getUninitializedTempGeographyValue(length);
    char* storage = const_cast<char*>(ValuePeeker::peekObjectValue(nval));

    Polygon poly;
    poly.init(&loops); // polygon takes ownership of loops here.
    SimpleOutputSerializer output(storage, length);
    poly.saveToBuffer(output);
    return nval;
}
예제 #2
0
static NValue polygonFromText(const std::string &wkt, bool doValidation)
{
    // Discard whitespace, but return commas or parentheses as tokens
    Tokenizer tokens(wkt, boost::char_separator<char>(" \f\n\r\t\v", ",()"));
    Tokenizer::iterator it = tokens.begin();
    Tokenizer::iterator end = tokens.end();

    if (! boost::iequals(*it, "polygon")) {
        throwInvalidWktPoly("does not start with POLYGON keyword");
    }
    ++it;

    if (! boost::iequals(*it, "(")) {
        throwInvalidWktPoly("missing left parenthesis after POLYGON keyword");
    }
    ++it;

    bool is_shell = true;
    std::size_t length = Polygon::serializedLengthNoLoops();
    std::vector<std::unique_ptr<S2Loop> > loops;
    while (it != end) {
        loops.push_back(std::unique_ptr<S2Loop>(new S2Loop()));
        readLoop(is_shell, wkt, it, end, loops.back().get());
        // Only the first loop is a shell.
        is_shell = false;
        length += Loop::serializedLength(loops.back()->num_vertices());
        if (*it == ",") {
            ++it;
        }
        else if (*it == ")") {
            ++it;
            break;
        }
        else {
            throwInvalidWktPoly("unexpected token: '" + (*it) + "'");
        }
    }

    if (it != end) {
        // extra stuff after input
        throwInvalidWktPoly("unrecognized input after WKT: '" + (*it) + "'");
    }

    NValue nval = ValueFactory::getUninitializedTempGeographyValue(length);
    char* storage = const_cast<char*>(ValuePeeker::peekObjectValue(nval));

    Polygon poly;
    poly.init(&loops); // polygon takes ownership of loops here.
    if (doValidation) {
        std::stringstream validReason;
        if (!poly.IsValid(&validReason)
                || isMultiPolygon(poly, &validReason)) {
            throwInvalidWktPoly(validReason.str());
        }
    }
    SimpleOutputSerializer output(storage, length);
    poly.saveToBuffer(output);
    return nval;
}
예제 #3
0
static GeographyPointValue::Coord stringToCoord(int pointOrPoly,
                                  const std::string& input,
                           const std::string& val)
{
    GeographyPointValue::Coord coord = 0.0;
    try {
        coord = boost::lexical_cast<GeographyPointValue::Coord>(val);
    }
    catch (const boost::bad_lexical_cast&) {
        if (pointOrPoly == POLY) {
            throwInvalidWktPoly("expected a number but found '" + val + "'");
        }
        else {
            throwInvalidWktPoint(input);
        }
    }

    return coord;
}
예제 #4
0
static void readLoop(bool is_shell,
                     const std::string &wkt,
                     Tokenizer::iterator &it,
                     const Tokenizer::iterator &end,
                     S2Loop *loop)
{
    if (! boost::iequals(*it, "(")) {
        throwInvalidWktPoly("expected left parenthesis to start a loop");
    }
    ++it;

    std::vector<S2Point> points;
    while (it != end && *it != ")") {
        GeographyPointValue::Coord lng = stringToCoord(POLY, wkt, *it);

        if (lng < -180 || lng > 180) {
            throwInvalidPolygonLongitude(*it);
        }
        ++it;
        GeographyPointValue::Coord lat = stringToCoord(POLY, wkt, *it);
        if (lat < -90 || lat > 90) {
            throwInvalidPolygonLatitude(*it);
        }
        ++it;

        // Note: This is S2.  It takes latitude, longitude, not
        //       longitude, latitude.
        points.push_back(S2LatLng::FromDegrees(lat, lng).ToPoint());

        if (*it == ",") {
            ++it;
            // continue to next lat long pair
        }
        else if (*it != ")") {
            throwInvalidWktPoly("unexpected token: '" + (*it) + "'");
        }
    }

    if (it == end) {
        // we hit the end of input before the closing parenthesis
        throwInvalidWktPoly("unexpected end of input");
    }

    assert (*it == ")");

    // Advance iterator to next token
    ++it;

    if (points.size() < 4) {
        throwInvalidWktPoly("A polygon ring must contain at least 4 points (including repeated closing vertex)");
    }

    const S2Point& first = points.at(0);
    const S2Point& last = points.at(points.size() - 1);

    if (first != last) {
        throwInvalidWktPoly("A polygon ring's first vertex must be equal to its last vertex");
    }

    // S2 format considers the closing vertex in a loop to be
    // implicit, while in WKT it is explicit.  Remove the closing
    // vertex here to reflect this.
    points.pop_back();
    // The first is a shell.  All others are holes.  We need to reverse
    // the order of the vertices for holes.
    if (!is_shell) {
        // Don't touch the first point.  We don't want to
        // cycle the vertices.
        std::reverse(++(points.begin()), points.end());
    }
    loop->Init(points);
}