// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.osmrec.features;
import java.util.ArrayList;
import java.util.List;
import org.openstreetmap.josm.plugins.osmrec.container.OSMWay;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import de.bwaldvogel.liblinear.FeatureNode;
/**
* Constructs the geometry feature nodes for liblinear.
*
* @author imis-nkarag
*/
public class GeometryFeatures {
private int id; //= 1422; //pass this as a param from main
private final GeometryFactory geometryFactory = new GeometryFactory();
private static final int NUMBER_OF_AREA_FEATURES = 25;
private static final int NUMBER_OF_POINTS = 13;
private static final int NUMBER_OF_MEAN = 23; //for boolean intervals
private static final int NUMBER_OF_VARIANCE = 37; //for boolean intervals
public GeometryFeatures(int id) {
this.id = id;
}
public void createGeometryFeatures(OSMWay wayNode) {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////// geometry Features ///////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// geometry type feature //
String geometryType = wayNode.getGeometry().getGeometryType();
switch (geometryType) {
//the IDs are unique for each geometry type
case "LineString":
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1));
id += 4;
break;
case "Polygon":
wayNode.getFeatureNodeList().add(new FeatureNode(id+1, 1));
id += 4;
break;
case "LinearRing":
wayNode.getFeatureNodeList().add(new FeatureNode(id+2, 1));
id += 4;
break;
case "Point":
wayNode.getFeatureNodeList().add(new FeatureNode(id+3, 1));
id += 4;
break;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// rectangle geometry shape feature //
//id 1426
if (wayNode.getGeometry().isRectangle()) {
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1.0));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// number of points of geometry feature //
id++; //1427
int numberOfPoints = wayNode.getGeometry().getNumPoints();
numberOfPointsFeature(numberOfPoints, wayNode);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// area of geometry feature //
//id 1440
double area = wayNode.getGeometry().getArea();
if (geometryType.equals("Polygon")) {
areaFeature(area, wayNode);
//the id increases by 25 in the areaFeature method
} else {
id += 25;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// resembles to a circle feature //
//id 1465
if (geometryResemblesCircle(wayNode)) { //this method checks if the shape of the geometry resembles to a circle
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1.0));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// mean edge feature //
id++;
//TOGGLE COMMENT !! commenting out mean and variance to run the best case
Coordinate[] nodeGeometries = wayNode.getGeometry().getCoordinates();
List<Double> edgeLengths = new ArrayList<>();
if (!wayNode.getGeometry().getGeometryType().toUpperCase().equals("POINT")) {
for (int i = 0; i < nodeGeometries.length-1; i++) {
Coordinate[] nodePair = new Coordinate[2];
nodePair[0] = nodeGeometries[i];
nodePair[1] = nodeGeometries[i+1];
LineString tempGeom = geometryFactory.createLineString(nodePair);
edgeLengths.add(tempGeom.getLength());
}
} else {
edgeLengths.add(0.0);
}
double edgeSum = 0;
for (Double edge : edgeLengths) {
edgeSum = edgeSum + edge;
}
double mean = edgeSum/edgeLengths.size();
//intervals with boolean values for mean feature
handleMean(wayNode, mean);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// variance feature//
double sum = 0;
for (Double edge : edgeLengths) {
sum = sum + (edge-mean)*(edge-mean);
}
double normalizedVariance = (sum/edgeLengths.size())/(mean*mean); //normalized with square of mean value
handleVariance(wayNode, normalizedVariance);
setLastID(id);
}
private void handleMean(OSMWay wayNode, double mean) {
if (mean < 2) {
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 4) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+1, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 6) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+2, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 8) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+3, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 10) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+4, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 12) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+5, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 14) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+6, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 16) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+7, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 18) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+8, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 20) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+9, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 25) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+10, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 30) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+11, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 35) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+12, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 40) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+13, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 45) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+14, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 50) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+15, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 60) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+16, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 70) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+17, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 80) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+18, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 90) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+19, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 100) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+20, 1.0));
id = id + NUMBER_OF_MEAN;
} else if (mean < 200) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+21, 1.0));
id = id + NUMBER_OF_MEAN;
} else {
wayNode.getFeatureNodeList().add(new FeatureNode(id+22, 1.0));
id = id + NUMBER_OF_MEAN;
}
}
private void handleVariance(OSMWay wayNode, double normalizedVariance) {
if (normalizedVariance == 0) {
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.005) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+1, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.01) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+2, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.02) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+3, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.03) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+4, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.04) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+5, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.05) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+6, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.06) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+7, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.07) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+8, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.08) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+9, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.09) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+10, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.1) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+11, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.12) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+12, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.14) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+13, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.16) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+14, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.18) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+15, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.20) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+16, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.22) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+17, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.24) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+18, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.26) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+19, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.28) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+20, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.30) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+21, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.32) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+22, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.34) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+23, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.36) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+24, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.38) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+25, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.40) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+26, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.42) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+27, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.44) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+28, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.46) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+29, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.48) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+30, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.5) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+31, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.6) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+32, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.7) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+33, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.8) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+34, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 0.9) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+35, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else if (normalizedVariance < 1) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+36, 1.0));
id = id + NUMBER_OF_VARIANCE;
} else {
wayNode.getFeatureNodeList().add(new FeatureNode(id+37, 1.0));
id = id + NUMBER_OF_VARIANCE;
}
}
private void numberOfPointsFeature(int numberOfPoints, OSMWay wayNode) {
//increase the id after the feature is found for the next portion of the vector.
if (numberOfPoints < 10) {
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 20) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+1, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 30) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+2, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 40) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+3, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 50) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+4, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 75) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+5, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 100) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+6, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 150) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+7, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 200) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+8, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 300) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+9, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 500) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+10, 1.0));
id += NUMBER_OF_POINTS;
} else if (numberOfPoints < 1000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+11, 1.0));
id += NUMBER_OF_POINTS;
} else {
wayNode.getFeatureNodeList().add(new FeatureNode(id+12, 1.0));
id += NUMBER_OF_POINTS;
}
}
private void areaFeature(double area, OSMWay wayNode) {
if (area < 50) {
wayNode.getFeatureNodeList().add(new FeatureNode(id, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 100) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+1, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 150) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+2, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 200) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+3, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 250) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+4, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 300) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+5, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 350) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+6, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 400) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+7, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 450) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+8, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 500) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+9, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 750) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+10, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 1000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+11, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 1250) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+12, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 1500) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+13, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 1750) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+14, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 2000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+15, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 2250) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+16, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 2500) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+17, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 2750) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+18, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 3000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+19, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 3500) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+20, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 4000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+21, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 5000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+22, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else if (area < 10000) {
wayNode.getFeatureNodeList().add(new FeatureNode(id+23, 1.0));
id += NUMBER_OF_AREA_FEATURES;
} else {
wayNode.getFeatureNodeList().add(new FeatureNode(id+24, 1.0));
id += NUMBER_OF_AREA_FEATURES;
}
}
private boolean geometryResemblesCircle(OSMWay way) {
Geometry wayGeometry = way.getGeometry();
boolean isCircle = false;
if (wayGeometry.getGeometryType().equals("Polygon") && wayGeometry.getNumPoints() >= 16) {
List<Geometry> points = way.getNodeGeometries();
Geometry firstPoint = points.get(0);
double radius = firstPoint.distance(wayGeometry.getCentroid());
// buffer around the distance of the first point to centroid
double radiusBufferSmaller = radius*0.6;
//the rest of the point-to-centroid distances will be compared with these
double radiusBufferGreater = radius*1.4;
isCircle = true;
for (Geometry point : points) {
double tempRadius = point.distance(wayGeometry.getCentroid());
boolean tempIsCircle = (radiusBufferSmaller <= tempRadius) && (tempRadius <= radiusBufferGreater);
isCircle = isCircle && tempIsCircle; //if any of the points give a false, the method will return false
//if (!isCircle) {break;}
}
double ratio = wayGeometry.getLength() / wayGeometry.getArea();
boolean tempIsCircle = ratio < 0.06; //arbitary value based on statistic measure of osm instances.
//The smaller this value, the closer this polygon resembles to a circle
isCircle = isCircle && tempIsCircle;
}
return isCircle;
}
private void setLastID(int lastID) {
this.id = lastID;
}
public int getLastID() {
return id + 1;
}
}