/*
* Copyright (C) 2014 Alec Dhuse
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.foldingmap.data;
import co.foldingmap.map.vector.VectorObjectList;
import co.foldingmap.map.vector.VectorObject;
import co.foldingmap.map.vector.Coordinate;
import co.foldingmap.map.vector.LineString;
import co.foldingmap.map.vector.MapPoint;
import co.foldingmap.map.vector.Polygon;
import co.foldingmap.Logger;
import co.foldingmap.dataStructures.PropertyValuePair;
import co.foldingmap.map.DigitalMap;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
* @author Alec
*/
public class MapObjectCreator {
protected DigitalMap mapData;
protected VectorObjectList<VectorObject> errorObjects;
protected StringBuffer results;
protected TabularData dataFile;
public MapObjectCreator(TabularData dataFile, DigitalMap mapData) {
this.mapData = mapData;
this.dataFile = dataFile;
this.results = new StringBuffer();
}
/**
* Adds information from the TabularData class in to MapObjects where
* the importDataVariable matches the mapObjectVariable.
*
* @param mapData
* @param importDataVariable
* @param mapObjectVariable
*/
public void addData(DigitalMap mapData, String importDataVariable, String mapObjectVariable) {
ArrayList<PropertyValuePair> customDataFields;
ArrayList<DataCell> uv;
ArrayList<DataCell> currentRow, uniqueColumnItems;
double average;
int affectedObjects, trueValues, totalValues, unaffectedObjects;
VectorObjectList<VectorObject> mapObjects;
PropertyValuePair currentPair;
String columnItemString, mapObjectVariableValue;
TabularData subSet;
affectedObjects = 0;
mapObjects = new VectorObjectList<VectorObject>(mapData.getAllMapObjects());
unaffectedObjects = 0;
uniqueColumnItems = dataFile.getUniqueColumnItems(importDataVariable);
for (DataCell currentItem: uniqueColumnItems) {
customDataFields = new ArrayList<PropertyValuePair>();
columnItemString = currentItem.toString().trim();
columnItemString = columnItemString.replaceAll("[\\u00A0]", "");
subSet = dataFile.getSubSet(importDataVariable, columnItemString);
//get data into a managable form
for (int i = 0; i < dataFile.getNumberOfHeaders(); i++) {
if (!(dataFile.getHeaderName(i).equals(importDataVariable))) {
//only parse the data for other field than the one to match to a VectorObject
uv = subSet.getUniqueColumnItems(i);
if (uv.size() == 1) {
//One Unique Value, use that value.
currentPair = new PropertyValuePair(dataFile.getHeaderName(i), uv.get(0).toString());
customDataFields.add(currentPair);
} else if (uv.size() == 2) {
/*
* There are only two options for choice in this column,
* take and average for the data point
*/
if (uv.get(0).trim().equals("")) {
trueValues = subSet.getNumberOfInstances(uv.get(1).toString(), i);
} else {
trueValues = subSet.getNumberOfInstances(uv.get(0).toString(), i);
}
average = ( ((double) trueValues) / ((double) dataFile.getColumn(i).size()));
average *= 100;
currentPair = new PropertyValuePair(dataFile.getHeaderName(i), Double.toString(average));
customDataFields.add(currentPair);
} else if (uv.size() == dataFile.getColumn(i).size()) {
//Every cell has a unique data point, use count;
currentPair = new PropertyValuePair(dataFile.getHeaderName(i), Integer.toString(uv.size()));
customDataFields.add(currentPair);
} else if (TabularData.areOnlyNumbers(subSet.getColumn(i))) {
//average numbers together
average = 0;
totalValues = 0;
for (DataCell currentCell: subSet.getColumn(i)) {
if (!currentCell.equals("")) {
average += currentCell.getFloatValue();
totalValues++;
}
}
results.append("Multiple matches for \"");
results.append(subSet.getHeaderName(i));
results.append("\", values were averaged.\n");
average = average / ((double) totalValues);
currentPair = new PropertyValuePair(dataFile.getHeaderName(i), Double.toString(average));
customDataFields.add(currentPair);
} else {
Logger.log(Logger.ERR, "Data Import - Could not find a data solution for: " + dataFile.getHeaderName(i));
}
}
}
//add data to object
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
if (mapObjectVariable.equalsIgnoreCase("Name")) {
if (currentMapObject.getName().trim().equalsIgnoreCase(currentItem.trim())) {
affectedObjects++;
for (PropertyValuePair currentPropPair: customDataFields)
currentMapObject.setCustomDataField(currentPropPair.getProperty(), currentPropPair.getValue());
//break;
} else {
unaffectedObjects++;
}
} else {
mapObjectVariableValue = currentMapObject.getCustomDataFieldValue(mapObjectVariable);
if (mapObjectVariableValue != null) {
columnItemString = currentItem.toString().trim();
columnItemString = columnItemString.replaceAll("[\\u00A0]", "");
if (mapObjectVariableValue.equalsIgnoreCase(columnItemString)) {
affectedObjects++;
for (PropertyValuePair currentPropPair: customDataFields)
currentMapObject.setCustomDataField(currentPropPair.getProperty(), currentPropPair.getValue());
//break;
} else {
unaffectedObjects++;
}
}
}
}
} // end uniqueColumnItems for loop
//write results
results.append("Added data to: ");
results.append(Integer.toString(affectedObjects));
results.append(" objects.\n");
results.append("Could not match: ");
results.append(Integer.toString(unaffectedObjects));
results.append(" objects.\n");
}
/**
* Creates LineStrings from the TabularData class.
*
* @param nameColumn
* @param coordinatesColumn
* @param objectType
*/
public VectorObjectList<VectorObject> createLineStrings(String nameColumn, String coordinatesColumn, String objectType) {
ArrayList<String> headers;
ArrayList<DataCell> row;
HashMap<String,String> customFields;
int coordinatesColumnIndex, nameColumnIndex, numberOfRows;
LineString newLine;
VectorObjectList<VectorObject> newLines;
String coordinateString, name;
coordinatesColumnIndex = -1;
coordinateString = "";
headers = dataFile.getHeaderNames();
name = "";
nameColumnIndex = -1;
newLines = new VectorObjectList<VectorObject>();
numberOfRows = dataFile.getNumberOfRows();
this.errorObjects = new VectorObjectList<VectorObject>();
//find the columns for name, coordinates
for (int i = 0; i < headers.size(); i++) {
String header = headers.get(i);
if (header.equalsIgnoreCase(nameColumn)) {
nameColumnIndex = i;
} else if (header.equalsIgnoreCase(coordinatesColumn)) {
coordinatesColumnIndex = i;
}
}
for (int i = 0; i < numberOfRows; i++) {
customFields = new HashMap<String,String>();
row = dataFile.getRow(i);
if (coordinatesColumnIndex >= 0) {
//coordinates exist
//get name and coordinate string fields
coordinateString = row.get(coordinatesColumnIndex).toString();
name = row.get(nameColumnIndex).toString();
//create custom fields
for (int headerIndex = 0; headerIndex < headers.size(); headerIndex++) {
if ( (headerIndex != nameColumnIndex) && (headerIndex != coordinatesColumnIndex) ) {
customFields.put(headers.get(headerIndex).toString(), row.get(headerIndex).toString());
}
} //end custom fields for loop
//create objects
try {
if (!coordinateString.equals("")) {
newLine = new LineString(name, objectType, coordinateString);
newLines.add(newLine);
} else {
errorObjects.add(new MapPoint(name, Coordinate.UNKNOWN_COORDINATE, customFields));
}
} catch (Exception e) {
errorObjects.add(new MapPoint(name, Coordinate.UNKNOWN_COORDINATE, customFields));
}
}
} //end for loop
//write results
results.append("Created ");
results.append(newLines.size());
results.append(" objects.");
if (errorObjects.size() > 0) {
results.append("\nCould not create ");
results.append(errorObjects.size());
results.append(" objects because of errors.");
results.append("\n\nObjects With Errors:\n");
results.append("--------------------");
for (VectorObject currentErrorObject: errorObjects) {
results.append("\n");
results.append(currentErrorObject.toString());
}
}
return newLines;
}
/**
* Creates MapPoints from the columns containing latitude, longitude and
* altitude.
*
* @param latitudeColumn
* @param longitudeColumn
* @param altitudeColumn
* @param objectType
*/
public VectorObjectList<VectorObject> createPoints(String latitudeColumn, String longitudeColumn, String altitudeColumn, String objectType) {
ArrayList<String> headers;
ArrayList<DataCell> row;
float latitude, longitude, altitude;
HashMap<String,String> customFields;
int numberOfRows;
int latitudeColumnIndex, longitudeColumnIndex, altitudeColumnIndex;
MapPoint currentMapPoint;
VectorObjectList<VectorObject> newPoints;
String name;
this.errorObjects = new VectorObjectList<VectorObject>();
headers = dataFile.getHeaderNames();
newPoints = new VectorObjectList<VectorObject>();
numberOfRows = dataFile.getNumberOfRows();
latitudeColumnIndex = -1;
longitudeColumnIndex = -1;
altitudeColumnIndex = -1;
//find the columns used for alt, lat, lon
for (int i = 0; i < headers.size(); i++) {
String header = headers.get(i);
if (header.equalsIgnoreCase(latitudeColumn)) {
latitudeColumnIndex = i;
} else if (header.equalsIgnoreCase(longitudeColumn)) {
longitudeColumnIndex = i;
} else if (header.equalsIgnoreCase(altitudeColumn)) {
altitudeColumnIndex = i;
}
}
for (int i = 0; i < numberOfRows; i++) {
customFields = new HashMap<String,String>();
name = "";
row = dataFile.getRow(i);
if (latitudeColumnIndex >= 0) {
latitude = row.get(latitudeColumnIndex).getFloatValue();
} else {
latitude = (float) Coordinate.UNKNOWN_COORDINATE.getLatitude();
}
if (longitudeColumnIndex >= 0) {
longitude = row.get(longitudeColumnIndex).getFloatValue();
} else {
longitude = (float) Coordinate.UNKNOWN_COORDINATE.getLongitude();
}
if ((altitudeColumnIndex >= 0)) {
altitude = row.get(altitudeColumnIndex).getFloatValue();
} else {
altitude = 0.0f;
}
//create custom fields
for (int headerIndex = 0; headerIndex < headers.size(); headerIndex++) {
if (headers.get(headerIndex).equalsIgnoreCase("Name")) {
name = row.get(headerIndex).trim();
} else if ( (headerIndex != altitudeColumnIndex) && (headerIndex != longitudeColumnIndex) && (headerIndex != latitudeColumnIndex) ) {
customFields.put(headers.get(headerIndex).toString(), row.get(headerIndex).toString());
}
}
if (Coordinate.isLongitudeValid(longitude) && Coordinate.isLatitudeValid(latitude)) {
float alt, lat, lon;
Coordinate coord = new Coordinate(altitude, latitude, longitude);
currentMapPoint = new MapPoint(name, coord, customFields);
currentMapPoint.setClass(objectType);
newPoints.add(currentMapPoint);
} else {
//log errors
errorObjects.add(new MapPoint(name, Coordinate.UNKNOWN_COORDINATE, customFields));
}
}
//write results
results.append("Created ");
results.append(newPoints.size());
results.append(" objects.");
if (errorObjects.size() > 0) {
results.append("\nCould not create ");
results.append(errorObjects.size());
results.append(" objects because of errors.");
results.append("\n\nObjects With Errors:\n");
results.append("--------------------");
for (VectorObject currentErrorObject: errorObjects) {
results.append("\n");
results.append(currentErrorObject.toString());
}
}
return newPoints;
}
/**
* Creates Polygons from the TabularData class.
* @param nameColumn
* @param coordinatesColumn
* @param objectType
*/
public VectorObjectList<VectorObject> createPolygons(String nameColumn, String coordinatesColumn, String objectType) {
ArrayList<String> headers;
ArrayList<DataCell> row;
HashMap<String,String> customFields;
int coordinatesColumnIndex, nameColumnIndex, numberOfRows;
VectorObjectList<VectorObject> newPolygons;
Polygon newPolygon;
String coordinateString, name;
coordinatesColumnIndex = -1;
coordinateString = "";
headers = dataFile.getHeaderNames();
name = "";
nameColumnIndex = -1;
newPolygons = new VectorObjectList<VectorObject>();
numberOfRows = dataFile.getNumberOfRows();
this.errorObjects = new VectorObjectList<VectorObject>();
//find the columns for name, coordinates
for (int i = 0; i < headers.size(); i++) {
String header = headers.get(i);
if (header.equalsIgnoreCase(nameColumn)) {
nameColumnIndex = i;
} else if (header.equalsIgnoreCase(coordinatesColumn)) {
coordinatesColumnIndex = i;
}
}
for (int i = 0; i < numberOfRows; i++) {
customFields = new HashMap<String,String>();
row = dataFile.getRow(i);
if (coordinatesColumnIndex >= 0) {
//coordinates exist
//get name and coordinate string fields
coordinateString = row.get(coordinatesColumnIndex).toString();
name = row.get(nameColumnIndex).toString();
//create custom fields
for (int headerIndex = 0; headerIndex < headers.size(); headerIndex++) {
if ( (headerIndex != nameColumnIndex) && (headerIndex != coordinatesColumnIndex) ) {
customFields.put(headers.get(headerIndex).toString(), row.get(headerIndex).toString());
}
} //end custom fields for loop
//create objects
try {
if (!coordinateString.equals("")) {
newPolygon = new Polygon(name, objectType, coordinateString);
newPolygons.add(newPolygon);
} else {
errorObjects.add(new MapPoint(name, Coordinate.UNKNOWN_COORDINATE, customFields));
}
} catch (Exception e) {
errorObjects.add(new MapPoint(name, Coordinate.UNKNOWN_COORDINATE, customFields));
}
}
} //end for loop
//write results
results.append("Created ");
results.append(newPolygons.size());
results.append(" objects.");
if (errorObjects.size() > 0) {
results.append("\nCould not create ");
results.append(errorObjects.size());
results.append(" objects because of errors.");
results.append("\n\nObjects With Errors:\n");
results.append("--------------------");
for (VectorObject currentErrorObject: errorObjects) {
results.append("\n");
results.append(currentErrorObject.toString());
}
}
return newPolygons;
}
/**
* Return a list of objects that had error during the creating.
* @return
*/
public VectorObjectList<VectorObject> getObjectsWithErrors() {
return errorObjects;
}
/**
* Get the result of the Converstion/Creation.
* @return
*/
public String getResults() {
return results.toString();
}
}