// Script.java
// Java Spatial Index Library
// Copyright (C) 2002-2005 Infomatiq Limited.
//
// 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; either
// version 2.1 of the License, or (at your option) any later version.
//
// 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.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package net.sf.jsi;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.StringTokenizer;
import junit.framework.TestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Script
*/
public class Script {
private static final Logger log = LoggerFactory.getLogger(Script.class);
static final int REFERENCE_COMPARISON = 1;
static final int REFERENCE_GENERATE = 2;
private float canvasSize = 100000F;
private void writeOutput(String outputLine, PrintWriter outputFile, LineNumberReader referenceFile) {
try {
outputFile.println(outputLine);
outputFile.flush();
if (referenceFile != null) {
String referenceLine = referenceFile.readLine();
if (!outputLine.equals(referenceLine)) {
log.error("Output does not match reference on line " + referenceFile.getLineNumber());
log.error(" Reference result: " + referenceLine);
log.error(" Test result: " + outputLine);
TestCase.assertTrue("Output does not match reference on line " + referenceFile.getLineNumber(), false);
}
}
} catch (IOException e) {
log.error("IOException while writing test results");
}
}
private float quantize(double d, int quantizer) {
if (quantizer <= 0) {
return (float) d;
}
d /= quantizer;
d = Math.round(d);
d *= quantizer;
return (float) d;
}
private Rectangle getRandomRectangle(Random r, float rectangleSize, float canvasSize, int quantizer) {
float x1 = quantize(r.nextGaussian() * canvasSize, quantizer);
float y1 = quantize(r.nextGaussian() * canvasSize, quantizer);
float x2 = x1 + quantize(r.nextGaussian() * rectangleSize, quantizer);
float y2 = y1 + quantize(r.nextGaussian() * rectangleSize, quantizer);
return new Rectangle(x1, y1, x2, y2);
}
/**
* @return Time taken to execute method, in milliseconds.
*/
public long run(String indexType, Properties indexProperties, String testId, int testType) {
if (log.isInfoEnabled()) {
log.info("runScript: " + indexType + ", testId=" + testId);
if (indexProperties != null) {
log.info("minEntries=" + indexProperties.getProperty("MinNodeEntries") +
", maxEntries=" + indexProperties.getProperty("MaxNodeEntries") +
", treeVariant=" + indexProperties.getProperty("TreeVariant"));
}
}
SpatialIndex si = SpatialIndexFactory.newInstance(indexType, indexProperties);
ListDecorator ld = new SortedListDecorator(si);
Random random = new Random();
DecimalFormat df = new DecimalFormat();
df.setMinimumFractionDigits(4);
df.setMaximumFractionDigits(4);
df.setMinimumIntegerDigits(7);
df.setMaximumIntegerDigits(7);
df.setPositivePrefix(" ");
df.setGroupingUsed(false);
int quantizer = -1;
String strTestInputRoot = "/test-inputs" + File.separator + "test-" + testId;
String strTestResultsRoot = "target/test-results" + File.separator + "test-" + testId;
// open test input file for read-only
LineNumberReader inputFile = null;
String inputFilename = strTestInputRoot + "-in";
try {
inputFile = new LineNumberReader(new InputStreamReader(getClass().getResourceAsStream(inputFilename)));
}
catch (Throwable t) {
log.error("Unable to open test input file " + inputFilename);
TestCase.assertTrue("Unable to open test input file " + inputFilename, false);
return -1;
}
// open reference results file for read-only. Filename is of form:
// test-testId-reference
LineNumberReader referenceFile = null;
if (testType == REFERENCE_COMPARISON) {
String referenceFilename = strTestResultsRoot + "-reference";
try {
referenceFile = new LineNumberReader(new InputStreamReader(new FileInputStream(referenceFilename)));
} catch (FileNotFoundException e) {
log.error("Unable to open reference test results file " + referenceFilename);
TestCase.assertTrue("Unable to open reference test results file " + referenceFilename, false);
return -1;
}
}
// open actual results file for writing. Filename is of form
// test-testId-indexType-revision-datetime, unless generating reference results.
PrintWriter outputFile = null;
String outputFilename = null;
if (testType == REFERENCE_COMPARISON) {
outputFilename = strTestResultsRoot + "-" + si.getVersion() +
"-" + new SimpleDateFormat("yyMMddHHmmss").format(new Date());
} else {
outputFilename = strTestResultsRoot + "-reference";
if (new File(outputFilename).exists()) {
log.info("Reusing existing reference file: " + outputFilename);
return 0;
}
}
new File(outputFilename).getParentFile().mkdirs();
try {
outputFile = new PrintWriter(new FileOutputStream(outputFilename));
} catch (FileNotFoundException e) {
log.error("Unable to open test output results file " + outputFilename);
TestCase.assertTrue("Unable to open test output results file " + outputFilename, false);
return -1;
}
long scriptStartTime = System.currentTimeMillis();
try {
// read lines from the test input file
while (inputFile.ready()) {
String inputLine = inputFile.readLine();
if (inputLine.startsWith("#")) {
continue;
}
StringBuffer outputBuffer = new StringBuffer(inputLine);
StringTokenizer st = new StringTokenizer(inputLine);
while (st.hasMoreTokens()) {
String operation = st.nextToken().toUpperCase();
if (operation.equals("DISTANCEQUANTIZER")) {
quantizer = Integer.parseInt(st.nextToken());
} else if (operation.equals("RANDOMIZE")) {
random.setSeed(Integer.parseInt(st.nextToken()));
writeOutput(outputBuffer.toString() + " : OK", outputFile, referenceFile);
} else if (operation.equals("ADDRANDOM")) {
int count = Integer.parseInt(st.nextToken());
int startId = Integer.parseInt(st.nextToken());
float rectangleSize = Float.parseFloat(st.nextToken());
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
long startTime = System.currentTimeMillis();
for (int id = startId; id < startId + count; id++) {
Rectangle r = getRandomRectangle(random, rectangleSize, canvasSize, quantizer);
si.add(r, id);
String outputLine = " " + id + " " + r.toString() + " : OK";
writeOutput(outputLine, outputFile, referenceFile);
}
long time = System.currentTimeMillis() - startTime;
if (log.isDebugEnabled()) {
log.debug("Added " + count + " entries in " + time + "ms (" + time / (float) count + " ms per add)");
}
} else if (operation.equals("DELETERANDOM")) {
int count = Integer.parseInt(st.nextToken());
int startId = Integer.parseInt(st.nextToken());
float rectangleSize = Float.parseFloat(st.nextToken());
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
long startTime = System.currentTimeMillis();
int successfulDeleteCount = 0;
for (int id = startId; id < startId + count; id++) {
Rectangle r = getRandomRectangle(random, rectangleSize, canvasSize, quantizer);
boolean deleted = si.delete(r, id);
if (deleted) {
successfulDeleteCount++;
}
String outputLine = " " + id + " " + r.toString() + " : " + deleted;
writeOutput(outputLine, outputFile, referenceFile);
}
long time = System.currentTimeMillis() - startTime;
if (log.isDebugEnabled()) {
log.debug("Attempted to delete " + count + " entries (" + successfulDeleteCount + " successful) in " + time + "ms (" + time / (float) count + " ms per delete)");
}
}
else if (operation.equals("NEARESTRANDOM")) {
int queryCount = Integer.parseInt(st.nextToken());
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
long startTime = System.currentTimeMillis();
int totalEntriesReturned = 0;
for (int id = 0; id < queryCount; id++) {
float x = (float) random.nextGaussian() * canvasSize;
float y = (float) random.nextGaussian() * canvasSize;
List<Integer> l = ld.nearest(new Point(x, y), Float.POSITIVE_INFINITY);
totalEntriesReturned += l.size();
StringBuffer tempBuffer = new StringBuffer(" " + id + " " +
df.format(x) + " " +
df.format(y) + " : OK");
Iterator<Integer> i = l.iterator();
while (i.hasNext()) {
tempBuffer.append(' ');
tempBuffer.append(i.next()).toString();
}
writeOutput(tempBuffer.toString(), outputFile, referenceFile);
}
long time = System.currentTimeMillis() - startTime;
if (log.isInfoEnabled()) {
log.info("NearestQueried " + queryCount + " times in " + time + "ms. Per query: " + time / (float) queryCount + " ms, " + (totalEntriesReturned / (float) queryCount) + " entries");
}
} else if (operation.equals("NEARESTNRANDOM")) {
int queryCount = Integer.parseInt(st.nextToken());
int n = Integer.parseInt(st.nextToken());
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
long startTime = System.currentTimeMillis();
int totalEntriesReturned = 0;
for (int id = 0; id < queryCount; id++) {
float x = (float) random.nextGaussian() * canvasSize;
float y = (float) random.nextGaussian() * canvasSize;
List<Integer> l = ld.nearestN(new Point(x, y), n, Float.POSITIVE_INFINITY);
totalEntriesReturned += l.size();
StringBuffer tempBuffer = new StringBuffer(" " + id + " " +
df.format(x) + " " +
df.format(y) + " : OK");
Iterator<Integer> i = l.iterator();
while (i.hasNext()) {
tempBuffer.append(' ');
tempBuffer.append(i.next()).toString();
}
writeOutput(tempBuffer.toString(), outputFile, referenceFile);
}
long time = System.currentTimeMillis() - startTime;
if (log.isInfoEnabled()) {
log.info("NearestNQueried " + queryCount + " times in " + time + "ms. Per query: " + time / (float) queryCount + " ms, " + (totalEntriesReturned / (float) queryCount) + " entries");
}
} else if (operation.equals("INTERSECTRANDOM")) {
int queryCount = Integer.parseInt(st.nextToken());
float rectangleSize = Float.parseFloat(st.nextToken());
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
long startTime = System.currentTimeMillis();
int totalEntriesReturned = 0;
for (int id = 0; id < queryCount; id++) {
Rectangle r = getRandomRectangle(random, rectangleSize, canvasSize, quantizer);
List<Integer> l = ld.intersects(r);
totalEntriesReturned += l.size();
Iterator<Integer> i = l.iterator();
StringBuffer tempBuffer = new StringBuffer(" " + id + " " + r.toString() + " : OK");
while (i.hasNext()) {
tempBuffer.append(' ');
tempBuffer.append(i.next()).toString();
}
writeOutput(tempBuffer.toString(), outputFile, referenceFile);
}
long time = System.currentTimeMillis() - startTime;
if (log.isInfoEnabled()) {
log.info("IntersectQueried " + queryCount + " times in " + time + "ms. Per query: " + time / (float) queryCount + " ms, " + (totalEntriesReturned / (float) queryCount) + " entries");
}
}
else if (operation.equals("CONTAINSRANDOM")) {
int queryCount = Integer.parseInt(st.nextToken());
float rectangleSize = Float.parseFloat(st.nextToken());
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
long startTime = System.currentTimeMillis();
int totalEntriesReturned = 0;
for (int id = 0; id < queryCount; id++) {
Rectangle r = getRandomRectangle(random, rectangleSize, canvasSize, quantizer);
List<Integer> l = ld.contains(r);
totalEntriesReturned += l.size();
Iterator<Integer> i = l.iterator();
StringBuffer tempBuffer = new StringBuffer(" " + id + " " + r.toString() + " : OK");
while (i.hasNext()) {
tempBuffer.append(' ');
tempBuffer.append((Integer)i.next()).toString();
}
writeOutput(tempBuffer.toString(), outputFile, referenceFile);
}
long time = System.currentTimeMillis() - startTime;
if (log.isInfoEnabled()) {
log.info("ContainsQueried " + queryCount + " times in " + time + "ms. Per query: " + time / (float) queryCount + " ms, " + (totalEntriesReturned / (float) queryCount) + " entries");
}
}
else if (operation.equals("ADD")) {
int id = Integer.parseInt(st.nextToken());
float x1 = Float.parseFloat(st.nextToken());
float y1 = Float.parseFloat(st.nextToken());
float x2 = Float.parseFloat(st.nextToken());
float y2 = Float.parseFloat(st.nextToken());
si.add(new Rectangle(x1, y1, x2, y2), id);
outputBuffer.append(" : OK");
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
}
else if (operation.equals("DELETE")) {
int id = Integer.parseInt(st.nextToken());
float x1 = Float.parseFloat(st.nextToken());
float y1 = Float.parseFloat(st.nextToken());
float x2 = Float.parseFloat(st.nextToken());
float y2 = Float.parseFloat(st.nextToken());
boolean deleted = si.delete(new Rectangle(x1, y1, x2, y2), id);
if (deleted) {
outputBuffer.append(" : OK");
} else {
outputBuffer.append(" : Not found");
}
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
}
else if (operation.equals("NEAREST")) {
float x = Float.parseFloat(st.nextToken());
float y = Float.parseFloat(st.nextToken());
List<Integer> l = ld.nearest(new Point(x, y), Float.POSITIVE_INFINITY);
outputBuffer.append(" : OK");
Iterator<Integer> i = l.iterator();
while (i.hasNext()) {
outputBuffer.append(" ");
outputBuffer.append((Integer)i.next()).toString();
}
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
}
else if (operation.equals("INTERSECT")) {
float x1 = Float.parseFloat(st.nextToken());
float y1 = Float.parseFloat(st.nextToken());
float x2 = Float.parseFloat(st.nextToken());
float y2 = Float.parseFloat(st.nextToken());
List<Integer> l = ld.intersects(new Rectangle(x1, y1, x2, y2));
outputBuffer.append(" : OK");
Iterator<Integer> i = l.iterator();
while (i.hasNext()) {
outputBuffer.append(" ");
outputBuffer.append(i.next()).toString();
}
writeOutput(outputBuffer.toString(), outputFile, referenceFile);
}
} // for each token on the current input line
} // for each input line
} catch (IOException e) {
log.error("IOException while running test script in SpatialIndexTest", e);
return -1;
}
long scriptEndTime = System.currentTimeMillis();
// try and clean up the largest objects to prevent garbage collection
// from slowing down a future run.
ld = null;
si = null;
System.gc();
return scriptEndTime - scriptStartTime;
}
}