25 #include <QJsonObject> 27 #include <QPainterPath> 29 #include <nlohmann/json.hpp> 50 if ( mCurves.size() != otherCurve->mCurves.size() )
53 for (
int i = 0; i < mCurves.size(); ++i )
55 if ( *mCurves.at( i ) != *otherCurve->mCurves.at( i ) )
64 auto result = qgis::make_unique< QgsCompoundCurve >();
66 return result.release();
71 return QStringLiteral(
"CompoundCurve" );
82 mCurves.reserve( curve.mCurves.size() );
85 mCurves.append( c->
clone() );
97 mCurves.append( c->
clone() );
111 qDeleteAll( mCurves );
118 if ( mCurves.empty() )
124 for (
int i = 1; i < mCurves.size(); ++i )
150 for (
int i = 0; i <
nCurves; ++i )
153 wkbPtr -= 1 +
sizeof( int );
166 currentCurve->
fromWkb( wkbPtr );
167 mCurves.append( currentCurve );
182 QString defaultChildWkbType = QStringLiteral(
"LineString%1%2" ).arg(
is3D() ? QStringLiteral(
"Z" ) : QString(),
isMeasure() ? QStringLiteral(
"M" ) : QString() );
185 for (
const QString &childWkt : blocks )
198 if ( !mCurves.back()->fromWkt( childWkt ) )
209 for (
const QgsCurve *curve : qgis::as_const( mCurves ) )
211 hasZ = hasZ || curve->is3D();
212 hasM = hasM || curve->isMeasure();
226 int binarySize =
sizeof( char ) +
sizeof( quint32 ) +
sizeof( quint32 );
227 QVector<QByteArray> wkbForCurves;
228 wkbForCurves.reserve( mCurves.size() );
229 for (
const QgsCurve *curve : mCurves )
231 QByteArray wkbForCurve = curve->asWkb();
232 binarySize += wkbForCurve.length();
233 wkbForCurves << wkbForCurve;
237 wkbArray.resize( binarySize );
240 wkb << static_cast<quint32>(
wkbType() );
241 wkb << static_cast<quint32>( mCurves.size() );
242 for (
const QByteArray &wkbForCurve : qgis::as_const( wkbForCurves ) )
251 QString wkt =
wktTypeStr() + QLatin1String(
" (" );
252 for (
const QgsCurve *curve : mCurves )
254 QString childWkt = curve->asWkt( precision );
255 if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
258 childWkt = childWkt.mid( childWkt.indexOf(
'(' ) );
260 wkt += childWkt +
',';
262 if ( wkt.endsWith(
',' ) )
273 std::unique_ptr< QgsLineString > line(
curveToLine() );
274 QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
280 QDomElement compoundCurveElem = doc.createElementNS( ns, QStringLiteral(
"CompositeCurve" ) );
283 return compoundCurveElem;
285 for (
const QgsCurve *curve : mCurves )
287 QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral(
"curveMember" ) );
288 QDomElement curveElem = curve->asGml3( doc, precision, ns, axisOrder );
289 curveMemberElem.appendChild( curveElem );
290 compoundCurveElem.appendChild( curveMemberElem );
293 return compoundCurveElem;
299 std::unique_ptr< QgsLineString > line(
curveToLine() );
300 return line->asJsonObject( precision );
306 for (
const QgsCurve *curve : mCurves )
308 length += curve->length();
315 if ( mCurves.empty() )
319 return mCurves.at( 0 )->startPoint();
324 if ( mCurves.empty() )
328 return mCurves.at( mCurves.size() - 1 )->
endPoint();
334 if ( mCurves.empty() )
339 mCurves[0]->points( pts );
340 for (
int i = 1; i < mCurves.size(); ++i )
343 mCurves[i]->points( pList );
358 for (
int i = 0; i <
nCurves; ++i )
360 nPoints += mCurves.at( i )->numPoints() - 1;
368 if ( mCurves.isEmpty() )
373 if ( !curve->isEmpty() )
382 std::unique_ptr< QgsLineString > currentLine;
383 for (
const QgsCurve *curve : mCurves )
385 currentLine.reset( curve->curveToLine( tolerance, toleranceType ) );
386 line->
append( currentLine.get() );
397 std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) );
400 result->mCurves.append( gridified.release() );
404 if ( result->mCurves.empty() )
407 return result.release();
413 const QVector< QgsCurve * > curves = mCurves;
419 if ( curve->numPoints() == 0 ||
qgsDoubleNear( curve->length(), 0.0, epsilon ) )
422 delete mCurves.takeAt( i );
430 curve->moveVertex(
QgsVertexId( -1, -1, 0 ), lastEnd );
441 if ( i < 0 || i >= mCurves.size() )
445 return mCurves.at( i );
452 if ( mCurves.empty() )
481 if ( i < 0 || i >= mCurves.size() )
486 delete mCurves.takeAt( i );
499 if ( !mCurves.isEmpty() )
501 lastCurve = mCurves.at( mCurves.size() - 1 );
508 mCurves.append( line );
525 for (
const QgsCurve *curve : mCurves )
533 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
535 curve->transform( ct, d, transformZ );
542 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
544 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
552 for (
const QgsCurve *curve : mCurves )
554 curve->addToPainterPath( pp );
562 for (
const QgsCurve *curve : mCurves )
564 curve->addToPainterPath( pp );
571 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
572 if ( curveIds.empty() )
576 int curveId = curveIds.at( 0 ).first;
577 if ( curveId >= mCurves.size() )
582 bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
592 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
593 QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
594 for ( ; idIt != curveIds.constEnd(); ++idIt )
596 mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
599 bool success = !curveIds.isEmpty();
609 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
610 if ( curveIds.size() == 1 )
612 if ( !mCurves.at( curveIds.at( 0 ).first )->
deleteVertex( curveIds.at( 0 ).second ) )
617 if ( mCurves.at( curveIds.at( 0 ).first )->
numPoints() == 0 )
622 else if ( curveIds.size() == 2 )
624 Q_ASSERT( curveIds.at( 1 ).first == curveIds.at( 0 ).first + 1 );
625 Q_ASSERT( curveIds.at( 0 ).second.vertex == mCurves.at( curveIds.at( 0 ).first )->
numPoints() - 1 );
626 Q_ASSERT( curveIds.at( 1 ).second.vertex == 0 );
631 mCurves.at( curveIds.at( 1 ).first )->
numPoints() > 3 )
635 mCurves.at( curveIds.at( 1 ).first ) ->
pointAt( 2, intermediatePoint, type );
636 mCurves.at( curveIds.at( 0 ).first )->
moveVertex(
637 QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->
numPoints() - 1 ), intermediatePoint );
639 else if ( !mCurves.at( curveIds.at( 0 ).first )->
deleteVertex( curveIds.at( 0 ).second ) )
645 mCurves.at( curveIds.at( 0 ).first )->
numPoints() > 0 &&
648 QgsPoint intermediatePoint = mCurves.at( curveIds.at( 0 ).first ) ->
endPoint();
651 else if ( !mCurves.at( curveIds.at( 1 ).first )->
deleteVertex( curveIds.at( 1 ).second ) )
656 if ( mCurves.at( curveIds.at( 0 ).first )->
numPoints() == 0 &&
657 mCurves.at( curveIds.at( 1 ).first )->
numPoints() != 0 )
662 else if ( mCurves.at( curveIds.at( 0 ).first )->
numPoints() != 0 &&
663 mCurves.at( curveIds.at( 1 ).first )->
numPoints() == 0 )
665 mCurves.at( curveIds.at( 0 ).first )->
moveVertex(
669 else if ( mCurves.at( curveIds.at( 0 ).first )->
numPoints() == 0 &&
670 mCurves.at( curveIds.at( 1 ).first )->
numPoints() == 0 )
675 line->insertVertex(
QgsVertexId( 0, 0, 0 ), startPoint );
676 line->insertVertex(
QgsVertexId( 0, 0, 1 ), endPoint );
677 mCurves.insert( curveIds.at( 0 ).first, line );
681 QgsPoint endPointOfFirst = mCurves.at( curveIds.at( 0 ).first ) ->
endPoint();
683 if ( endPointOfFirst != startPointOfSecond )
688 mCurves.insert( curveIds.at( 1 ).first, line );
693 bool success = !curveIds.isEmpty();
701 QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId(
QgsVertexId id )
const 703 QVector< QPair<int, QgsVertexId> > curveIds;
705 int currentVertexIndex = 0;
706 for (
int i = 0; i < mCurves.size(); ++i )
708 int increment = mCurves.at( i )->numPoints() - 1;
709 if (
id.vertex >= currentVertexIndex &&
id.vertex <= currentVertexIndex + increment )
711 int curveVertexId =
id.vertex - currentVertexIndex;
715 vid.
vertex = curveVertexId;
716 curveIds.append( qMakePair( i, vid ) );
717 if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) )
720 curveIds.append( qMakePair( i + 1, vid ) );
724 currentVertexIndex += increment;
737 int currentVertexId = 0;
738 for (
int j = 0; j < mCurves.size(); ++j )
740 int nCurvePoints = mCurves.at( j )->numPoints();
741 if ( ( node - currentVertexId ) < nCurvePoints )
743 return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
745 currentVertexId += ( nCurvePoints - 1 );
752 int currentVertexId = 0;
753 for (
int j = 0; j < mCurves.size(); ++j )
755 int nCurvePoints = mCurves.at( j )->numPoints();
756 if ( ( index - currentVertexId ) < nCurvePoints )
758 return mCurves.at( j )->xAt( index - currentVertexId );
760 currentVertexId += ( nCurvePoints - 1 );
767 int currentVertexId = 0;
768 for (
int j = 0; j < mCurves.size(); ++j )
770 int nCurvePoints = mCurves.at( j )->numPoints();
771 if ( ( index - currentVertexId ) < nCurvePoints )
773 return mCurves.at( j )->yAt( index - currentVertexId );
775 currentVertexId += ( nCurvePoints - 1 );
782 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
784 curve->filterVertices( filter );
791 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
800 for (
const QgsCurve *curve : mCurves )
802 curve->sumUpArea( sum );
817 for (
const QgsCurve *curve : mCurves )
819 if ( curve->hasCurvedSegments() )
829 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
830 if ( curveIds.size() == 1 )
832 QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
833 return curve->
vertexAngle( curveIds.at( 0 ).second );
835 else if ( curveIds.size() > 1 )
837 QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
838 QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
839 double angle1 = curve1->
vertexAngle( curveIds.at( 0 ).second );
840 double angle2 = curve2->
vertexAngle( curveIds.at( 1 ).second );
851 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
853 for (
auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
855 length += mCurves.at( it->first )->segmentLength( it->second );
863 for (
int i = mCurves.count() - 1; i >= 0; --i )
865 QgsCurve *reversedCurve = mCurves.at( i )->reversed();
876 double distanceTraversed = 0;
877 for (
const QgsCurve *curve : mCurves )
879 const double thisCurveLength = curve->
length();
880 if ( distanceTraversed + thisCurveLength > distance ||
qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
883 const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
886 return curve->interpolatePoint( distanceToPoint );
889 distanceTraversed += thisCurveLength;
897 if ( startDistance < 0 && endDistance < 0 )
900 endDistance = std::max( startDistance, endDistance );
901 std::unique_ptr< QgsCompoundCurve > substring = qgis::make_unique< QgsCompoundCurve >();
903 double distanceTraversed = 0;
904 for (
const QgsCurve *curve : mCurves )
906 const double thisCurveLength = curve->length();
907 if ( distanceTraversed + thisCurveLength < startDistance )
913 std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
915 substring->addCurve( part.release() );
918 distanceTraversed += thisCurveLength;
919 if ( distanceTraversed > endDistance )
923 return substring.release();
933 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
935 curve->addZValue( zValue );
948 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
950 curve->addMValue( mValue );
962 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
976 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
986 for (
QgsCurve *curve : qgis::as_const( mCurves ) )
bool isMeasure() const
Returns true if the geometry contains m values.
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
A rectangle specified with double values.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
int dimension() const override
Returns the inherent dimension of the geometry.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1, y1) to (x2, y2) and (x2, y2) to (x3, y3).
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
void swapXy() override
Swaps the x and y coordinates from the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
void clear() override
Clears the geometry, ie reset it to a null geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
QgsCompoundCurve * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex...
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
QgsWkbTypes::Type mWkbType
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
virtual double length() const
Returns the length of the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
Type
The WKB type describes the number of dimensions a geometry has.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
double length() const override
Returns the length of the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Utility class for identifying a unique vertex within a geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
QString geometryType() const override
Returns a unique string representing the geometry type.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
Abstract base class for curved geometry type.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
AxisOrder
Axis order for GML generation.
virtual bool isClosed() const
Returns true if the curve is closed.
void removeCurve(int i)
Removes a curve from the geometry.
int nCurves() const
Returns the number of curves in the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
bool isEmpty() const override
Returns true if the geometry is empty.
QVector< QgsPoint > QgsPointSequence
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
Line string geometry type, with support for z-dimension and m-values.
QgsCompoundCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
const QgsCurve * curveAt(int i) const
Returns the curve at the specified index.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Compound curve geometry type.
Circular string geometry type.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
void close()
Appends first point if not already closed.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
~QgsCompoundCurve() override
QgsCompoundCurve * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid...
bool dropMValue() override
Drops any measure values which exist in the geometry.
static Type flatType(Type type)
Returns the flat type for a WKB type.
QgsWkbTypes::Type readHeader() const
readHeader
int numPoints() const override
Returns the number of points in the curve.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negativ values mean left a...
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsPoint endPoint() const override
Returns the end point of the curve.
virtual bool fromWkb(QgsConstWkbPtr &wkb)=0
Sets the geometry from a WKB string.