/* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace OpenSearch.Client
{
///
/// Writes types to Well-Known Text (WKT)
///
public class GeoWKTWriter
{
///
/// Writes a to Well-Known Text (WKT)
///
public static string Write(IGeoShape shape) =>
shape == null ? null : Write(shape, new StringBuilder());
private static string Write(IGeoShape shape, StringBuilder builder)
{
switch (shape)
{
case IPointGeoShape point:
WritePoint(point, builder);
break;
case IMultiPointGeoShape multiPoint:
WriteMultiPoint(multiPoint, builder);
break;
case ILineStringGeoShape lineString:
WriteLineString(lineString, builder);
break;
case IMultiLineStringGeoShape multiLineString:
WriteMultiLineString(multiLineString, builder);
break;
case IPolygonGeoShape polygon:
WritePolygon(polygon, builder);
break;
case IMultiPolygonGeoShape multiPolygon:
WriteMultiPolygon(multiPolygon, builder);
break;
case IGeometryCollection geometryCollection:
WriteGeometryCollection(geometryCollection, builder);
break;
case IEnvelopeGeoShape envelope:
WriteEnvelope(envelope, builder);
break;
default:
throw new GeoWKTException($"Unknown geometry type: {shape.GetType().Name}");
}
return builder.ToString();
}
private static void WritePoint(IPointGeoShape point, StringBuilder builder)
{
builder.Append(GeoShapeType.Point).Append(" (");
WriteCoordinate(point.Coordinates, builder);
builder.Append(")");
}
private static void WriteMultiPoint(IMultiPointGeoShape multiPoint, StringBuilder builder)
{
builder.Append(GeoShapeType.MultiPoint).Append(" (");
WriteCoordinates(multiPoint.Coordinates, builder);
builder.Append(")");
}
private static void WriteLineString(ILineStringGeoShape lineString, StringBuilder builder)
{
builder.Append(GeoShapeType.LineString).Append(" (");
WriteCoordinates(lineString.Coordinates, builder);
builder.Append(")");
}
private static void WriteMultiLineString(IMultiLineStringGeoShape multiLineString, StringBuilder builder)
{
builder.Append(GeoShapeType.MultiLineString).Append(" ");
WriteCoordinatesList(multiLineString.Coordinates, builder);
}
private static void WritePolygon(IPolygonGeoShape polygon, StringBuilder builder)
{
builder.Append(GeoShapeType.Polygon).Append(" ");
WriteCoordinatesList(polygon.Coordinates, builder);
}
private static void WriteMultiPolygon(IMultiPolygonGeoShape multiPolygon, StringBuilder builder)
{
builder.Append(GeoShapeType.MultiPolygon).Append(" (");
var i = 0;
foreach (var polygon in multiPolygon.Coordinates)
{
if (i > 0)
builder.Append(", ");
WriteCoordinatesList(polygon, builder);
i++;
}
builder.Append(")");
}
private static void WriteGeometryCollection(IGeometryCollection geometryCollection, StringBuilder builder)
{
builder.Append(GeoShapeType.GeometryCollection).Append(" (");
var i = 0;
foreach (var shape in geometryCollection.Geometries)
{
if (i > 0)
builder.Append(", ");
Write(shape, builder);
i++;
}
builder.Append(")");
}
private static void WriteEnvelope(IEnvelopeGeoShape envelope, StringBuilder builder)
{
builder.Append(GeoShapeType.BoundingBox).Append(" (");
var topLeft = envelope.Coordinates.ElementAt(0);
var bottomRight = envelope.Coordinates.ElementAt(1);
// WKT specification expects the following order: minLon, maxLon, maxLat, minLat.
// envelope is top_left (minLon, maxLat), bottom_right (maxLon, minLat)
builder.Append(topLeft.Longitude.ToString(CultureInfo.InvariantCulture))
.Append(", ")
.Append(bottomRight.Longitude.ToString(CultureInfo.InvariantCulture))
.Append(", ")
.Append(topLeft.Latitude.ToString(CultureInfo.InvariantCulture))
.Append(", ")
.Append(bottomRight.Latitude.ToString(CultureInfo.InvariantCulture))
.Append(")");
}
private static void WriteCoordinatesList(IEnumerable> coordinates, StringBuilder builder)
{
builder.Append("(");
var i = 0;
foreach (var coordinateGroup in coordinates)
{
if (i > 0)
builder.Append(", ");
builder.Append("(");
WriteCoordinates(coordinateGroup, builder);
builder.Append(")");
i++;
}
builder.Append(")");
}
private static void WriteCoordinates(IEnumerable coordinates, StringBuilder builder)
{
var i = 0;
foreach (var coordinate in coordinates)
{
if (i > 0)
builder.Append(", ");
WriteCoordinate(coordinate, builder);
i++;
}
}
private static void WriteCoordinate(GeoCoordinate coordinate, StringBuilder builder)
{
builder.Append(coordinate.Longitude.ToString(CultureInfo.InvariantCulture))
.Append(" ")
.Append(coordinate.Latitude.ToString(CultureInfo.InvariantCulture));
if (coordinate.Z.HasValue)
builder.Append(" ").Append(coordinate.Z.Value.ToString(CultureInfo.InvariantCulture));
}
}
}