/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2011, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.data.efeature.tests.unit;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.query.conditions.eobjects.EObjectCondition;
import org.geotools.data.efeature.DataBuilder;
import org.geotools.data.efeature.DataTypes;
import org.geotools.data.efeature.EFeature;
import org.geotools.data.efeature.EFeatureInfo;
import org.geotools.data.efeature.query.EAttributeValueIsEqual;
import org.geotools.data.efeature.query.EFeatureEncoderException;
import org.geotools.data.efeature.query.EGeometryValueEquals;
import org.geotools.data.efeature.tests.EFeatureCompatibleData;
import org.geotools.data.efeature.tests.EFeatureData;
import org.geotools.data.efeature.tests.EFeatureTestsFactory;
import org.geotools.data.efeature.tests.NonGeoEObject;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
/**
* {@link EFeature} test data class.
* <p>
* This class adds test data to given EMF
* {@link Resource resource} instance.
*
* @author kengu - 20. mai 2011
*
*/
public class EFeatureTestData {
private static final String WKT_COORDS = "%1$s %2$s";
private static final String WKT_GROUP = "(%1$s)";
private static final String WTK_POINT = "POINT "+WKT_GROUP;
private static final String WTK_LINE_STRING = "LINESTRING "+WKT_GROUP;
private static final String WTK_POLYGON = "POLYGON "+WKT_GROUP;
private boolean isInit = false;
private Resource eResource;
private int nonGeoEObjectCount = 0;
private int eFeatureDataCount = 0;
private int eFeatureCompatibleDataCount = 0;
public static void main(String[] args) {
String[] s = generatePointWKTs(10, 0, 20);
for(String it : s) {
System.out.println(it);
}
s = generateLineStringWKTs(10, 0, 20, 1, 5);
for(String it : s) {
System.out.println(it);
}
s = generatePolygonWKTs(10, 0, 20, 1, 5, 1, 10);
for(String it : s) {
System.out.println(it);
}
}
public EFeatureTestData(Resource eResource)
{
this.eResource = eResource;
}
public int getNonGeoEObjectCount() {
return nonGeoEObjectCount;
}
public int getEFeatureDataCount() {
return eFeatureDataCount;
}
public int getEFeatureCompatibleDataCount() {
return eFeatureCompatibleDataCount;
}
/**
* Initialize resource by adding given amount of data into it.
* <p>
* If already initialized, this does nothing.
* <p>
* @param ncount - number of {@link NonGeoEObject} instances
* @param fcount - number of {@link EFeatureData} instances
* @param ccount - number of {@link EFeatureCompatibleData} instances
*
* @throws Exception If initialization fails for some reason
*/
public void random(int ncount, int fcount, int ccount) throws Exception {
//
// Only initialize once
//
if(!isInit) {
//
// Add given amount of NonGeoEObject instances
//
addNonGeoEObjects(ncount);
//
// Calculate number object with geometry data
//
int gcount = fcount + ccount;
//
// Add objects with geometry data?
//
if(gcount>0) {
//
// Get numeric attribute data types
//
List<Class<Number>> aTypes = DataTypes.getSubTypes(Number.class);
//
// Get number of numeric attribute data types
//
int acount = aTypes.size();
//
// Calculate number of attribute values per numeric type
//
int tcount = (int)Math.ceil((double)gcount/(double)acount);
//
// Prepare attribute generating data
//
int i = 0;
int n = Math.min(tcount, gcount);
int count = n;
//
// Generate random numeric data
//
Number[] attributes = new Number[0];
while(i<acount && count<=gcount) {
Class<Number> type = aTypes.get(i);
Number nmin = DataTypes.getMinValue(type);
Number nmax = DataTypes.getMaxValue(type);
attributes = attributes(Number.class, type, attributes, n, nmin, nmax);
count += (n = Math.min(tcount, gcount-count));
i++;
}
//
// Calculate geometry values per unique type
//
tcount = (int)Math.ceil(gcount/3.0);
//
// Prepare geometry data for EFeatureData instances
//
i = 0;
count = (n = Math.min(tcount, gcount));
//
// Generate random geometry data
//
Geometry[] geometries = geometries(null, Geometry.class, generatePointWKTs(n, 0, 20));
count += (n = Math.min(tcount, gcount-count));
if(count<=gcount) {
geometries = geometries(geometries, Geometry.class, generateLineStringWKTs(n, 0, 20, 1, 5));
count += (n = Math.min(tcount, gcount-count));
} if(count<=gcount) {
geometries = geometries(geometries, Geometry.class, generatePolygonWKTs(n, 0, 20, 1, 5, 1, 10));
}
//
// Add objects
//
addFeatureData(0,fcount,attributes, geometries);
addFeatureCompatibleData(fcount,gcount,attributes, geometries);
}
//
// Save counts
//
nonGeoEObjectCount = ncount;
eFeatureDataCount = fcount;
eFeatureCompatibleDataCount = ccount;
//
// Prevent future initializations
//
isInit = true;
}
}
/**
* Save changes to file.
* @throws IOException
*/
public void save() throws IOException {
if(eResource.isLoaded()) {
eResource.save(Collections.emptyMap());
}
}
public <A, G extends Geometry> List<EFeatureData<A, G>> addFeatureData(
Class<G> type, A[] attributes, String... wkts) throws Exception {
G[] g = geometries(null, type, wkts);
return addFeatureData(0, g.length, attributes, g);
}
public <A, G extends Geometry> List<EFeatureCompatibleData<A, G>> addFeatureCompatibleData(
Class<G> type, A[] attributes, String... wkts) throws Exception {
G[] g = geometries(null, type, wkts);
return addFeatureCompatibleData(0, g.length, attributes, g);
}
/**
* Print data statistics
*/
public void print() {
int nCount = 0;
int gCount = 0;
TreeIterator<EObject> it = eResource.getAllContents();
while(it.hasNext()) {
if(it.next() instanceof NonGeoEObject) {
nCount++;
} else {
gCount++;
}
}
System.out.println("NonGeoEObject count: " + nCount);
System.out.println("EFeatureData count: " + gCount);
}
/**
* Dispose this test.
*/
public void dispose() {
eResource = null;
}
/**
* Add given number of {@link NonGeoEObject} instances.
* @param count - number of instances to add
*/
public List<NonGeoEObject> addNonGeoEObjects(int count)
{
List<NonGeoEObject> items = new ArrayList<NonGeoEObject>(count);
if(count>0) {
for(int i=0;i<count;i++) {
NonGeoEObject it = EFeatureTestsFactory.eINSTANCE.createNonGeoEObject();
items.add(it);
}
eResource.getContents().addAll(items);
}
return items;
}
/**
* Add given number of valid {@link EFeatureData} instances.
* @param offset TODO
* @param attrClass - attribute data class
* @param geoClass - geometry data class
* @param eData - array of data
* @param <A> - Attribute data type
* @param <G> - Geometry data type
* @return
*/
public <A, G extends Geometry> List<EFeatureData<A, G>> addFeatureData(int offset, int count, A[] a, G[] g)
{
count = Math.min(count, Math.min(a.length,g.length));
List<EFeatureData<A, G>> items = new ArrayList<EFeatureData<A,G>>(count);
for(int i=offset;i<count;i++) {
EFeatureData<A, G> it = EFeatureTestsFactory.eINSTANCE.<A,G>createEFeatureData();
it.setAttribute(a[i]);
it.setGeometry(g[i]);
eResource.getContents().add(it);
}
return items;
}
/**
* Add given number of valid {@link EFeatureCompatibleData} instances.
* @param offset TODO
* @param attrClass - attribute data class
* @param geoClass - geometry data class
* @param eData - array of data
* @param <A> - Attribute data type
* @param <G> - Geometry data type
* @return
*/
public <A, G extends Geometry> List<EFeatureCompatibleData<A, G>> addFeatureCompatibleData(int offset, int count, A[] a, G[] g)
{
count = Math.min(count, Math.min(a.length,g.length));
List<EFeatureCompatibleData<A, G>> items = new ArrayList<EFeatureCompatibleData<A,G>>(count);
for(int i=offset;i<count;i++) {
EFeatureCompatibleData<A, G> it = EFeatureTestsFactory.eINSTANCE.<A,G>createEFeatureCompatibleData();
it.setAttribute(a[i]);
it.setGeometry(g[i]);
eResource.getContents().add(it);
}
return items;
}
public static String[] generateCoordWKTs(int count, int min, int max)
{
String[] coords = new String[count];
for(int i=0; i<count; i++) {
int x = (int)(Math.random()*max + min);
int y = (int)(Math.random()*max + min);
coords[i] = String.format(WKT_COORDS,x,y);
}
return coords;
}
public static String toCSV(String[] items) {
StringBuffer b = new StringBuffer(items[0]);
int count = items.length;
for(int i=1; i<count; i++) {
b.append(", "+items[i]);
}
return b.toString();
}
public static String[] generatePointWKTs(int count, int min, int max) {
String[] points = new String[count];
String[] coords = generateCoordWKTs(count, min, max);
for(int i=0; i<count; i++) {
points[i] = String.format(WTK_POINT, coords[i]);
}
return points;
}
public static String[] generateLineStringWKTs(int count, int cmin, int cmax, int lmin, int lmax) {
lmin = Math.max(2, lmin);
lmax = Math.max(2, lmax);
String[] lines = new String[count];
for(int i=0; i<count; i++) {
int l = (int)(Math.random()*lmax + lmin);
String[] coords = generateCoordWKTs(l, cmin, cmax);
lines[i] = String.format(WTK_LINE_STRING, toCSV(coords));
}
return lines;
}
public static String[] generatePolygonWKTs(int count, int cmin, int cmax, int lmin, int lmax, int pmin, int pmax) {
lmin = Math.max(4, lmin);
lmax = Math.max(4, lmax);
pmin = Math.max(2, pmin);
pmax = Math.max(2, pmax);
String[] polygons = new String[count];
for(int i=0; i<count; i++) {
int p = (int)(Math.random()*pmax + pmin);
String[] lines = new String[p];
for(int j=0; j<p; j++) {
int l = (int)(Math.random()*lmax + lmin);
String[] coords = generateCoordWKTs(l, cmin, cmax);
coords[coords.length-1] = coords[0];
lines[j] = String.format(WKT_GROUP, toCSV(coords));
}
polygons[i] = String.format(WTK_POLYGON, toCSV(lines));
}
return polygons;
}
@SuppressWarnings("unchecked")
public static <A ,N extends Number> A[] attributes(Class<A> attClass, Class<N> numClass,
A[] attributes, int count, N min, N max) throws ParseException {
//
// Initialize list
//
List<Object> items = null;
if(attributes==null) {
items = new ArrayList<Object>(count);
} else {
items = new ArrayList<Object>(Arrays.asList(attributes));
}
//
// Create random numbers
//
for(int i=0; i<count; i++) {
N v = scale(numClass,Math.random(),min,max);
items.add(v);
}
return items.toArray((A[])Array.newInstance(attClass, items.size()));
}
private static <N extends Number> N scale(Class<N> type, double value, N min, N max) {
if(type.isAssignableFrom(Integer.class))
return type.cast(Integer.valueOf((int)(value*max.intValue() + min.intValue())));
if(type.isAssignableFrom(Double.class))
return type.cast((value*max.doubleValue() + min.doubleValue()));
if(type.isAssignableFrom(Long.class))
return type.cast(Long.valueOf((long)(value*max.longValue() + min.longValue())));
if(type.isAssignableFrom(Short.class))
return type.cast(Short.valueOf((short)(value*max.shortValue() + min.shortValue())));
if(type.isAssignableFrom(Byte.class))
return type.cast(Byte.valueOf((byte)(value*max.byteValue() + min.byteValue())));
if(type.isAssignableFrom(Float.class))
return type.cast(Float.valueOf((float)(value*max.floatValue() + min.floatValue())));
return null;
}
@SuppressWarnings("unchecked")
public static <G extends Geometry> G[] geometries(G[] geometries,
Class<G> geoClass, String... wkts) throws ParseException {
//
// Initialize list
//
List<G> items = null;
if(geometries==null) {
items = new ArrayList<G>(wkts.length);
} else {
items = new ArrayList<G>(Arrays.asList(geometries));
}
for(String wkt : wkts) {
Geometry geom = DataBuilder.toGeometry(wkt);
if(geoClass.isInstance(geom)) {
items.add(geoClass.cast(geom));
}
}
return items.toArray((G[]) Array.newInstance(geoClass, items.size()));
}
public <A, G extends Geometry> Object[] generateData(Object[] data, A[] attributes, G[] geometries) {
//
// Prepare to merge
//
int cmin = 0;
int cmax = 0;
Object[] amin = null;
Object[] amax = null;
if(attributes.length<geometries.length) {
cmin = attributes.length;
cmax = geometries.length;
amin = attributes;
amax = geometries;
} else {
cmin = geometries.length;
cmax = attributes.length;
amin = geometries;
amax = attributes;
}
//
// Initialize list
//
List<Object> items = null;
if(data==null) {
items = new ArrayList<Object>(cmax);
} else {
items = new ArrayList<Object>(Arrays.asList(data));
}
//
// Order on array length
//
//
// Merge into one continuous array of objects
//
for(int i=0; i<cmin; i++) {
items.add(amin[i]);
items.add(amax[i]);
}
//
// Add residue
//
for(int i=cmin;i<cmax;i++) {
items.add(amax[i]);
}
//
// Finished
//
return items.toArray(new Object[items.size()]);
}
// -----------------------------------------------------
// Static utility methods
// -----------------------------------------------------
public static EObjectCondition newIsEqual(EAttribute eAttribute, String value,
EFeatureInfo... eFeatureInfo) throws EFeatureEncoderException
{
if(eFeatureInfo.length>0) {
eAttribute = eFeatureInfo[0].eMappedTo(eAttribute);
}
return new EAttributeValueIsEqual(eAttribute,value);
}
public static EObjectCondition newIsEqual(EAttribute eAttribute, Integer value,
EFeatureInfo...eFeatureInfo) throws EFeatureEncoderException
{
if(eFeatureInfo.length>0) {
eAttribute = eFeatureInfo[0].eMappedTo(eAttribute);
}
return new EAttributeValueIsEqual(eAttribute,value);
}
public static EObjectCondition newIsEqual(EAttribute eAttribute, Geometry value,
EFeatureInfo...eFeatureInfo) throws EFeatureEncoderException
{
if(eFeatureInfo.length>0) {
eAttribute = eFeatureInfo[0].eMappedTo(eAttribute);
}
return new EGeometryValueEquals(eAttribute,value,false);
}
}