/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.core.gis.map.tiled;
import gnu.trove.map.hash.TLongObjectHashMap;
import gnu.trove.procedure.TLongObjectProcedure;
import gnu.trove.procedure.TLongProcedure;
import gnu.trove.set.hash.TLongHashSet;
import java.util.ArrayList;
import java.util.List;
import com.opendoorlogistics.core.gis.map.data.DrawableObject;
import com.opendoorlogistics.core.gis.map.data.DrawableObjectImpl;
import com.opendoorlogistics.core.utils.Colours;
import com.opendoorlogistics.core.utils.strings.Strings;
public class ChangedObjectsCalculator {
private TLongObjectHashMap<List<DrawableObject>> drawables = new TLongObjectHashMap<>();
private TLongHashSet selected = new TLongHashSet();
/**
* Get the list of objects whose selection state has changed
*
* @param newSelected
* @return
*/
synchronized List<DrawableObject> updateSelected(final TLongHashSet newSelected) {
final TLongHashSet changeset = new TLongHashSet();
// get ids added to the selection
newSelected.forEach(new TLongProcedure() {
@Override
public boolean execute(long id) {
if (selected.contains(id) == false) {
changeset.add(id);
}
return true;
}
});
// get ids removed from the selection
this.selected.forEach(new TLongProcedure() {
@Override
public boolean execute(long id) {
if (newSelected.contains(id) == false) {
changeset.add(id);
}
return true;
}
});
// update internal list
selected = new TLongHashSet(newSelected);
// translate to a change set of objects
final ArrayList<DrawableObject> ret = new ArrayList<>(changeset.size());
changeset.forEach(new TLongProcedure() {
@Override
public boolean execute(long id) {
List<DrawableObject> objs = drawables.get(id);
if (objs != null) {
ret.addAll(objs);
}
return true;
}
});
return ret;
}
/**
* Get the list of object that has changed
*
* @param objs
*/
synchronized List<DrawableObject> updateObjects(Iterable<? extends DrawableObject> objs) {
final ArrayList<DrawableObject> ret = new ArrayList<>();
// Take copy of input in a hashmap to compare with and replace our internal list.
// Also check for any added objects.
final TLongObjectHashMap<List<DrawableObject>> newMap = new TLongObjectHashMap<>();
for (DrawableObject drawable : objs) {
DrawableObject copy = new DrawableObjectImpl(drawable);
List<DrawableObject> list = newMap.get(drawable.getGlobalRowId());
if(list==null){
list = new ArrayList<>(1);
newMap.put(drawable.getGlobalRowId(), list);
}
list.add(copy);
// is this new?
if (drawables.containsKey(drawable.getGlobalRowId()) == false) {
ret.add(copy);
}
}
// Check for any removed or modified objects from our current objects
drawables.forEachEntry(new TLongObjectProcedure<List<DrawableObject>>() {
@Override
public boolean execute(long id, List<DrawableObject> oldList) {
List<DrawableObject> newList = newMap.get(id);
// has this been deleted?
if (newList == null) {
ret.addAll(oldList);
}
else {
boolean addBothLists=false;
if(oldList.size()!=newList.size()){
// different sized lists.. must have changed
addBothLists = true;
}
if(!addBothLists){
if(oldList.size()== 1 && newList.size()==1){
// special case where only one object in each.. easy to check
addBothLists = isDifferent(oldList.get(0), newList.get(0));
}else{
// match lists
ArrayList<DrawableObject> newCopy = new ArrayList<>(newList);
int n = oldList.size();
for(int i =0 ; i<n && addBothLists==false;i++){
// parse the copy of the list trying to match old to new objects
int n2 = newCopy.size();
int match=-1;
for(int j=0;j<n2;j++){
if(isDifferent(oldList.get(i), newCopy.get(j))==false){
match = j;
break;
}
}
// System.out.println("" + id + " - " + oldList.get(i).getGeometry());
// System.out.println("" + id + " - " + newList.get(i).getGeometry());
if(match!=-1){
// found match
newCopy.remove(match);
}else{
addBothLists = true;
}
}
}
}
if(addBothLists){
ret.addAll(oldList);
ret.addAll(newList);
}
}
return true;
}
});
// replace internal list
drawables = newMap;
// System.out.println("Total different: " + ret.size());
return ret;
}
private static boolean isDifferent(DrawableObject oldObj, DrawableObject newObj) {
// has it been modified?
boolean modified = false;
// check position
if (!modified) {
modified = oldObj.getLatitude() != newObj.getLatitude() || oldObj.getLongitude() != newObj.getLongitude();
}
// if(oldObj.getGeometry()!=null && newObj.getGeometry()!=null && Point.class.isInstance(((ODLGeomImpl)oldObj.getGeometry()).getJTSGeometry())
// && Point.class.isInstance(((ODLGeomImpl)oldObj.getGeometry()).getJTSGeometry())){
// System.out.println(oldObj.getGlobalRowId());
// }
// check geometry (geometry is immutable so just check its the same object)
if (!modified) {
modified = oldObj.getGeometry() != newObj.getGeometry();
}
// check colour
if (!modified) {
modified = Colours.compare(oldObj.getColour(), newObj.getColour()) != 0;
}
// check colour key
if (!modified) {
modified = !Strings.equals(oldObj.getColourKey(), newObj.getColourKey());
}
// check draw outline
if (!modified) {
modified = oldObj.getDrawOutline() != newObj.getDrawOutline();
}
// image formula key
if (!modified) {
modified = !Strings.equals(oldObj.getImageFormulaKey(), newObj.getImageFormulaKey());
}
// legend key
if (!modified) {
modified = !Strings.equals(oldObj.getLegendKey(), newObj.getLegendKey());
}
// label
if (!modified) {
modified = !Strings.equals(oldObj.getLabel(), newObj.getLabel());
}
if(!modified){
modified = !Strings.equals(oldObj.getLabelPositioningOption(), newObj.getLabelPositioningOption());
}
// check label colour
if (!modified) {
modified = Colours.compare(oldObj.getLabelColour(), newObj.getLabelColour()) != 0;
}
// check label priority
if(!modified){
modified = oldObj.getLabelPriority()!=newObj.getLabelPriority();
}
// font size
if (!modified) {
modified = oldObj.getFontSize() != newObj.getFontSize();
}
// pixel width
if (!modified) {
modified = oldObj.getPixelWidth() != newObj.getPixelWidth();
}
// opaque
if (!modified) {
modified = oldObj.getOpaque() != newObj.getOpaque();
}
// symbol
if(!modified){
modified = !Strings.equals(oldObj.getSymbol(), newObj.getSymbol());
}
// flags
if(!modified){
modified = oldObj.getFlags()!=newObj.getFlags();
}
if(!modified){
modified = oldObj.getMinZoom()!=newObj.getMinZoom();
}
if(!modified){
modified = oldObj.getMaxZoom()!=newObj.getMaxZoom();
}
return modified;
}
}