planetiler/planetiler-core/src/main/java/com/onthegomap/planetiler/reader/osm/OsmElement.java

180 wiersze
3.9 KiB
Java

package com.onthegomap.planetiler.reader.osm;
import com.carrotsearch.hppc.LongArrayList;
import com.onthegomap.planetiler.geo.GeoUtils;
import com.onthegomap.planetiler.reader.WithTags;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* An input element read from OpenStreetMap data.
*
* @see <a href="https://wiki.openstreetmap.org/wiki/Elements">OSM element data model</a>
*/
public interface OsmElement extends WithTags {
/** OSM element ID */
long id();
int cost();
enum Type {
NODE,
WAY,
RELATION
}
/** An un-handled element read from the .osm.pbf file (i.e. file header). */
record Other(
@Override long id,
@Override Map<String, Object> tags
) implements OsmElement {
@Override
public int cost() {
return 1 + tags.size();
}
}
/** A point on the earth's surface. */
final class Node implements OsmElement {
private static final long MISSING_LOCATION = Long.MIN_VALUE;
private final long id;
private final Map<String, Object> tags;
private final double lat;
private final double lon;
// bailed out of a record to make encodedLocation lazy since it is fairly expensive to compute
private long encodedLocation = MISSING_LOCATION;
public Node(
long id,
Map<String, Object> tags,
double lat,
double lon
) {
this.id = id;
this.tags = tags;
this.lat = lat;
this.lon = lon;
}
public Node(long id, double lat, double lon) {
this(id, new HashMap<>(), lat, lon);
}
@Override
public long id() {
return id;
}
@Override
public Map<String, Object> tags() {
return tags;
}
public double lat() {
return lat;
}
public double lon() {
return lon;
}
public long encodedLocation() {
if (encodedLocation == MISSING_LOCATION) {
encodedLocation = GeoUtils.encodeFlatLocation(lon, lat);
}
return encodedLocation;
}
@Override
public int cost() {
return 1 + tags.size();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
var that = (Node) obj;
return this.id == that.id &&
Objects.equals(this.tags, that.tags) &&
Double.doubleToLongBits(this.lat) == Double.doubleToLongBits(that.lat) &&
Double.doubleToLongBits(this.lon) == Double.doubleToLongBits(that.lon);
}
@Override
public int hashCode() {
return Objects.hash(id, tags, lat, lon);
}
@Override
public String toString() {
return "Node[" +
"id=" + id + ", " +
"tags=" + tags + ", " +
"lat=" + lat + ", " +
"lon=" + lon + ']';
}
}
/** An ordered list of 2-2,000 nodes that define a polyline. */
record Way(
@Override long id,
@Override Map<String, Object> tags,
LongArrayList nodes
) implements OsmElement {
public Way(long id) {
this(id, new HashMap<>(), new LongArrayList(5));
}
@Override
public int cost() {
return 1 + tags.size() + nodes.size();
}
}
/** An ordered list of nodes, ways, and other relations. */
record Relation(
@Override long id,
@Override Map<String, Object> tags,
List<Member> members
) implements OsmElement {
public Relation(long id) {
this(id, new HashMap<>(), new ArrayList<>());
}
public Relation {
if (members == null) {
members = Collections.emptyList();
}
}
@Override
public int cost() {
return 1 + tags.size() + members.size() * 3;
}
/**
* A node, way, or relation contained in a relation with an optional "role" to clarify the purpose of each member.
*/
public record Member(
Type type,
long ref,
String role
) {}
}
}