package de.lmu.ifi.dbs.elki.datasource.parser;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
Copyright (C) 2011
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.data.ExternalID;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
import de.lmu.ifi.dbs.elki.data.spatial.PolygonsObject;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
/**
* Parser to load polygon data (2D and 3D only) from a simple format. One record
* per line, points separated by whitespace, numbers separated by colons.
* Multiple polygons components can be separated using
* {@link #POLYGON_SEPARATOR}.
*
* Unparseable parts will be treated as labels.
*
* @author Erich Schubert
*
* @apiviz.has PolygonsObject
*/
public class SimplePolygonParser extends AbstractParser implements Parser {
/**
* Class logger
*/
private static final Logging logger = Logging.getLogger(SimplePolygonParser.class);
/**
* Pattern to catch coordinates
*/
public static final Pattern COORD = Pattern.compile("^(" + NUMBER_PATTERN + "),\\s*(" + NUMBER_PATTERN + ")(?:,\\s*(" + NUMBER_PATTERN + "))?$");
/**
* Polygon separator
*/
public static final String POLYGON_SEPARATOR = "--";
/**
* Constructor.
*
* @param colSep
* @param quoteChar
*/
public SimplePolygonParser(Pattern colSep, char quoteChar) {
super(colSep, quoteChar);
}
@Override
public MultipleObjectsBundle parse(InputStream in) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
int lineNumber = 1;
List<PolygonsObject> polys = new ArrayList<PolygonsObject>();
List<LabelList> labels = new ArrayList<LabelList>();
List<ExternalID> eids = new ArrayList<ExternalID>();
try {
for(String line; (line = reader.readLine()) != null; lineNumber++) {
if(!line.startsWith(COMMENT) && line.length() > 0) {
Object[] objs = parseLine(line);
polys.add((PolygonsObject)objs[0]);
labels.add((LabelList)objs[1]);
eids.add((ExternalID)objs[2]);
}
}
}
catch(IOException e) {
throw new IllegalArgumentException("Error while parsing line " + lineNumber + ".");
}
return MultipleObjectsBundle.makeSimple(TypeUtil.POLYGON_TYPE, polys, TypeUtil.LABELLIST, labels, TypeUtil.EXTERNALID, eids);
}
/**
* Parse a single line.
*
* @param line Line to parse
*
* @return Parsed polygon
*/
private Object[] parseLine(String line) {
List<String> entries = tokenize(line);
Iterator<String> iter = entries.iterator();
LabelList labels = new LabelList();
List<Polygon> polys = new java.util.Vector<Polygon>(1);
List<Vector> coords = new ArrayList<Vector>();
while(iter.hasNext()) {
String cur = iter.next();
Matcher m = COORD.matcher(cur);
if(m.find()) {
try {
double c1 = Double.valueOf(m.group(1));
double c2 = Double.valueOf(m.group(2));
if(m.group(3) != null) {
double c3 = Double.valueOf(m.group(3));
coords.add(new Vector(new double[] { c1, c2, c3 }));
}
else {
coords.add(new Vector(new double[] { c1, c2 }));
}
continue;
}
catch(NumberFormatException e) {
logger.warning("Looked like a coordinate pair but didn't parse: " + cur);
}
}
// Polygon separator.
if(cur.equals(POLYGON_SEPARATOR)) {
if(coords.size() > 0) {
polys.add(new Polygon(coords));
coords = new ArrayList<Vector>();
}
continue;
}
// Label
labels.add(cur);
}
// Complete polygon
if(coords.size() > 0) {
polys.add(new Polygon(coords));
}
// Use first label as eternal ID
ExternalID eid = labels.size() > 0 ? new ExternalID(labels.remove(0)) : null;
return new Object[] { new PolygonsObject(polys), labels, eid };
}
@Override
protected Logging getLogger() {
return logger;
}
/**
* Parameterization class.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
public static class Parameterizer extends AbstractParser.Parameterizer {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
}
@Override
protected SimplePolygonParser makeInstance() {
return new SimplePolygonParser(colSep, quoteChar);
}
}
}