///|
pub fn to_geojson_point(point : Point) -> Result[String, RouteError] {
match validate_point(point, 0) {
Ok(_) => ()
Err(err) => return Err(err)
}
let out = StringBuilder(size_hint=64)
out.write_string("{\"type\":\"Point\",\"coordinates\":")
write_point_coordinate(out, point)
out.write_char('}')
Ok(out.to_string())
}
///|
pub fn to_geojson_multipoint(
points : ArrayView[Point],
) -> Result[String, RouteError] {
match require_points(points) {
Ok(_) => ()
Err(err) => return Err(err)
}
let out = StringBuilder(size_hint=points.length() * 32)
out.write_string("{\"type\":\"MultiPoint\",\"coordinates\":")
write_coordinates(out, points)
out.write_char('}')
Ok(out.to_string())
}
///|
pub fn to_geojson_bbox_polygon(bbox : BBox) -> Result[String, RouteError] {
let polygon = bbox_polygon_points(bbox)
let out = StringBuilder(size_hint=160)
out.write_string("{\"type\":\"Polygon\",\"coordinates\":[")
write_coordinates(out, polygon)
out.write_string("]}")
Ok(out.to_string())
}
///|
pub fn to_geojson_bbox_feature(
bbox : BBox,
name? : String = "bbox",
) -> Result[String, RouteError] {
let polygon = bbox_polygon_points(bbox)
let out = StringBuilder(size_hint=180 + name.length())
out.write_string("{\"type\":\"Feature\",\"properties\":{\"name\":\"")
out.write_string(escape_json_string(name))
out.write_string("\"},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[")
write_coordinates(out, polygon)
out.write_string("]}}")
Ok(out.to_string())
}
///|
pub fn routes_to_geojson_feature_collection(
routes : ArrayView[Array[Point]],
name_prefix? : String = "route",
) -> Result[String, RouteError] {
if routes.length() == 0 {
return Err(EmptyRoute)
}
let out = StringBuilder(size_hint=routes.length() * 128)
out.write_string("{\"type\":\"FeatureCollection\",\"features\":[")
for i, route in routes {
if i > 0 {
out.write_char(',')
}
match require_points(route) {
Ok(_) => ()
Err(err) => return Err(err)
}
out.write_string("{\"type\":\"Feature\",\"properties\":{\"name\":\"")
out.write_string(escape_json_string("\{name_prefix}-\{i + 1}"))
out.write_string("\",\"point_count\":")
out.write_string(route.length().to_string())
out.write_string("},\"geometry\":{\"type\":\"LineString\",\"coordinates\":")
write_coordinates(out, route)
out.write_string("}}")
}
out.write_string("]}")
Ok(out.to_string())
}
///|
pub fn samples_to_geojson_feature_collection(
samples : ArrayView[RouteSample],
name? : String = "samples",
) -> Result[String, RouteError] {
if samples.length() == 0 {
return Err(EmptyRoute)
}
let out = StringBuilder(size_hint=samples.length() * 120 + name.length())
out.write_string("{\"type\":\"FeatureCollection\",\"name\":\"")
out.write_string(escape_json_string(name))
out.write_string("\",\"features\":[")
for i, sample in samples {
if i > 0 {
out.write_char(',')
}
out.write_string("{\"type\":\"Feature\",\"properties\":{\"distance_m\":")
out.write_string(round_meters(sample.distance_m).to_string())
out.write_string(",\"segment_index\":")
out.write_string(sample.segment_index.to_string())
out.write_string(",\"fraction\":")
out.write_string(sample.fraction.to_string())
out.write_string("},\"geometry\":{\"type\":\"Point\",\"coordinates\":")
write_point_coordinate(out, sample.point)
out.write_string("}}")
}
out.write_string("]}")
Ok(out.to_string())
}
///|
pub fn nearest_points_to_geojson_feature_collection(
points : ArrayView[NearestPoint],
name? : String = "nearest-points",
) -> Result[String, RouteError] {
if points.length() == 0 {
return Err(EmptyRoute)
}
let out = StringBuilder(size_hint=points.length() * 140 + name.length())
out.write_string("{\"type\":\"FeatureCollection\",\"name\":\"")
out.write_string(escape_json_string(name))
out.write_string("\",\"features\":[")
for i, item in points {
if i > 0 {
out.write_char(',')
}
out.write_string(
"{\"type\":\"Feature\",\"properties\":{\"distance_to_route_m\":",
)
out.write_string(round_meters(item.distance_to_route_m).to_string())
out.write_string(",\"distance_along_route_m\":")
out.write_string(round_meters(item.distance_along_route_m).to_string())
out.write_string(",\"segment_index\":")
out.write_string(item.segment_index.to_string())
out.write_string("},\"geometry\":{\"type\":\"Point\",\"coordinates\":")
write_point_coordinate(out, item.point)
out.write_string("}}")
}
out.write_string("]}")
Ok(out.to_string())
}
///|
pub fn tiles_to_geojson_feature_collection(
tiles : ArrayView[Tile],
name? : String = "tiles",
) -> Result[String, RouteError] {
if tiles.length() == 0 {
return Err(EmptyRoute)
}
let out = StringBuilder(size_hint=tiles.length() * 200 + name.length())
out.write_string("{\"type\":\"FeatureCollection\",\"name\":\"")
out.write_string(escape_json_string(name))
out.write_string("\",\"features\":[")
for i, tile in tiles {
if i > 0 {
out.write_char(',')
}
let bounds = match tile_bounds(tile) {
Ok(value) => value
Err(err) => return Err(err)
}
out.write_string("{\"type\":\"Feature\",\"properties\":{\"tile\":\"")
out.write_string(tile.key())
out.write_string("\"},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[")
write_coordinates(out, bbox_polygon_points(bounds.to_bbox()))
out.write_string("]}}")
}
out.write_string("]}")
Ok(out.to_string())
}
///|
pub fn route_report_geojson(
route : ArrayView[Point],
zoom? : Int = 12,
name? : String = "route",
) -> Result[String, RouteError] {
match require_points(route) {
Ok(_) => ()
Err(err) => return Err(err)
}
let summary = match summarize_route(route, zoom~) {
Ok(value) => value
Err(err) => return Err(err)
}
let out = StringBuilder(size_hint=route.length() * 64 + 260 + name.length())
out.write_string("{\"type\":\"Feature\",\"properties\":{\"name\":\"")
out.write_string(escape_json_string(name))
out.write_string("\",\"point_count\":")
out.write_string(summary.point_count.to_string())
out.write_string(",\"distance_m\":")
out.write_string(round_meters(summary.distance_m).to_string())
out.write_string(",\"simplified_point_count\":")
out.write_string(summary.simplified_point_count.to_string())
out.write_string(",\"encoded_polyline\":\"")
out.write_string(escape_json_string(summary.encoded_polyline))
out.write_string("\",\"tile_count\":")
out.write_string(summary.tiles.length().to_string())
out.write_string("},\"geometry\":{\"type\":\"LineString\",\"coordinates\":")
write_coordinates(out, route)
out.write_string("}}")
Ok(out.to_string())
}
///|
pub fn route_debug_bundle_geojson(
route : ArrayView[Point],
zoom? : Int = 12,
) -> Result[String, RouteError] {
match require_points(route) {
Ok(_) => ()
Err(err) => return Err(err)
}
let samples = match sample_route_by_count(route, 5) {
Ok(value) => value
Err(err) => return Err(err)
}
let tiles = match tile_cover(route, zoom) {
Ok(value) => value
Err(err) => return Err(err)
}
let bbox = match bounding_box(route) {
Ok(value) => value
Err(err) => return Err(err)
}
let out = StringBuilder(size_hint=2048)
out.write_string("{\"type\":\"FeatureCollection\",\"features\":[")
let route_feature = match route_report_geojson(route, zoom~, name="route") {
Ok(value) => value
Err(err) => return Err(err)
}
out.write_string(route_feature)
out.write_char(',')
let bbox_feature = match to_geojson_bbox_feature(bbox, name="bbox") {
Ok(value) => value
Err(err) => return Err(err)
}
out.write_string(bbox_feature)
for sample in samples {
out.write_char(',')
out.write_string(
"{\"type\":\"Feature\",\"properties\":{\"kind\":\"sample\",\"distance_m\":",
)
out.write_string(round_meters(sample.distance_m).to_string())
out.write_string("},\"geometry\":{\"type\":\"Point\",\"coordinates\":")
write_point_coordinate(out, sample.point)
out.write_string("}}")
}
for tile in tiles {
let bounds = match tile_bounds(tile) {
Ok(value) => value
Err(err) => return Err(err)
}
out.write_char(',')
out.write_string(
"{\"type\":\"Feature\",\"properties\":{\"kind\":\"tile\",\"tile\":\"",
)
out.write_string(tile.key())
out.write_string("\"},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[")
write_coordinates(out, bbox_polygon_points(bounds.to_bbox()))
out.write_string("]}}")
}
out.write_string("]}")
Ok(out.to_string())
}
///|
fn bbox_polygon_points(bbox : BBox) -> Array[Point] {
[
Point(bbox.min_lat, bbox.min_lon),
Point(bbox.min_lat, bbox.max_lon),
Point(bbox.max_lat, bbox.max_lon),
Point(bbox.max_lat, bbox.min_lon),
Point(bbox.min_lat, bbox.min_lon),
]
}
///|
fn write_point_coordinate(out : StringBuilder, point : Point) -> Unit {
out.write_char('[')
out.write_string(point.lon.to_string())
out.write_char(',')
out.write_string(point.lat.to_string())
out.write_char(']')
}