package com.revolsys.geometry.index; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; import com.revolsys.collection.list.Lists; import com.revolsys.geometry.model.Point; import com.revolsys.parallel.channel.Channel; import com.revolsys.predicate.Predicates; import com.revolsys.record.Record; public class PointRecordMap { public static PointRecordMap newMap(final Iterable<? extends Record> records) { return new PointRecordMap(records); } private Comparator<Record> comparator; private Map<Point, List<Record>> recordMap = new HashMap<>(); private boolean removeEmptyLists; private int size = 0; public PointRecordMap() { } public PointRecordMap(final Comparator<Record> comparator) { this.comparator = comparator; } public PointRecordMap(final Comparator<Record> comparator, final Iterable<? extends Record> records) { this.comparator = comparator; addAll(records); } public PointRecordMap(final Iterable<? extends Record> records) { addAll(records); } public void addAll(final Iterable<? extends Record> records) { for (final Record record : records) { addRecord(record); } } public boolean addKey(final Point point) { final Point key = getKey(point); if (this.recordMap.get(key) == null) { this.recordMap.put(key, new ArrayList<>()); return true; } else { return false; } } /** * Add a {@link Point} {@link Record} to the list of objects at the given * coordinate. * * @param pointObjects The map of point objects. * @param record The object to add. */ public boolean addRecord(final Record record) { final Point key = getKey(record); final List<Record> records = getOrCreateRecords(key); if (records.add(record)) { if (this.comparator != null) { Collections.sort(records, this.comparator); } this.size++; return true; } else { return false; } } public void clear() { this.size = 0; this.recordMap = new HashMap<>(); } public boolean containsKey(final Point point) { final Point key = getKey(point); return this.recordMap.containsKey(key); } public List<Record> getAll() { final List<Record> records = new ArrayList<>(); for (final List<Record> recordsAtPoint : this.recordMap.values()) { records.addAll(recordsAtPoint); } return records; } @SuppressWarnings("unchecked") public <V extends Record> V getFirstMatch(final Point point) { final List<Record> records = getRecords(point); if (records.isEmpty()) { return null; } else { return (V)records.get(0); } } public Record getFirstMatch(final Record record, final Predicate<Record> filter) { final List<Record> records = getRecords(record); for (final Record matchRecord : records) { if (filter.test(matchRecord)) { return matchRecord; } } return null; } private Point getKey(final Point point) { return point.newPoint2D(); } private Point getKey(final Record record) { final Point point = record.getGeometry(); return getKey(point); } public Set<Point> getKeys() { return Collections.<Point> unmodifiableSet(this.recordMap.keySet()); } public List<Record> getMatches(final Record record, final Predicate<Record> predicate) { final List<Record> records = getRecords(record); final List<Record> filteredRecords = Predicates.filter(records, predicate); return filteredRecords; } protected List<Record> getOrCreateRecords(final Point key) { List<Record> objects = this.recordMap.get(key); if (objects == null) { objects = new ArrayList<>(1); this.recordMap.put(key, objects); } return objects; } @SuppressWarnings({ "rawtypes", "unchecked" }) public <R extends Record> List<R> getRecords(final Point point) { final Point key = getKey(point); final List<R> records = (List)this.recordMap.get(key); return Lists.toArray(records); } public <R extends Record> List<R> getRecords(final Record record) { final Point point = record.getGeometry(); final List<R> records = getRecords(point); return records; } public boolean hasRecords(final Point point) { final List<Record> records = this.recordMap.get(point); return records != null && !records.isEmpty(); } public void initialize(final Point point) { if (!isRemoveEmptyLists()) { final Point key = getKey(point); getOrCreateRecords(key); } } public boolean isRemoveEmptyLists() { return this.removeEmptyLists; } public void removeRecord(final Record record) { final Point key = getKey(record); final List<Record> objects = this.recordMap.get(key); if (objects != null) { objects.remove(record); if (objects.isEmpty()) { if (isRemoveEmptyLists()) { this.recordMap.remove(key); } } else if (this.comparator != null) { Collections.sort(objects, this.comparator); } } this.size--; } public void setRemoveEmptyLists(final boolean removeEmptyLists) { this.removeEmptyLists = removeEmptyLists; } public int size() { return this.size; } public void sort(final Record record) { if (this.comparator != null) { final List<Record> records = getRecords(record); if (records != null) { Collections.sort(records, this.comparator); } } } public void write(final Channel<Record> out) { if (out != null) { for (final Point point : getKeys()) { final List<Record> points = getRecords(point); for (final Record object : points) { out.write(object); } } } } }