org.openstreetmap.atlas.geography.atlas.packed.PackedAtlas Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlas Show documentation
Show all versions of atlas Show documentation
"Library to load OSM data into an Atlas format"
package org.openstreetmap.atlas.geography.atlas.packed;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.openstreetmap.atlas.exception.CoreException;
import org.openstreetmap.atlas.geography.Location;
import org.openstreetmap.atlas.geography.Longitude;
import org.openstreetmap.atlas.geography.PolyLine;
import org.openstreetmap.atlas.geography.Polygon;
import org.openstreetmap.atlas.geography.Rectangle;
import org.openstreetmap.atlas.geography.atlas.AbstractAtlas;
import org.openstreetmap.atlas.geography.atlas.Atlas;
import org.openstreetmap.atlas.geography.atlas.AtlasMetaData;
import org.openstreetmap.atlas.geography.atlas.builder.AtlasSize;
import org.openstreetmap.atlas.geography.atlas.exception.AtlasIntegrityException;
import org.openstreetmap.atlas.geography.atlas.items.Area;
import org.openstreetmap.atlas.geography.atlas.items.AtlasEntity;
import org.openstreetmap.atlas.geography.atlas.items.Edge;
import org.openstreetmap.atlas.geography.atlas.items.ItemType;
import org.openstreetmap.atlas.geography.atlas.items.Line;
import org.openstreetmap.atlas.geography.atlas.items.Node;
import org.openstreetmap.atlas.geography.atlas.items.Point;
import org.openstreetmap.atlas.geography.atlas.items.Relation;
import org.openstreetmap.atlas.geography.atlas.items.RelationMember;
import org.openstreetmap.atlas.geography.atlas.items.RelationMemberList;
import org.openstreetmap.atlas.streaming.resource.Resource;
import org.openstreetmap.atlas.streaming.resource.WritableResource;
import org.openstreetmap.atlas.utilities.arrays.ByteArrayOfArrays;
import org.openstreetmap.atlas.utilities.arrays.IntegerArrayOfArrays;
import org.openstreetmap.atlas.utilities.arrays.LongArray;
import org.openstreetmap.atlas.utilities.arrays.LongArrayOfArrays;
import org.openstreetmap.atlas.utilities.arrays.PolyLineArray;
import org.openstreetmap.atlas.utilities.arrays.PolygonArray;
import org.openstreetmap.atlas.utilities.collections.Iterables;
import org.openstreetmap.atlas.utilities.collections.MultiIterable;
import org.openstreetmap.atlas.utilities.compression.IntegerDictionary;
import org.openstreetmap.atlas.utilities.maps.LongToLongMap;
import org.openstreetmap.atlas.utilities.maps.LongToLongMultiMap;
import org.openstreetmap.atlas.utilities.scalars.Distance;
import org.openstreetmap.atlas.utilities.time.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Atlas that packs data in large arrays
*
* @author matthieun
*/
public final class PackedAtlas extends AbstractAtlas
{
/**
* Serialization format settings for an {@link Atlas}. While the serialization interface for
* saving is well-defined by the {@link Atlas}, the actual serialization mechanics - as well as
* the interface for loading - are left up to the discretion of the implementing {@link Atlas}
* subclass.
*
* @author lcram
*/
public enum AtlasSerializationFormat
{
PROTOBUF,
JAVA
}
private static final long serialVersionUID = -7582554057580336684L;
private static final Logger logger = LoggerFactory.getLogger(PackedAtlas.class);
// Keep track of the field names for reflection code in the Serializer.
protected static final String FIELD_PREFIX = "FIELD_";
protected static final String FIELD_BOUNDS = "bounds";
protected static final String FIELD_LOGGER = "logger";
protected static final String FIELD_SERIAL_VERSION_UID = "serialVersionUID";
protected static final String FIELD_SERIALIZER = "serializer";
protected static final String FIELD_SAVE_SERIALIZATION_FORMAT = "saveSerializationFormat";
protected static final String FIELD_LOAD_SERIALIZATION_FORMAT = "loadSerializationFormat";
protected static final String FIELD_META_DATA = "metaData";
protected static final String FIELD_DICTIONARY = "dictionary";
protected static final Object FIELD_DICTIONARY_LOCK = new Object();
protected static final String FIELD_EDGE_IDENTIFIERS = "edgeIdentifiers";
protected static final Object FIELD_EDGE_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_NODE_IDENTIFIERS = "nodeIdentifiers";
protected static final Object FIELD_NODE_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_AREA_IDENTIFIERS = "areaIdentifiers";
protected static final Object FIELD_AREA_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_LINE_IDENTIFIERS = "lineIdentifiers";
protected static final Object FIELD_LINE_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_POINT_IDENTIFIERS = "pointIdentifiers";
protected static final Object FIELD_POINT_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_RELATION_IDENTIFIERS = "relationIdentifiers";
protected static final Object FIELD_RELATION_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX = "edgeIdentifierToEdgeArrayIndex";
protected static final Object FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX_LOCK = new Object();
protected static final String FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX = "nodeIdentifierToNodeArrayIndex";
protected static final Object FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX_LOCK = new Object();
protected static final String FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX = "areaIdentifierToAreaArrayIndex";
protected static final Object FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX_LOCK = new Object();
protected static final String FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX = "lineIdentifierToLineArrayIndex";
protected static final Object FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX_LOCK = new Object();
protected static final String FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX = "pointIdentifierToPointArrayIndex";
protected static final Object FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX_LOCK = new Object();
protected static final String FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX = "relationIdentifierToRelationArrayIndex";
protected static final Object FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX_LOCK = new Object();
protected static final String FIELD_NODE_LOCATIONS = "nodeLocations";
protected static final Object FIELD_NODE_LOCATIONS_LOCK = new Object();
protected static final String FIELD_NODE_IN_EDGES_INDICES = "nodeInEdgesIndices";
protected static final Object FIELD_NODE_IN_EDGES_INDICES_LOCK = new Object();
protected static final String FIELD_NODE_OUT_EDGES_INDICES = "nodeOutEdgesIndices";
protected static final Object FIELD_NODE_OUT_EDGES_INDICES_LOCK = new Object();
protected static final String FIELD_NODE_TAGS = "nodeTags";
protected static final Object FIELD_NODE_TAGS_LOCK = new Object();
protected static final String FIELD_NODE_INDEX_TO_RELATION_INDICES = "nodeIndexToRelationIndices";
protected static final Object FIELD_NODE_INDEX_TO_RELATION_INDICES_LOCK = new Object();
protected static final String FIELD_EDGE_START_NODE_INDEX = "edgeStartNodeIndex";
protected static final Object FIELD_EDGE_START_NODE_INDEX_LOCK = new Object();
protected static final String FIELD_EDGE_END_NODE_INDEX = "edgeEndNodeIndex";
protected static final Object FIELD_EDGE_END_NODE_INDEX_LOCK = new Object();
protected static final String FIELD_EDGE_POLY_LINES = "edgePolyLines";
protected static final Object FIELD_EDGE_POLY_LINES_LOCK = new Object();
protected static final String FIELD_EDGE_TAGS = "edgeTags";
protected static final Object FIELD_EDGE_TAGS_LOCK = new Object();
protected static final String FIELD_EDGE_INDEX_TO_RELATION_INDICES = "edgeIndexToRelationIndices";
protected static final Object FIELD_EDGE_INDEX_TO_RELATION_INDICES_LOCK = new Object();
protected static final String FIELD_AREA_POLYGONS = "areaPolygons";
protected static final Object FIELD_AREA_POLYGONS_LOCK = new Object();
protected static final String FIELD_AREA_TAGS = "areaTags";
protected static final Object FIELD_AREA_TAGS_LOCK = new Object();
protected static final String FIELD_AREA_INDEX_TO_RELATION_INDICES = "areaIndexToRelationIndices";
protected static final Object FIELD_AREA_INDEX_TO_RELATION_INDICES_LOCK = new Object();
protected static final String FIELD_LINE_POLYLINES = "linePolyLines";
protected static final Object FIELD_LINE_POLYLINES_LOCK = new Object();
protected static final String FIELD_LINE_TAGS = "lineTags";
protected static final Object FIELD_LINE_TAGS_LOCK = new Object();
protected static final String FIELD_LINE_INDEX_TO_RELATION_INDICES = "lineIndexToRelationIndices";
protected static final Object FIELD_LINE_INDEX_TO_RELATION_INDICES_LOCK = new Object();
protected static final String FIELD_POINT_LOCATIONS = "pointLocations";
protected static final Object FIELD_POINT_LOCATIONS_LOCK = new Object();
protected static final String FIELD_POINT_TAGS = "pointTags";
protected static final Object FIELD_POINT_TAGS_LOCK = new Object();
protected static final String FIELD_POINT_INDEX_TO_RELATION_INDICES = "pointIndexToRelationIndices";
protected static final Object FIELD_POINT_INDEX_TO_RELATION_INDICES_LOCK = new Object();
protected static final String FIELD_RELATION_MEMBERS_INDICES = "relationMemberIndices";
protected static final Object FIELD_RELATION_MEMBERS_INDICES_LOCK = new Object();
protected static final String FIELD_RELATION_MEMBER_TYPES = "relationMemberTypes";
protected static final Object FIELD_RELATION_MEMBER_TYPES_LOCK = new Object();
protected static final String FIELD_RELATION_MEMBER_ROLES = "relationMemberRoles";
protected static final Object FIELD_RELATION_MEMBER_ROLES_LOCK = new Object();
protected static final String FIELD_RELATION_TAGS = "relationTags";
protected static final Object FIELD_RELATION_TAGS_LOCK = new Object();
protected static final String FIELD_RELATION_INDEX_TO_RELATION_INDICES = "relationIndexToRelationIndices";
protected static final Object FIELD_RELATION_INDEX_TO_RELATION_INDICES_LOCK = new Object();
protected static final String FIELD_RELATION_OSM_IDENTIFIER_TO_RELATION_IDENTIFIERS = "relationOsmIdentifierToRelationIdentifiers";
protected static final Object FIELD_RELATION_OSM_IDENTIFIER_TO_RELATION_IDENTIFIERS_LOCK = new Object();
protected static final String FIELD_RELATION_OSM_IDENTIFIERS = "relationOsmIdentifiers";
protected static final Object FIELD_RELATION_OSM_IDENTIFIERS_LOCK = new Object();
// Serializer.
private transient PackedAtlasSerializer serializer;
// Serialization formats for saving/loading this PackedAtlas
private AtlasSerializationFormat saveSerializationFormat = AtlasSerializationFormat.PROTOBUF;
private AtlasSerializationFormat loadSerializationFormat = AtlasSerializationFormat.PROTOBUF;
// Meta-Data
private AtlasMetaData metaData = new AtlasMetaData();
// Dictionary
private final IntegerDictionary dictionary;
// The OSM (and way-sectioned) edge and node indices
private final LongArray edgeIdentifiers;
private final LongArray nodeIdentifiers;
private final LongArray areaIdentifiers;
private final LongArray lineIdentifiers;
private final LongArray pointIdentifiers;
private final LongArray relationIdentifiers;
// The maps from edge index to index in the arrays above, and in the attributes
private final LongToLongMap edgeIdentifierToEdgeArrayIndex;
private final LongToLongMap nodeIdentifierToNodeArrayIndex;
private final LongToLongMap areaIdentifierToAreaArrayIndex;
private final LongToLongMap lineIdentifierToLineArrayIndex;
private final LongToLongMap pointIdentifierToPointArrayIndex;
private final LongToLongMap relationIdentifierToRelationArrayIndex;
// Node attributes
private final LongArray nodeLocations;
private final LongArrayOfArrays nodeInEdgesIndices;
private final LongArrayOfArrays nodeOutEdgesIndices;
private final PackedTagStore nodeTags;
private final LongToLongMultiMap nodeIndexToRelationIndices;
// Edge attributes
private final LongArray edgeStartNodeIndex;
private final LongArray edgeEndNodeIndex;
private final PolyLineArray edgePolyLines;
private final PackedTagStore edgeTags;
private final LongToLongMultiMap edgeIndexToRelationIndices;
// Areas attributes
private final PolygonArray areaPolygons;
private final PackedTagStore areaTags;
private final LongToLongMultiMap areaIndexToRelationIndices;
// Line attributes
private final PolyLineArray linePolyLines;
private final PackedTagStore lineTags;
private final LongToLongMultiMap lineIndexToRelationIndices;
// Point attributes
private final LongArray pointLocations;
private final PackedTagStore pointTags;
private final LongToLongMultiMap pointIndexToRelationIndices;
// Relation attributes
private final LongArrayOfArrays relationMemberIndices;
private final ByteArrayOfArrays relationMemberTypes;
private final IntegerArrayOfArrays relationMemberRoles;
private final PackedTagStore relationTags;
private final LongToLongMultiMap relationIndexToRelationIndices;
private final LongToLongMultiMap relationOsmIdentifierToRelationIdentifiers;
private final LongArray relationOsmIdentifiers;
// Bounds of the Atlas
private Rectangle bounds;
/**
* Clone an {@link Atlas} into a {@link PackedAtlas}
*
* @param other
* The {@link Atlas} to clone
* @return The cloned {@link PackedAtlas}
*/
public static PackedAtlas cloneFrom(final Atlas other)
{
return new PackedAtlasCloner().cloneFrom(other);
}
/**
* Load a {@link PackedAtlas} from a zip entry resource
*
* @param resource
* The {@link Resource} to read from
* @return The deserialized {@link PackedAtlas}
*/
public static PackedAtlas load(final Resource resource)
{
final PackedAtlas result = PackedAtlasSerializer.load(resource);
result.setName(resource.getName());
return result;
}
/**
* This constructor is used only by the serializer.
*/
protected PackedAtlas()
{
this.metaData = null;
this.dictionary = null;
this.edgeIdentifiers = null;
this.nodeIdentifiers = null;
this.areaIdentifiers = null;
this.lineIdentifiers = null;
this.pointIdentifiers = null;
this.relationIdentifiers = null;
this.edgeIdentifierToEdgeArrayIndex = null;
this.nodeIdentifierToNodeArrayIndex = null;
this.areaIdentifierToAreaArrayIndex = null;
this.lineIdentifierToLineArrayIndex = null;
this.pointIdentifierToPointArrayIndex = null;
this.relationIdentifierToRelationArrayIndex = null;
this.nodeLocations = null;
this.nodeInEdgesIndices = null;
this.nodeOutEdgesIndices = null;
this.nodeTags = null;
this.nodeIndexToRelationIndices = null;
this.edgeStartNodeIndex = null;
this.edgeEndNodeIndex = null;
this.edgePolyLines = null;
this.edgeTags = null;
this.edgeIndexToRelationIndices = null;
this.areaPolygons = null;
this.areaTags = null;
this.areaIndexToRelationIndices = null;
this.linePolyLines = null;
this.lineTags = null;
this.lineIndexToRelationIndices = null;
this.pointLocations = null;
this.pointTags = null;
this.pointIndexToRelationIndices = null;
this.relationMemberIndices = null;
this.relationMemberTypes = null;
this.relationMemberRoles = null;
this.relationTags = null;
this.relationIndexToRelationIndices = null;
this.relationOsmIdentifierToRelationIdentifiers = null;
this.relationOsmIdentifiers = null;
}
/**
* Construct an Atlas
*
* @param estimates
* The size estimates
*/
protected PackedAtlas(final AtlasSize estimates)
{
final long edgeNumberEstimate = estimates.getEdgeNumber();
final long nodeNumberEstimate = estimates.getNodeNumber();
final long areaNumberEstimate = estimates.getAreaNumber();
final long lineNumberEstimate = estimates.getLineNumber();
final long pointNumberEstimate = estimates.getPointNumber();
final long relationNumberEstimate = estimates.getRelationNumber();
final int subArraySize = Integer.MAX_VALUE;
final long maximumSize = Long.MAX_VALUE;
final int edgeMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,
edgeNumberEstimate % Integer.MAX_VALUE);
final int nodeMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,
nodeNumberEstimate % Integer.MAX_VALUE);
final int areaMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,
areaNumberEstimate % Integer.MAX_VALUE);
final int lineMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,
lineNumberEstimate % Integer.MAX_VALUE);
final int pointMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,
pointNumberEstimate % Integer.MAX_VALUE);
final int relationMemoryBlockSize = (int) Math.max(DEFAULT_NUMBER_OF_ITEMS,
relationNumberEstimate % Integer.MAX_VALUE);
final int edgeHashSize = (int) Math
.max(Math.min(edgeNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);
final int nodeHashSize = (int) Math
.max(Math.min(nodeNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);
final int areaHashSize = (int) Math
.max(Math.min(areaNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);
final int lineHashSize = (int) Math
.max(Math.min(lineNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);
final int pointHashSize = (int) Math
.max(Math.min(pointNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);
final int relationHashSize = (int) Math
.max(Math.min(relationNumberEstimate / HASH_MODULO_RATIO, Integer.MAX_VALUE), 1);
this.dictionary = new IntegerDictionary<>();
this.edgeIdentifiers = new LongArray(maximumSize, edgeMemoryBlockSize, subArraySize);
this.nodeIdentifiers = new LongArray(maximumSize, nodeMemoryBlockSize, subArraySize);
this.areaIdentifiers = new LongArray(maximumSize, areaMemoryBlockSize, subArraySize);
this.lineIdentifiers = new LongArray(maximumSize, lineMemoryBlockSize, subArraySize);
this.pointIdentifiers = new LongArray(maximumSize, pointMemoryBlockSize, subArraySize);
this.relationIdentifiers = new LongArray(maximumSize, relationMemoryBlockSize,
subArraySize);
this.edgeIdentifierToEdgeArrayIndex = new LongToLongMap(
"PackedAtlas - edgeIdentifierToEdgeArrayIndex", maximumSize, edgeHashSize,
edgeMemoryBlockSize, subArraySize, edgeMemoryBlockSize, subArraySize);
this.nodeIdentifierToNodeArrayIndex = new LongToLongMap(
"PackedAtlas - nodeIdentifierToNodeArrayIndex", maximumSize, nodeHashSize,
nodeMemoryBlockSize, subArraySize, nodeMemoryBlockSize, subArraySize);
this.areaIdentifierToAreaArrayIndex = new LongToLongMap(
"PackedAtlas - areaIdentifierToAreaArrayIndex", maximumSize, areaHashSize,
areaMemoryBlockSize, subArraySize, areaMemoryBlockSize, subArraySize);
this.lineIdentifierToLineArrayIndex = new LongToLongMap(
"PackedAtlas - lineIdentifierToLineArrayIndex", maximumSize, lineHashSize,
lineMemoryBlockSize, subArraySize, lineMemoryBlockSize, subArraySize);
this.pointIdentifierToPointArrayIndex = new LongToLongMap(
"PackedAtlas - pointIdentifierToPointArrayIndex", maximumSize, pointHashSize,
pointMemoryBlockSize, subArraySize, pointMemoryBlockSize, subArraySize);
this.relationIdentifierToRelationArrayIndex = new LongToLongMap(
"PackedAtlas - relationIdentifierToRelationArrayIndex", maximumSize,
relationHashSize, relationMemoryBlockSize, subArraySize, relationMemoryBlockSize,
subArraySize);
this.nodeInEdgesIndices = new LongArrayOfArrays(subArraySize, nodeMemoryBlockSize,
subArraySize);
this.nodeOutEdgesIndices = new LongArrayOfArrays(subArraySize, nodeMemoryBlockSize,
subArraySize);
this.nodeLocations = new LongArray(maximumSize, nodeMemoryBlockSize, subArraySize);
this.nodeTags = new PackedTagStore(maximumSize, nodeMemoryBlockSize, subArraySize,
dictionary());
this.nodeIndexToRelationIndices = new LongToLongMultiMap(
"PackedAtlas - nodeIndexToRelationIndices", maximumSize, nodeHashSize,
nodeMemoryBlockSize, subArraySize, nodeMemoryBlockSize, nodeHashSize);
this.edgeStartNodeIndex = new LongArray(maximumSize, edgeMemoryBlockSize, subArraySize);
this.edgeEndNodeIndex = new LongArray(maximumSize, edgeMemoryBlockSize, subArraySize);
this.edgePolyLines = new PolyLineArray(maximumSize, edgeMemoryBlockSize, subArraySize);
this.edgeTags = new PackedTagStore(maximumSize, edgeMemoryBlockSize, subArraySize,
dictionary());
this.edgeIndexToRelationIndices = new LongToLongMultiMap(
"PackedAtlas - edgeIndexToRelationIndices", maximumSize, edgeHashSize,
edgeMemoryBlockSize, subArraySize, edgeMemoryBlockSize, edgeHashSize);
this.areaPolygons = new PolygonArray(maximumSize, areaMemoryBlockSize, subArraySize);
this.areaTags = new PackedTagStore(maximumSize, areaMemoryBlockSize, subArraySize,
dictionary());
this.areaIndexToRelationIndices = new LongToLongMultiMap(
"PackedAtlas - areaIndexToRelationIndices", maximumSize, areaHashSize,
areaMemoryBlockSize, subArraySize, areaMemoryBlockSize, areaHashSize);
this.linePolyLines = new PolyLineArray(maximumSize, lineMemoryBlockSize, subArraySize);
this.lineTags = new PackedTagStore(maximumSize, lineMemoryBlockSize, subArraySize,
dictionary());
this.lineIndexToRelationIndices = new LongToLongMultiMap(
"PackedAtlas - lineIndexToRelationIndices", maximumSize, lineHashSize,
lineMemoryBlockSize, subArraySize, lineMemoryBlockSize, lineHashSize);
this.pointLocations = new LongArray(maximumSize, pointMemoryBlockSize, subArraySize);
this.pointTags = new PackedTagStore(maximumSize, pointMemoryBlockSize, subArraySize,
dictionary());
this.pointIndexToRelationIndices = new LongToLongMultiMap(
"PackedAtlas - pointIndexToRelationIndices", maximumSize, pointHashSize,
pointMemoryBlockSize, subArraySize, pointMemoryBlockSize, pointHashSize);
this.relationMemberIndices = new LongArrayOfArrays(maximumSize, relationMemoryBlockSize,
subArraySize);
this.relationMemberTypes = new ByteArrayOfArrays(maximumSize, relationMemoryBlockSize,
subArraySize);
this.relationMemberRoles = new IntegerArrayOfArrays(maximumSize, relationMemoryBlockSize,
subArraySize);
this.relationTags = new PackedTagStore(maximumSize, relationMemoryBlockSize, subArraySize,
dictionary());
this.relationIndexToRelationIndices = new LongToLongMultiMap(
"PackedAtlas - relationIndexToRelationIndices", maximumSize, relationHashSize,
relationMemoryBlockSize, subArraySize, relationMemoryBlockSize, relationHashSize);
this.relationOsmIdentifierToRelationIdentifiers = new LongToLongMultiMap(
"PackedAtlas - relationOsmIdentifierToRelationIdentifier", maximumSize,
relationHashSize, relationMemoryBlockSize, subArraySize, relationMemoryBlockSize,
subArraySize);
this.relationOsmIdentifiers = new LongArray(maximumSize, relationMemoryBlockSize,
subArraySize);
this.edgeIdentifiers.setName("PackedAtlas - edgeIdentifiers");
this.edgeStartNodeIndex.setName("PackedAtlas - edgeStartNodeIndex");
this.edgeEndNodeIndex.setName("PackedAtlas - edgeEndNodeIndex");
this.edgePolyLines.setName("PackedAtlas - edgePolyLines");
this.nodeIdentifiers.setName("PackedAtlas - nodeIdentifiers");
this.nodeInEdgesIndices.setName("PackedAtlas - nodeInEdgesIndices");
this.nodeOutEdgesIndices.setName("PackedAtlas - nodeOutEdgesIndices");
this.nodeLocations.setName("PackedAtlas - nodeLocations");
this.areaIdentifiers.setName("PackedAtlas - areaIdentifiers");
this.areaPolygons.setName("PackedAtlas - areaPolygons");
this.lineIdentifiers.setName("PackedAtlas - lineIdentifiers");
this.linePolyLines.setName("PackedAtlas - linePolyLines");
this.pointIdentifiers.setName("PackedAtlas - pointIdentifiers");
this.pointLocations.setName("PackedAtlas - pointLocations");
this.relationIdentifiers.setName("PackedAtlas - relationIdentifiers");
this.relationMemberIndices.setName("PackedAtlas - relationMemberIndices");
this.relationMemberTypes.setName("PackedAtlas - relationMemberTypes");
this.relationMemberRoles.setName("PackedAtlas - relationMemberRoles");
this.relationOsmIdentifiers.setName("PackedAtlas - relationOsmIdentifiers");
}
@Override
public Area area(final long identifier)
{
if (this.areaIdentifierToAreaArrayIndex().containsKey(identifier))
{
return new PackedArea(this, this.areaIdentifierToAreaArrayIndex().get(identifier));
}
return null;
}
@Override
public Iterable areas()
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < PackedAtlas.this.areaIdentifiers().size();
}
@Override
public Area next()
{
return new PackedArea(PackedAtlas.this, this.index++);
}
};
}
@Override
public Rectangle bounds()
{
if (this.bounds == null)
{
final Iterable boundedEntities = Iterables.filter(this,
entity -> entity.bounds() != null);
this.bounds = Rectangle.forLocated(boundedEntities);
}
return this.bounds;
}
@Override
public Edge edge(final long identifier)
{
if (this.edgeIdentifierToEdgeArrayIndex().containsKey(identifier))
{
return new PackedEdge(this, this.edgeIdentifierToEdgeArrayIndex().get(identifier));
}
return null;
}
@Override
public Iterable edges()
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < PackedAtlas.this.edgeIdentifiers().size();
}
@Override
public Edge next()
{
return new PackedEdge(PackedAtlas.this, this.index++);
}
};
}
/**
* Get the serialization format used for saving this {@link PackedAtlas}. By default use Java
* serialization.
*
* @return The serialization format setting
*/
public AtlasSerializationFormat getSaveSerializationFormat()
{
return this.saveSerializationFormat;
}
/**
* Get the serialization format with which this atlas was loaded.
*
* @return the format
*/
public AtlasSerializationFormat getSerializationFormat()
{
return this.loadSerializationFormat;
}
@Override
public Line line(final long identifier)
{
if (this.lineIdentifierToLineArrayIndex().containsKey(identifier))
{
return new PackedLine(this, this.lineIdentifierToLineArrayIndex().get(identifier));
}
return null;
}
@Override
public Iterable lines()
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < PackedAtlas.this.lineIdentifiers().size();
}
@Override
public Line next()
{
return new PackedLine(PackedAtlas.this, this.index++);
}
};
}
@Override
public AtlasMetaData metaData()
{
if (this.metaData == null)
{
this.serializer.deserializeIfNeeded(FIELD_META_DATA);
}
return this.metaData;
}
@Override
public Node node(final long identifier)
{
if (this.nodeIdentifierToNodeArrayIndex().containsKey(identifier))
{
return new PackedNode(this, this.nodeIdentifierToNodeArrayIndex().get(identifier));
}
return null;
}
@Override
public Iterable nodes()
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < PackedAtlas.this.nodeIdentifiers().size();
}
@Override
public Node next()
{
return new PackedNode(PackedAtlas.this, this.index++);
}
};
}
@Override
public long numberOfAreas()
{
return this.areaIdentifiers().size();
}
@Override
public long numberOfEdges()
{
return this.edgeIdentifiers().size();
}
@Override
public long numberOfLines()
{
return this.lineIdentifiers().size();
}
@Override
public long numberOfNodes()
{
return this.nodeIdentifiers().size();
}
@Override
public long numberOfPoints()
{
return this.pointIdentifiers().size();
}
@Override
public long numberOfRelations()
{
return this.relationIdentifiers().size();
}
@Override
public Point point(final long identifier)
{
if (this.pointIdentifierToPointArrayIndex().containsKey(identifier))
{
return new PackedPoint(this, this.pointIdentifierToPointArrayIndex().get(identifier));
}
return null;
}
@Override
public Iterable points()
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < PackedAtlas.this.pointIdentifiers().size();
}
@Override
public Point next()
{
return new PackedPoint(PackedAtlas.this, this.index++);
}
};
}
@Override
public Relation relation(final long identifier)
{
if (this.relationIdentifierToRelationArrayIndex().containsKey(identifier))
{
return new PackedRelation(this,
this.relationIdentifierToRelationArrayIndex().get(identifier));
}
return null;
}
@Override
public Iterable relations()
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < PackedAtlas.this.relationIdentifiers().size();
}
@Override
public Relation next()
{
return new PackedRelation(PackedAtlas.this, this.index++);
}
};
}
@Override
public void save(final WritableResource writableResource)
{
new PackedAtlasSerializer(this, writableResource).save();
}
/**
* Set the serialization format for saving this {@link PackedAtlas}.
*
* @param format
* The format to use
*/
public void setSaveSerializationFormat(final AtlasSerializationFormat format)
{
this.saveSerializationFormat = format;
}
/**
* Trim this Atlas' arrays with the proper size. WARNING! This could potentially temporarily
* double the amount of memory used by each array.
*/
public void trim()
{
logger.info("Trimming Atlas {} to save on space.", this.getName());
final Time start = Time.now();
this.edgeIdentifiers.trim();
this.nodeIdentifiers.trim();
this.areaIdentifiers.trim();
this.lineIdentifiers.trim();
this.pointIdentifiers.trim();
this.relationIdentifiers.trim();
this.edgeIdentifierToEdgeArrayIndex.trim();
this.nodeIdentifierToNodeArrayIndex.trim();
this.areaIdentifierToAreaArrayIndex.trim();
this.lineIdentifierToLineArrayIndex.trim();
this.pointIdentifierToPointArrayIndex.trim();
this.relationIdentifierToRelationArrayIndex.trim();
this.nodeLocations.trim();
this.nodeInEdgesIndices.trim();
this.nodeOutEdgesIndices.trim();
this.nodeTags.trim();
this.nodeIndexToRelationIndices.trim();
this.edgeStartNodeIndex.trim();
this.edgeEndNodeIndex.trim();
this.edgePolyLines.trim();
this.edgeTags.trim();
this.edgeIndexToRelationIndices.trim();
this.areaPolygons.trim();
this.areaTags.trim();
this.areaIndexToRelationIndices.trim();
this.linePolyLines.trim();
this.lineTags.trim();
this.lineIndexToRelationIndices.trim();
this.pointLocations.trim();
this.pointTags.trim();
this.pointIndexToRelationIndices.trim();
this.relationMemberIndices.trim();
this.relationMemberTypes.trim();
this.relationMemberRoles.trim();
this.relationTags.trim();
this.relationIndexToRelationIndices.trim();
this.relationOsmIdentifierToRelationIdentifiers.trim();
this.relationOsmIdentifiers.trim();
logger.info("Trimmed Atlas {} in {}.", this.getName(), start.elapsedSince());
}
protected void addArea(final long areaIdentifier, final Polygon polygon,
final Map tags)
{
synchronized (this.areaIdentifiers)
{
if (this.areaIdentifierToAreaArrayIndex.containsKey(areaIdentifier))
{
throw new AtlasIntegrityException(
"Area with identifier " + areaIdentifier + " already exists.");
}
final long index = this.areaIdentifiers.size();
this.areaIdentifiers.add(areaIdentifier);
this.areaIdentifierToAreaArrayIndex.put(areaIdentifier, index);
this.areaPolygons.add(polygon);
this.getAsNewAreaSpatialIndex().add(new PackedArea(this, index));
// Tags
for (final String key : tags.keySet())
{
final String value = tags.get(key);
this.areaTags.add(index, key, value);
}
if (tags.keySet().isEmpty())
{
this.areaTags.add(index, null, null);
}
}
}
protected void addEdge(final long edgeIdentifier, final long startNodeIdentifier,
final long endNodeIdentifier, final PolyLine polyline, final Map tags)
{
synchronized (this.edgeIdentifiers)
{
if (this.edgeIdentifierToEdgeArrayIndex.containsKey(edgeIdentifier))
{
throw new AtlasIntegrityException(
"Edge with identifier " + edgeIdentifier + " already exists.");
}
final long index = this.edgeIdentifiers.size();
this.edgeIdentifiers.add(edgeIdentifier);
this.edgeIdentifierToEdgeArrayIndex.put(edgeIdentifier, index);
this.edgePolyLines.add(polyline);
// Start Node
final long startNodeIndex = this.nodeIdentifierToNodeArrayIndex
.get(startNodeIdentifier);
this.edgeStartNodeIndex.add(startNodeIndex);
// End Node
final long endNodeIndex = this.nodeIdentifierToNodeArrayIndex.get(endNodeIdentifier);
this.edgeEndNodeIndex.add(endNodeIndex);
// Node In edges
updateNodeEdgesReference(endNodeIndex, this.nodeInEdgesIndices, index);
// Node Out edges
updateNodeEdgesReference(startNodeIndex, this.nodeOutEdgesIndices, index);
// Spatial Index
this.getAsNewEdgeSpatialIndex().add(new PackedEdge(this, index));
// Tags
for (final String key : tags.keySet())
{
final String value = tags.get(key);
this.edgeTags.add(index, key, value);
}
if (tags.keySet().isEmpty())
{
this.edgeTags.add(index, null, null);
}
}
}
protected void addLine(final long lineIdentifier, final PolyLine polyline,
final Map tags)
{
synchronized (this.lineIdentifiers)
{
if (this.lineIdentifierToLineArrayIndex.containsKey(lineIdentifier))
{
throw new AtlasIntegrityException(
"Line with identifier " + lineIdentifier + " already exists.");
}
final long index = this.lineIdentifiers.size();
this.lineIdentifiers.add(lineIdentifier);
this.lineIdentifierToLineArrayIndex.put(lineIdentifier, index);
this.linePolyLines.add(polyline);
this.getAsNewLineSpatialIndex().add(new PackedLine(this, index));
// Tags
for (final String key : tags.keySet())
{
final String value = tags.get(key);
this.lineTags.add(index, key, value);
}
if (tags.keySet().isEmpty())
{
this.lineTags.add(index, null, null);
}
}
}
protected void addNode(final long nodeIdentifier, final Location location,
final Map tags)
{
synchronized (this.nodeIdentifiers)
{
if (this.nodeIdentifierToNodeArrayIndex.containsKey(nodeIdentifier))
{
throw new AtlasIntegrityException(
"Node with identifier " + nodeIdentifier + " already exists.");
}
final long index = this.nodeIdentifiers.size();
this.nodeIdentifiers.add(nodeIdentifier);
this.nodeIdentifierToNodeArrayIndex.put(nodeIdentifier, index);
this.nodeLocations.add(location.asConcatenation());
// Fill the in/out edges arrays for later
this.nodeInEdgesIndices.add(new long[0]);
this.nodeOutEdgesIndices.add(new long[0]);
this.getAsNewNodeSpatialIndex().add(new PackedNode(this, index));
// Tags
for (final String key : tags.keySet())
{
final String value = tags.get(key);
this.nodeTags.add(index, key, value);
}
if (tags.keySet().isEmpty())
{
this.nodeTags.add(index, null, null);
}
}
}
protected void addPoint(final long pointIdentifier, final Location location,
final Map tags)
{
synchronized (this.pointIdentifiers)
{
if (this.pointIdentifierToPointArrayIndex.containsKey(pointIdentifier))
{
throw new AtlasIntegrityException(
"Point with identifier " + pointIdentifier + " already exists.");
}
final long index = this.pointIdentifiers.size();
this.pointIdentifiers.add(pointIdentifier);
this.pointIdentifierToPointArrayIndex.put(pointIdentifier, index);
this.pointLocations.add(location.asConcatenation());
this.getAsNewPointSpatialIndex().add(new PackedPoint(this, index));
// Tags
for (final String key : tags.keySet())
{
final String value = tags.get(key);
this.pointTags.add(index, key, value);
}
if (tags.keySet().isEmpty())
{
this.pointTags.add(index, null, null);
}
}
}
/**
* Add a relation to the {@link PackedAtlas}. WARNING: This method will throw
* {@link AtlasIntegrityException}s in the following cases:
*
* - The identifiers list, types list and roles list do not have all the same length
*
- The identifiers list, types list and roles list are empty
*
- Some member identifiers are null
*
*
* @param relationIdentifier
* The identifier of the relation
* @param relationOsmIdentifier
* The original OSM identifier of the relation. Can be the same as the identifier.
* @param identifiers
* The member identifiers.
* @param types
* The member types in the same order as the member identifiers
* @param roles
* The member roles in the same order as the member identifiers
* @param tags
* The relation's tags
*/
protected void addRelation(final long relationIdentifier, final long relationOsmIdentifier,
final List identifiers, final List types, final List roles,
final Map tags)
{
if (identifiers.size() != types.size() || types.size() != roles.size())
{
throw new AtlasIntegrityException(
"Different sizes for relation identifiers and types and roles.");
}
if (identifiers.isEmpty())
{
throw new AtlasIntegrityException("Cannot add the relation {} with no members",
relationIdentifier);
}
// Do not allow relations with some null members.
if (identifiers.stream().anyMatch(Objects::isNull))
{
throw new AtlasIntegrityException("Cannot have a relation with null members.");
}
synchronized (this.relationIdentifiers)
{
if (this.relationIdentifierToRelationArrayIndex.containsKey(relationIdentifier))
{
throw new AtlasIntegrityException(
"Relation with identifier " + relationIdentifier + " already exists.");
}
final long index = this.relationIdentifiers.size();
this.relationIdentifiers.add(relationIdentifier);
this.relationIdentifierToRelationArrayIndex.put(relationIdentifier, index);
this.relationOsmIdentifierToRelationIdentifiers.add(relationOsmIdentifier,
relationIdentifier);
this.relationOsmIdentifiers.add(relationOsmIdentifier);
final long[] memberIndices = new long[identifiers.size()];
final byte[] typeValues = new byte[types.size()];
final int[] roleValues = new int[roles.size()];
for (int i = 0; i < identifiers.size(); i++)
{
final ItemType type = types.get(i);
typeValues[i] = (byte) type.getValue();
roleValues[i] = this.dictionary.add(roles.get(i));
final Long memberIdentifier = identifiers.get(i);
switch (type)
{
case NODE:
addRelationMember("Node", index, memberIdentifier, i, memberIndices,
this.nodeIdentifierToNodeArrayIndex,
this.nodeIndexToRelationIndices);
break;
case EDGE:
addRelationMember("Edge", index, memberIdentifier, i, memberIndices,
this.edgeIdentifierToEdgeArrayIndex,
this.edgeIndexToRelationIndices);
break;
case AREA:
addRelationMember("Area", index, memberIdentifier, i, memberIndices,
this.areaIdentifierToAreaArrayIndex,
this.areaIndexToRelationIndices);
break;
case LINE:
addRelationMember("Line", index, memberIdentifier, i, memberIndices,
this.lineIdentifierToLineArrayIndex,
this.lineIndexToRelationIndices);
break;
case POINT:
addRelationMember("Point", index, memberIdentifier, i, memberIndices,
this.pointIdentifierToPointArrayIndex,
this.pointIndexToRelationIndices);
break;
case RELATION:
addRelationMember("Relation", index, memberIdentifier, i, memberIndices,
this.relationIdentifierToRelationArrayIndex,
this.relationIndexToRelationIndices);
break;
default:
throw new CoreException("Cannot recognize ItemType {}", type);
}
}
this.relationMemberTypes.add(typeValues);
this.relationMemberIndices.add(memberIndices);
this.relationMemberRoles.add(roleValues);
// Tags
for (final String key : tags.keySet())
{
final String value = tags.get(key);
this.relationTags.add(index, key, value);
}
if (tags.keySet().isEmpty())
{
this.relationTags.add(index, null, null);
}
}
}
protected long areaIdentifier(final long index)
{
return this.areaIdentifiers().get(index);
}
protected Polygon areaPolygon(final long index)
{
return this.areaPolygons().get(index);
}
protected Set areaRelations(final long index)
{
return itemRelations(this.areaIndexToRelationIndices().get(index));
}
protected Map areaTags(final long index)
{
return this.areaTags().keyValuePairs(index);
}
protected Node edgeEndNode(final long index)
{
return new PackedNode(this, this.edgeEndNodeIndex().get(index));
}
protected long edgeIdentifier(final long index)
{
return this.edgeIdentifiers().get(index);
}
protected PolyLine edgePolyLine(final long index)
{
return this.edgePolyLines().get(index);
}
protected Set edgeRelations(final long index)
{
return itemRelations(this.edgeIndexToRelationIndices().get(index));
}
protected Node edgeStartNode(final long index)
{
return new PackedNode(this, this.edgeStartNodeIndex().get(index));
}
protected Map edgeTags(final long index)
{
return this.edgeTags().keyValuePairs(index);
}
/**
* Get the serialization format used for loading this {@link PackedAtlas}.
*
* @return The load serialization format setting
*/
protected AtlasSerializationFormat getLoadSerializationFormat()
{
return this.loadSerializationFormat;
}
protected Optional getSerializer()
{
return Optional.ofNullable(this.serializer);
}
protected boolean isEmpty()
{
return this.nodeIdentifiers().isEmpty() && this.edgeIdentifiers().isEmpty()
&& this.areaIdentifiers().isEmpty() && this.lineIdentifiers().isEmpty()
&& this.pointIdentifiers().isEmpty() && this.relationIdentifiers().isEmpty();
}
protected long lineIdentifier(final long index)
{
return this.lineIdentifiers().get(index);
}
protected PolyLine linePolyLine(final long index)
{
return this.linePolyLines().get(index);
}
protected Set lineRelations(final long index)
{
return itemRelations(this.lineIndexToRelationIndices().get(index));
}
protected Map lineTags(final long index)
{
return this.lineTags().keyValuePairs(index);
}
protected long nodeIdentifier(final long index)
{
return this.nodeIdentifiers().get(index);
}
/**
* In very rare cases, Way Slicing will return slightly non-deterministic cut locations in
* different shards. This tolerance allows the PackedAtlasBuilder to identify very closeby nodes
* and use them instead.
*
* @param location
* The location to target
* @param searchDistance
* The distance to search for around the location
* @param toleranceDistance
* The maximum distance at which a node is accepted
* @return The resulting node identifier
*/
protected Long nodeIdentifierForEnlargedLocation(final Location location,
final Distance searchDistance, final Distance toleranceDistance)
{
final Rectangle bounds = location.bounds().expand(searchDistance);
buildNodeSpatialIndexIfNecessary();
final SortedSet nodes = new TreeSet<>((node1, node2) ->
{
final Distance distance1 = location.distanceTo(node1.getLocation());
final Distance distance2 = location.distanceTo(node2.getLocation());
final double difference = distance2.asMillimeters() - distance1.asMillimeters();
if (difference > 0.0)
{
return 1;
}
else if (difference < 0.0)
{
return -1;
}
else
{
return 0;
}
});
this.getNodeSpatialIndex().get(bounds).forEach(nodes::add);
for (final Node candidate : nodes)
{
final Distance distance = location.distanceTo(candidate.getLocation());
if (distance.isLessThanOrEqualTo(toleranceDistance))
{
return candidate.getIdentifier();
}
}
return null;
}
protected Long nodeIdentifierForLocation(final Location location)
{
buildNodeSpatialIndexIfNecessary();
final Iterator nodes;
final Rectangle bounds = location.bounds();
// Handle the anti-meridian case. +180 and -180 are identical in the Atlas, if this happens
// to be the Longitude of the passed in Location, make sure to also check the equivalent
// Location across the anti-meridian.
if (location.getLongitude().equals(Longitude.ANTIMERIDIAN_EAST)
|| location.getLongitude().equals(Longitude.ANTIMERIDIAN_WEST))
{
final Location locationAcrossAntiMeridian = new Location(location.getLatitude(),
Longitude.dm7(-location.getLongitude().asDm7()));
final Rectangle boundsAcrossAntiMeridian = locationAcrossAntiMeridian.bounds();
nodes = new MultiIterable<>(this.getNodeSpatialIndex().get(bounds),
this.getNodeSpatialIndex().get(boundsAcrossAntiMeridian)).iterator();
}
else
{
nodes = this.getNodeSpatialIndex().get(bounds).iterator();
}
if (nodes.hasNext())
{
return nodes.next().getIdentifier();
}
return null;
}
protected SortedSet nodeInEdges(final long index)
{
final SortedSet result = new TreeSet<>();
for (final long edgeIndex : this.nodeInEdgesIndices().get(index))
{
result.add(new PackedEdge(this, edgeIndex));
}
return result;
}
protected Location nodeLocation(final long index)
{
return new Location(this.nodeLocations().get(index));
}
protected SortedSet nodeOutEdges(final long index)
{
final SortedSet result = new TreeSet<>();
for (final long edgeIndex : this.nodeOutEdgesIndices().get(index))
{
result.add(new PackedEdge(this, edgeIndex));
}
return result;
}
protected Set nodeRelations(final long index)
{
return itemRelations(this.nodeIndexToRelationIndices().get(index));
}
protected Map nodeTags(final long index)
{
return this.nodeTags().keyValuePairs(index);
}
protected long pointIdentifier(final long index)
{
return this.pointIdentifiers().get(index);
}
protected Location pointLocation(final long index)
{
return new Location(this.pointLocations().get(index));
}
protected Set pointRelations(final long index)
{
return itemRelations(this.pointIndexToRelationIndices().get(index));
}
protected Map pointTags(final long index)
{
return this.pointTags().keyValuePairs(index);
}
protected RelationMemberList relationAllKnownOsmMembers(final long index)
{
final List result = new ArrayList<>();
for (final long candidateIdentifier : this.relationOsmIdentifierToRelationIdentifiers()
.get(relationOsmIdentifier(index)))
{
final long candidateIndex = this.relationIdentifierToRelationArrayIndex()
.get(candidateIdentifier);
relationMembers(candidateIndex).forEach(relationMember -> result.add(relationMember));
}
return new RelationMemberList(result);
}
protected List relationAllRelationsWithSameOsmIdentifier(final long index)
{
final List result = new ArrayList<>();
for (final long candidateIdentifier : this.relationOsmIdentifierToRelationIdentifiers()
.get(relationOsmIdentifier(index)))
{
final long candidateIndex = this.relationIdentifierToRelationArrayIndex()
.get(candidateIdentifier);
result.add(new PackedRelation(this, candidateIndex));
}
return result;
}
protected long relationIdentifier(final long index)
{
return this.relationIdentifiers().get(index);
}
/**
* Fetch the {@link RelationMemberList} for a given index. Note that while OSM technically
* allows duplicate {@link RelationMember}s, this method disallows duplicates. So a valid OSM
* relation that looks like {[1L, 'role1', POINT], [1L, 'role1', POINT], [45L, 'area', AREA]}
* would become {[1L, 'role1', POINT], [45L, 'area', AREA]}.
*
* @param index
* the {@link Relation} array index
* @return a fully constructed {@link RelationMemberList} for the {@link Relation} at the given
* index
*/
protected RelationMemberList relationMembers(final long index)
{
final Set result = new TreeSet<>();
int arrayIndex = 0;
for (final byte typeValue : this.relationMemberTypes().get(index))
{
final ItemType type = ItemType.forValue(typeValue);
final long memberIndex = this.relationMemberIndices().get(index)[arrayIndex];
final String role = this.dictionary()
.word(this.relationMemberRoles().get(index)[arrayIndex]);
final AtlasEntity entity;
switch (type)
{
case NODE:
entity = new PackedNode(this, memberIndex);
break;
case EDGE:
entity = new PackedEdge(this, memberIndex);
break;
case AREA:
entity = new PackedArea(this, memberIndex);
break;
case LINE:
entity = new PackedLine(this, memberIndex);
break;
case POINT:
entity = new PackedPoint(this, memberIndex);
break;
case RELATION:
entity = new PackedRelation(this, memberIndex);
break;
default:
throw new CoreException("Invalid member type {}", type);
}
result.add(new RelationMember(role, entity, relationIdentifier(index)));
arrayIndex++;
}
return new RelationMemberList(result);
}
protected long relationOsmIdentifier(final long index)
{
return this.relationOsmIdentifiers().get(index);
}
protected Set relationRelations(final long index)
{
return itemRelations(this.relationIndexToRelationIndices().get(index));
}
protected Map relationTags(final long index)
{
return this.relationTags().keyValuePairs(index);
}
/**
* Set the serialization format for loading this {@link PackedAtlas}.
*
* @param loadFormat
* The format to use
*/
protected void setLoadSerializationFormat(final AtlasSerializationFormat loadFormat)
{
this.loadSerializationFormat = loadFormat;
}
/**
* This method is to be used by the {@link PackedAtlasBuilder} only
*
* @param metaData
* The new MetaData
*/
protected void setMetaData(final AtlasMetaData metaData)
{
this.metaData = metaData;
}
@Override
protected void setName(final String name)
{
super.setName(name);
}
/**
* Add a {@link RelationMember}
*
* @param relationIndex
* The index of the {@link Relation} in the {@link Relation} arrays.
* @param memberIdentifier
* The identifier of the member to add
* @param relationMemberListIndex
* The index of the member in the {@link Relation}'s member arrays.
* @param relationMemberIndexArray
* The array of the indices of the relation members.
* @param memberIdentifierToArrayIndex
* The member type's identifier to array index map
* @param memberIndicesToRelationIndices
* The member type's index to relation indices map.
*/
private void addRelationMember(final String type, final long relationIndex,
final Long memberIdentifier, final int relationMemberListIndex,
final long[] relationMemberIndexArray, final LongToLongMap memberIdentifierToArrayIndex,
final LongToLongMultiMap memberIndicesToRelationIndices)
{
if (memberIdentifierToArrayIndex.containsKey(memberIdentifier))
{
relationMemberIndexArray[relationMemberListIndex] = memberIdentifierToArrayIndex
.get(memberIdentifier);
memberIndicesToRelationIndices.add(relationMemberIndexArray[relationMemberListIndex],
relationIndex);
}
else
{
throw new AtlasIntegrityException("The {} {} does not exist for relation {}.", type,
memberIdentifier, this.relationIdentifiers.get(relationIndex));
}
}
private LongArray areaIdentifiers()
{
if (this.areaIdentifiers == null)
{
synchronized (FIELD_AREA_IDENTIFIERS_LOCK)
{
if (this.areaIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_AREA_IDENTIFIERS);
}
}
}
return this.areaIdentifiers;
}
private LongToLongMap areaIdentifierToAreaArrayIndex()
{
if (this.areaIdentifierToAreaArrayIndex == null)
{
synchronized (FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX_LOCK)
{
if (this.areaIdentifierToAreaArrayIndex == null)
{
this.serializer.deserializeIfNeeded(FIELD_AREA_IDENTIFIER_TO_AREA_ARRAY_INDEX);
}
}
}
return this.areaIdentifierToAreaArrayIndex;
}
private LongToLongMultiMap areaIndexToRelationIndices()
{
if (this.areaIndexToRelationIndices == null)
{
synchronized (FIELD_AREA_INDEX_TO_RELATION_INDICES_LOCK)
{
if (this.areaIndexToRelationIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_AREA_INDEX_TO_RELATION_INDICES);
}
}
}
return this.areaIndexToRelationIndices;
}
private PolygonArray areaPolygons()
{
if (this.areaPolygons == null)
{
synchronized (FIELD_AREA_POLYGONS_LOCK)
{
if (this.areaPolygons == null)
{
this.serializer.deserializeIfNeeded(FIELD_AREA_POLYGONS);
}
}
}
return this.areaPolygons;
}
private PackedTagStore areaTags()
{
if (this.areaTags == null)
{
synchronized (FIELD_AREA_TAGS_LOCK)
{
if (this.areaTags == null)
{
this.serializer.deserializeIfNeeded(FIELD_AREA_TAGS);
}
}
}
this.areaTags.setDictionary(dictionary());
return this.areaTags;
}
private IntegerDictionary dictionary()
{
if (this.dictionary == null)
{
synchronized (FIELD_DICTIONARY_LOCK)
{
if (this.dictionary == null)
{
this.serializer.deserializeIfNeeded(FIELD_DICTIONARY);
}
}
}
return this.dictionary;
}
private LongArray edgeEndNodeIndex()
{
if (this.edgeEndNodeIndex == null)
{
synchronized (FIELD_EDGE_END_NODE_INDEX_LOCK)
{
if (this.edgeEndNodeIndex == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_END_NODE_INDEX);
}
}
}
return this.edgeEndNodeIndex;
}
private LongArray edgeIdentifiers()
{
if (this.edgeIdentifiers == null)
{
synchronized (FIELD_EDGE_IDENTIFIERS_LOCK)
{
if (this.edgeIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_IDENTIFIERS);
}
}
}
return this.edgeIdentifiers;
}
private LongToLongMap edgeIdentifierToEdgeArrayIndex()
{
if (this.edgeIdentifierToEdgeArrayIndex == null)
{
synchronized (FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX_LOCK)
{
if (this.edgeIdentifierToEdgeArrayIndex == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_IDENTIFIER_TO_EDGE_ARRAY_INDEX);
}
}
}
return this.edgeIdentifierToEdgeArrayIndex;
}
private LongToLongMultiMap edgeIndexToRelationIndices()
{
if (this.edgeIndexToRelationIndices == null)
{
synchronized (FIELD_EDGE_INDEX_TO_RELATION_INDICES_LOCK)
{
if (this.edgeIndexToRelationIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_INDEX_TO_RELATION_INDICES);
}
}
}
return this.edgeIndexToRelationIndices;
}
private PolyLineArray edgePolyLines()
{
if (this.edgePolyLines == null)
{
synchronized (FIELD_EDGE_POLY_LINES_LOCK)
{
if (this.edgePolyLines == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_POLY_LINES);
}
}
}
return this.edgePolyLines;
}
private LongArray edgeStartNodeIndex()
{
if (this.edgeStartNodeIndex == null)
{
synchronized (FIELD_EDGE_START_NODE_INDEX_LOCK)
{
if (this.edgeStartNodeIndex == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_START_NODE_INDEX);
}
}
}
return this.edgeStartNodeIndex;
}
private PackedTagStore edgeTags()
{
if (this.edgeTags == null)
{
synchronized (FIELD_EDGE_TAGS_LOCK)
{
if (this.edgeTags == null)
{
this.serializer.deserializeIfNeeded(FIELD_EDGE_TAGS);
}
}
}
this.edgeTags.setDictionary(dictionary());
return this.edgeTags;
}
private Set itemRelations(final long[] relationIndices)
{
final Set result = new LinkedHashSet<>();
if (relationIndices == null)
{
return result;
}
for (final long relationIndex : relationIndices)
{
result.add(new PackedRelation(this, relationIndex));
}
return result;
}
private LongArray lineIdentifiers()
{
if (this.lineIdentifiers == null)
{
synchronized (FIELD_LINE_IDENTIFIERS_LOCK)
{
if (this.lineIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_LINE_IDENTIFIERS);
}
}
}
return this.lineIdentifiers;
}
private LongToLongMap lineIdentifierToLineArrayIndex()
{
if (this.lineIdentifierToLineArrayIndex == null)
{
synchronized (FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX_LOCK)
{
if (this.lineIdentifierToLineArrayIndex == null)
{
this.serializer.deserializeIfNeeded(FIELD_LINE_IDENTIFIER_TO_LINE_ARRAY_INDEX);
}
}
}
return this.lineIdentifierToLineArrayIndex;
}
private LongToLongMultiMap lineIndexToRelationIndices()
{
if (this.lineIndexToRelationIndices == null)
{
synchronized (FIELD_LINE_INDEX_TO_RELATION_INDICES_LOCK)
{
if (this.lineIndexToRelationIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_LINE_INDEX_TO_RELATION_INDICES);
}
}
}
return this.lineIndexToRelationIndices;
}
private PolyLineArray linePolyLines()
{
if (this.linePolyLines == null)
{
synchronized (FIELD_LINE_POLYLINES_LOCK)
{
if (this.linePolyLines == null)
{
this.serializer.deserializeIfNeeded(FIELD_LINE_POLYLINES);
}
}
}
return this.linePolyLines;
}
private PackedTagStore lineTags()
{
if (this.lineTags == null)
{
synchronized (FIELD_LINE_TAGS_LOCK)
{
if (this.lineTags == null)
{
this.serializer.deserializeIfNeeded(FIELD_LINE_TAGS);
}
}
}
this.lineTags.setDictionary(dictionary());
return this.lineTags;
}
// Keep this method around so legacy Atlas files can still be deserialized.
@SuppressWarnings("unused")
private PackedTagStore newPackedTagStore(final long maximumSize, final int memoryBlockSize,
final int subArraySize)
{
return new PackedTagStore(maximumSize, memoryBlockSize, subArraySize, dictionary())
{
private static final long serialVersionUID = 5959934069025112665L;
@Override
public IntegerDictionary keysDictionary()
{
return super.keysDictionary();
}
@Override
public IntegerDictionary valuesDictionary()
{
return super.valuesDictionary();
}
};
}
private LongArray nodeIdentifiers()
{
if (this.nodeIdentifiers == null)
{
synchronized (FIELD_NODE_IDENTIFIERS_LOCK)
{
if (this.nodeIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_IDENTIFIERS);
}
}
}
return this.nodeIdentifiers;
}
private LongToLongMap nodeIdentifierToNodeArrayIndex()
{
if (this.nodeIdentifierToNodeArrayIndex == null)
{
synchronized (FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX_LOCK)
{
if (this.nodeIdentifierToNodeArrayIndex == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_IDENTIFIER_TO_NODE_ARRAY_INDEX);
}
}
}
return this.nodeIdentifierToNodeArrayIndex;
}
private LongToLongMultiMap nodeIndexToRelationIndices()
{
if (this.nodeIndexToRelationIndices == null)
{
synchronized (FIELD_NODE_INDEX_TO_RELATION_INDICES_LOCK)
{
if (this.nodeIndexToRelationIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_INDEX_TO_RELATION_INDICES);
}
}
}
return this.nodeIndexToRelationIndices;
}
private LongArrayOfArrays nodeInEdgesIndices()
{
if (this.nodeInEdgesIndices == null)
{
synchronized (FIELD_NODE_IN_EDGES_INDICES_LOCK)
{
if (this.nodeInEdgesIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_IN_EDGES_INDICES);
}
}
}
return this.nodeInEdgesIndices;
}
private LongArray nodeLocations()
{
if (this.nodeLocations == null)
{
synchronized (FIELD_NODE_LOCATIONS_LOCK)
{
if (this.nodeLocations == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_LOCATIONS);
}
}
}
return this.nodeLocations;
}
private LongArrayOfArrays nodeOutEdgesIndices()
{
if (this.nodeOutEdgesIndices == null)
{
synchronized (FIELD_NODE_OUT_EDGES_INDICES_LOCK)
{
if (this.nodeOutEdgesIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_OUT_EDGES_INDICES);
}
}
}
return this.nodeOutEdgesIndices;
}
private PackedTagStore nodeTags()
{
if (this.nodeTags == null)
{
synchronized (FIELD_NODE_TAGS_LOCK)
{
if (this.nodeTags == null)
{
this.serializer.deserializeIfNeeded(FIELD_NODE_TAGS);
}
}
}
this.nodeTags.setDictionary(dictionary());
return this.nodeTags;
}
private LongArray pointIdentifiers()
{
if (this.pointIdentifiers == null)
{
synchronized (FIELD_POINT_IDENTIFIERS_LOCK)
{
if (this.pointIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_POINT_IDENTIFIERS);
}
}
}
return this.pointIdentifiers;
}
private LongToLongMap pointIdentifierToPointArrayIndex()
{
if (this.pointIdentifierToPointArrayIndex == null)
{
synchronized (FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX_LOCK)
{
if (this.pointIdentifierToPointArrayIndex == null)
{
this.serializer
.deserializeIfNeeded(FIELD_POINT_IDENTIFIER_TO_POINT_ARRAY_INDEX);
}
}
}
return this.pointIdentifierToPointArrayIndex;
}
private LongToLongMultiMap pointIndexToRelationIndices()
{
if (this.pointIndexToRelationIndices == null)
{
synchronized (FIELD_POINT_INDEX_TO_RELATION_INDICES_LOCK)
{
if (this.pointIndexToRelationIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_POINT_INDEX_TO_RELATION_INDICES);
}
}
}
return this.pointIndexToRelationIndices;
}
private LongArray pointLocations()
{
if (this.pointLocations == null)
{
synchronized (FIELD_POINT_LOCATIONS_LOCK)
{
if (this.pointLocations == null)
{
this.serializer.deserializeIfNeeded(FIELD_POINT_LOCATIONS);
}
}
}
return this.pointLocations;
}
private PackedTagStore pointTags()
{
if (this.pointTags == null)
{
synchronized (FIELD_POINT_TAGS_LOCK)
{
if (this.pointTags == null)
{
this.serializer.deserializeIfNeeded(FIELD_POINT_TAGS);
}
}
}
this.pointTags.setDictionary(dictionary());
return this.pointTags;
}
private LongArray relationIdentifiers()
{
if (this.relationIdentifiers == null)
{
synchronized (FIELD_RELATION_IDENTIFIERS_LOCK)
{
if (this.relationIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_IDENTIFIERS);
}
}
}
return this.relationIdentifiers;
}
private LongToLongMap relationIdentifierToRelationArrayIndex()
{
if (this.relationIdentifierToRelationArrayIndex == null)
{
synchronized (FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX_LOCK)
{
if (this.relationIdentifierToRelationArrayIndex == null)
{
this.serializer
.deserializeIfNeeded(FIELD_RELATION_IDENTIFIER_TO_RELATION_ARRAY_INDEX);
}
}
}
return this.relationIdentifierToRelationArrayIndex;
}
private LongToLongMultiMap relationIndexToRelationIndices()
{
if (this.relationIndexToRelationIndices == null)
{
synchronized (FIELD_RELATION_INDEX_TO_RELATION_INDICES_LOCK)
{
if (this.relationIndexToRelationIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_INDEX_TO_RELATION_INDICES);
}
}
}
return this.relationIndexToRelationIndices;
}
private LongArrayOfArrays relationMemberIndices()
{
if (this.relationMemberIndices == null)
{
synchronized (FIELD_RELATION_MEMBERS_INDICES_LOCK)
{
if (this.relationMemberIndices == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_MEMBERS_INDICES);
}
}
}
return this.relationMemberIndices;
}
private IntegerArrayOfArrays relationMemberRoles()
{
if (this.relationMemberRoles == null)
{
synchronized (FIELD_RELATION_MEMBER_ROLES_LOCK)
{
if (this.relationMemberRoles == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_MEMBER_ROLES);
}
}
}
return this.relationMemberRoles;
}
private ByteArrayOfArrays relationMemberTypes()
{
if (this.relationMemberTypes == null)
{
synchronized (FIELD_RELATION_MEMBER_TYPES_LOCK)
{
if (this.relationMemberTypes == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_MEMBER_TYPES);
}
}
}
return this.relationMemberTypes;
}
private LongArray relationOsmIdentifiers()
{
if (this.relationOsmIdentifiers == null)
{
synchronized (FIELD_RELATION_OSM_IDENTIFIERS_LOCK)
{
if (this.relationOsmIdentifiers == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_OSM_IDENTIFIERS);
}
}
}
return this.relationOsmIdentifiers;
}
private LongToLongMultiMap relationOsmIdentifierToRelationIdentifiers()
{
if (this.relationOsmIdentifierToRelationIdentifiers == null)
{
synchronized (FIELD_RELATION_OSM_IDENTIFIER_TO_RELATION_IDENTIFIERS_LOCK)
{
if (this.relationOsmIdentifierToRelationIdentifiers == null)
{
this.serializer.deserializeIfNeeded(
FIELD_RELATION_OSM_IDENTIFIER_TO_RELATION_IDENTIFIERS);
}
}
}
return this.relationOsmIdentifierToRelationIdentifiers;
}
private PackedTagStore relationTags()
{
if (this.relationTags == null)
{
synchronized (FIELD_RELATION_TAGS_LOCK)
{
if (this.relationTags == null)
{
this.serializer.deserializeIfNeeded(FIELD_RELATION_TAGS);
}
}
}
this.relationTags.setDictionary(dictionary());
return this.relationTags;
}
/**
* Update references for Node in/out edges
*
* @param nodeEdgesIndices
* Either the nodeInEdges or the nodeOutEdges
*/
private void updateNodeEdgesReference(final long nodeIndex,
final LongArrayOfArrays nodeEdgesIndices, final long edgeIndex)
{
final long[] nodeEdges = nodeEdgesIndices.get(nodeIndex);
final long[] newNodeEdges = new long[nodeEdges.length + 1];
for (int i = 0; i < nodeEdges.length; i++)
{
newNodeEdges[i] = nodeEdges[i];
}
newNodeEdges[newNodeEdges.length - 1] = edgeIndex;
nodeEdgesIndices.set(nodeIndex, newNodeEdges);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy