├── LICENSE.txt ├── README.markdown ├── examples ├── rangequery-pig ├── rangequery-pigeon ├── rangequery-ways ├── trajectory.pig └── trajectory.tsv ├── pom.xml └── src ├── main ├── assembly │ └── assembly.xml ├── java │ └── edu │ │ └── umn │ │ └── cs │ │ └── pigeon │ │ ├── Area.java │ │ ├── AsHex.java │ │ ├── AsText.java │ │ ├── Boundary.java │ │ ├── Break.java │ │ ├── Buffer.java │ │ ├── Connect.java │ │ ├── Contains.java │ │ ├── ConvexHull.java │ │ ├── Crosses.java │ │ ├── Decompose.java │ │ ├── Difference.java │ │ ├── ESRIGeometryParser.java │ │ ├── ESRIShapeFromText.java │ │ ├── ESRIShapeFromWKB.java │ │ ├── Envelope.java │ │ ├── Extent.java │ │ ├── GeoException.java │ │ ├── GeometryFromText.java │ │ ├── GeometryFromWKB.java │ │ ├── GridCell.java │ │ ├── GridPartition.java │ │ ├── Intersection.java │ │ ├── Intersects.java │ │ ├── IsEmpty.java │ │ ├── IsValid.java │ │ ├── JTSGeometryParser.java │ │ ├── MakeBox.java │ │ ├── MakeLine.java │ │ ├── MakeLinePolygon.java │ │ ├── MakePoint.java │ │ ├── MakePolygon.java │ │ ├── MakeSegments.java │ │ ├── NumPoints.java │ │ ├── OGCGeometryToESRIShapeParser.java │ │ ├── Overlaps.java │ │ ├── SJPlaneSweep.java │ │ ├── Touches.java │ │ ├── Union.java │ │ ├── Within.java │ │ ├── XMax.java │ │ ├── XMin.java │ │ ├── YMax.java │ │ └── YMin.java └── resources │ ├── pigeon_import.pig │ └── sample.pig └── test └── java └── edu └── umn └── cs └── pigeon ├── TestArea.java ├── TestAsText.java ├── TestBounds.java ├── TestBreak.java ├── TestBuffer.java ├── TestConnect.java ├── TestConvexHull.java ├── TestCrosses.java ├── TestDifference.java ├── TestEnvelope.java ├── TestExtent.java ├── TestGeometryParser.java ├── TestGridPartition.java ├── TestHelper.java ├── TestMakeBox.java ├── TestMakeLine.java ├── TestMakeLinePolygon.java ├── TestMakePoint.java ├── TestMakePolygon.java ├── TestMakeSegments.java ├── TestNumPoints.java ├── TestUnion.java └── TestXMin.java /LICENSE.txt: -------------------------------------------------------------------------------- 1 | About This Content 2 | 2015 3 | 4 | License 5 | The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the Apache License, Version 2.0. A copy of the Apache License, Version 2.0 is available at http://www.opensource.org/licenses/apache2.0.php. 6 | 7 | If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may apply to your use of any object code in the Content. Check the Redistributor’s license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise indicated below, the terms and conditions of the Apache License, Version 2.0 still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org. 8 | 9 | 10 | Third Party Content 11 | The Content includes items that have been sourced from third parties as set out below. If you did not receive this Content directly from the Eclipse Foundation, the following is provided for informational purposes only, and you should look to the Redistributor’s license for terms and conditions of use. 12 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Pigeon 2 | ====== 3 | 4 | Pigeon is a spatial extension to Pig that allows it to process spatial data. 5 | All functionalities in Pigeon are introduced as user-defined functions (UDFS) 6 | which makes it unobtrusive and allows it to work with your existing systems. 7 | All the spatial functionality is supported by [ESRI Geometry API] 8 | (https://github.com/Esri/geometry-api-java) 9 | a native Java open source library for spatial functionality licensed under 10 | [Apache Public License](http://www.apache.org/licenses/LICENSE-2.0.html). 11 | 12 | 13 | Our target is to have something like [PostGIS](http://postgis.net/) but for Pig 14 | instead of PostgreSQL. We use the same function names to make it easier for 15 | existing users to use Pigeon. Here is an example the computes the union of all 16 | ZIP codes in each city. 17 | 18 | zip_codes = LOAD 'zips' AS (zip, city, geom); 19 | zip_by_city = GROUP zip_codes BY city; 20 | zip_union = FOREACH zip_by_city 21 | GENERATE group AS city, ST_Union(geom); 22 | 23 | 24 | Data types 25 | ========== 26 | Currently, Pig does not support the creation of custom data types. This is not 27 | the best thing for Pigeon because we wanted to have our own data type (Geometry) similar to 28 | PostGIS. As a work around, we use the more generic type `bytearray` as our main 29 | data type. All conversions happen from `bytearray` to `Geometry` and vice-verse on 30 | the fly while the function is executed. If a function expects an input of type 31 | Geometry, it receives a `bytearray` and converts it `Geometry`. If the output is 32 | of type `Geometry`, it computes the output, converts it to `bytearray`, and returns 33 | that `bytearray` instead. This is a little bit cumbersome, but the Pig team is 34 | able to add custom data types so that we have a cleaner extension. 35 | 36 | 37 | How to compile 38 | ============== 39 | Pigeon requires ESRI Geometry API to compile. You need to 40 | download a recent version of the library and add it to the classpath of your Java compiler 41 | to be able to compile the code. Of course you also need Pig classes to be available 42 | in the classpath. The current code is tested against ESRI Geometry API 1.0 and Pig 0.11.1. 43 | Currently you have to do the compilation manually but we are planning to create 44 | an ANT build file to automate the compilation. Once you compile the code, you 45 | can create a jar file out of it and [REGISTER](http://pig.apache.org/docs/r0.11.1/basic.html#register) it in your Pig scripts. 46 | 47 | 48 | How to use 49 | ========== 50 | To use Pigeon in your Pig scripts, you need to [REGISTER](http://pig.apache.org/docs/r0.11.1/basic.html#register) the jar file in your Pig 51 | script. Then you can use the spatial functionality in your script as you cdo with 52 | normal functionality. Here are some simple examples on how to use Pigeon. 53 | 54 | Let's say you have a trajectory in the form (latitude, longitude, timestamp). We need to 55 | for a Linestring out of this trajectory when points in this linestring are sorted 56 | by timestamp. 57 | 58 | 59 | points = LOAD 'trajectory.tsv' AS (time: datetime, lat:double, lon:double); 60 | s_points = FOREACH points GENERATE ST_MakePoint(lat, lon) AS point, time; 61 | points_by_time = ORDER s_points BY time; 62 | points_grouped = GROUP points_by_time ALL; 63 | lines = FOREACH points_grouped GENERATE ST_AsText(ST_MakeLine(points_by_time)); 64 | STORE lines INTO 'line'; 65 | 66 | 67 | Supported functions 68 | =================== 69 | Here is a list of all functions that are currently supported. 70 | 71 | Basic Spatial Functions 72 | ----------------------- 73 | 74 | + *ST_AsHex* Converts a shape to its Well-Known Binary (WKB) format encoded as Hex string 75 | + *ST_AsText* Converts a shape to its Well-Known Text (WKT) format 76 | + *ST_GeomFromText* Parses a WKT representation into an object 77 | + *ST_GeomFromWKT* Parses an object from a hex-encoded WKB representation 78 | + *ST_MakePoint* Creates a geometry point given two numeric coordinates 79 | + *ST_MakeBox* Creates a rectangle from its four coordinates (x1, y1, x2, y2) 80 | + *ST_Area* Calculates the area of a surface shape (e.g., Polygon) 81 | + *ST_Envelope* Calculates the envelope (MBR) of a shape 82 | + *ST_Buffer* Computes a buffer with the specified distance around a geometry. 83 | + *ST_NumPoints* Returns number of points in a linestring 84 | + *ST_XMin* Returns the minimum x-coordinate of an object 85 | + *ST_YMin* Returns the minimum y-coordinate of an object 86 | + *ST_XMax* Returns the maximum x-coordinate of an object 87 | + *ST_YMax* Returns the maximum y-coordinate of an object 88 | 89 | 90 | Spatial Predicates 91 | ------------------ 92 | 93 | + *ST_Crosses* Checks if one polygon crosses another polygon 94 | + *ST_IsEmpty* Tests whether a shape is empty or not. 95 | + *ST_Contains* Tests for containment between two geometries 96 | + *ST_Intersects* Tests if two objects intersect 97 | + *ST_Overlaps* Tests if two objects overlap 98 | + *ST_Touches* Tests if two objects touch 99 | + *ST_Within* Tests if one geometry is withing another geometry 100 | 101 | Spatial Analysis 102 | ---------------- 103 | 104 | + *ST_Buffer* Computes a buffer with the specified distance around a geometry. 105 | + *ST_ConvexHull* Computes the minimal convex polygon of a shape. 106 | + *ST_Break* Breaks a geometry into straight line segments 107 | + *ST_Difference* Calculates the difference between two polygons. 108 | + *ST_Intersection* Computes the intersection betwen two polygons. 109 | + *ST_MakeSegments* Creates a set of line segments given a set of points by connecting each two consecutive points 110 | 111 | Aggregate functions 112 | ------------------- 113 | 114 | + *ST_MakeLine* Creates a line string given a bag of points 115 | + *ST_MakePolygon* Creates a polygon given a circular list of points 116 | + *ST_MakeLinePolygon* Creates a line or polygons from a list of points depending on whether the last point is the same as the first point or not 117 | + *ST_ConvexHull* Computes the convex hull from a bag of shapes 118 | + *ST_Union* Computes the spatial union of a set of surfaces (e.g., Polygons) 119 | + *ST_Extent* Computes the minimal bounding rectangle (MBR) of a set of shapes 120 | + *ST_Connect* Connects a set of linestrings together to form longer linestring or polygons 121 | 122 | Contribution 123 | ============ 124 | Pigeon is open source and licensed under the Apache open source license. Your 125 | contribution is highly welcome and appreciated. Here is a simple guideline of 126 | how to contribute. 127 | 128 | 1. Clone your own copy of the source code or fork the project in github. 129 | 2. Pick an issue from the list of issues associated with the project. 130 | 3. Write a test case for the new functionality and make sure it fails. 131 | 4. Fix the code so that the test case succeeds. 132 | 5. Make sure that all existing tests still pass. 133 | 6. Revise all your changes and add comments whenever needed. Make sure you 134 | don't make any unnecessary changes (e.g., reformatting). 135 | 7. Submit a pull request if you are a github user or send a patch if you aren't. 136 | 8. We will revise the submitted patch and merge it with the code when done. 137 | 138 | When writing the test, keep in mind that we are not testing Pig or ESRI Geometry API. We are 139 | testing Pigeon which is a wrapper around ESRI Geometry API. For example, you don't have to test 140 | all the special cases of polygons if implementing an intersection function. 141 | All what we want is to make sure that you call the right function in ESRI Geometry API and 142 | return the output in the correct format. 143 | 144 | Mainly, we need to support as many as possible of the 145 | [functions](http://postgis.net/docs/manual-1.4/ch08.html) provided by PostGIS. 146 | -------------------------------------------------------------------------------- /examples/rangequery-pig: -------------------------------------------------------------------------------- 1 | REGISTER osmx.jar; 2 | REGISTER pigeon.jar 3 | REGISTER esri-geometry-api-1.0.jar; 4 | 5 | IMPORT 'pigeon_import.pig'; 6 | 7 | points = LOAD 'points' AS (id:long, lon:double, lat:double); 8 | 9 | results = FILTER points BY 10 | lon < -93.158 AND lon > -93.175 AND 11 | lat > 45.0077 AND lat < 45.0164; 12 | 13 | STORE results INTO 'results'; -------------------------------------------------------------------------------- /examples/rangequery-pigeon: -------------------------------------------------------------------------------- 1 | REGISTER osmx.jar; 2 | REGISTER pigeon.jar 3 | REGISTER esri-geometry-api-1.0.jar; 4 | 5 | IMPORT 'pigeon_import.pig'; 6 | 7 | points = LOAD 'points-pigeon' AS (id:long, location); 8 | 9 | results = FILTER points BY 10 | ST_Contains(ST_MakeBox(-93.175, 45.0077, -93.158, 45.0164), location); 11 | 12 | STORE results INTO 'results-pigeon'; -------------------------------------------------------------------------------- /examples/rangequery-ways: -------------------------------------------------------------------------------- 1 | REGISTER osmx.jar; 2 | REGISTER pigeon.jar 3 | REGISTER esri-geometry-api-1.0.jar; 4 | 5 | IMPORT 'pigeon_import.pig'; 6 | 7 | points = LOAD 'points-pigeon' AS (id:long, shape); 8 | 9 | results = FILTER points BY 10 | ST_Overlaps(ST_MakeBox(-93.175, 45.0077, -93.158, 45.0164), shape); 11 | 12 | STORE results INTO 'results-ways'; -------------------------------------------------------------------------------- /examples/trajectory.pig: -------------------------------------------------------------------------------- 1 | REGISTER pigeon.jar; 2 | REGISTER jts-1.8.jar; 3 | 4 | IMPORT 'pigeon_import.pig'; 5 | 6 | points = LOAD 'trajectory.tsv' AS (type, time: datetime, lat:double, lon:double); 7 | s_points = FOREACH points GENERATE ST_MakePoint(lat, lon) AS point, time; 8 | points_by_time = ORDER s_points BY time; 9 | points_grouped = GROUP points_by_time ALL; 10 | lines = FOREACH points_grouped GENERATE ST_AsText(ST_MakeLine(points_by_time)); 11 | STORE lines INTO 'line'; 12 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | edu.umn.cs.spatialHadoop 5 | pigeon 6 | jar 7 | 0.2.2 8 | pigeon 9 | http://maven.apache.org 10 | 11 | 12 | 13 | Apache License, Version 2.0 14 | http://www.apache.org/licenses/LICENSE-2.0.txt 15 | 16 | 17 | 18 | 19 | scm:git:git@github.com:aseldawy/pigeon.git 20 | scm:git:git@github.com:aseldawy/pigeon.git 21 | git@github.com:aseldawy/pigeon.git 22 | 23 | 24 | 25 | 26 | Ahmed Eldawy 27 | eldawy@cs.ucr.edu 28 | University of California, Riverside 29 | http://www.ucr.edu/ 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-compiler-plugin 39 | 3.1 40 | 41 | 1.6 42 | 1.6 43 | 44 | 45 | 46 | 47 | maven-assembly-plugin 48 | 2.6 49 | 50 | 51 | src/main/assembly/assembly.xml 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | junit 61 | junit 62 | 4.11 63 | test 64 | 65 | 66 | 67 | org.apache.pig 68 | pigunit 69 | 0.17.0 70 | test 71 | 72 | 73 | 74 | 75 | org.apache.hadoop 76 | hadoop-client 77 | 3.2.0 78 | 79 | 80 | 81 | org.apache.pig 82 | pig 83 | 0.17.0 84 | 85 | 86 | 87 | com.esri.geometry 88 | esri-geometry-api 89 | 2.2.4 90 | 91 | 92 | 93 | 94 | org.locationtech.jts 95 | jts-core 96 | 1.18.1 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/main/assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 8 | 10 | bin 11 | 12 | tar.gz 13 | 14 | 15 | false 16 | 17 | 18 | 19 | 20 | ${project.basedir} 21 | / 22 | 23 | README* 24 | LICENSE* 25 | 26 | 27 | 28 | 29 | src/main/resources 30 | 31 | 32 | 33 | 34 | ${project.build.directory} 35 | 36 | 37 | ${artifactId}-${version}.jar 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | com.vividsolutions:jts 47 | com.esri.geometry:esri-geometry-api 48 | 49 | false 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Area.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.Geometry; 16 | import com.esri.core.geometry.ogc.OGCGeometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the area of a geometry as calculated by 21 | * {@link Geometry#calculateArea2D()} 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class Area extends EvalFunc { 26 | 27 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 28 | 29 | @Override 30 | public Double exec(Tuple input) throws IOException { 31 | OGCGeometry geom = null; 32 | try { 33 | Object v = input.get(0); 34 | geom = geometryParser.parseGeom(v); 35 | return geom.getEsriGeometry().calculateArea2D(); 36 | } catch (ExecException ee) { 37 | throw new GeoException(geom, ee); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/AsHex.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.Tuple; 13 | 14 | import com.esri.core.geometry.ogc.OGCGeometry; 15 | 16 | /** 17 | * Returns the Well-Known Binary (WKB) representation of a geometry object 18 | * represented as hex string. 19 | * @author Ahmed Eldawy 20 | * 21 | */ 22 | public class AsHex extends EvalFunc { 23 | 24 | private ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 25 | 26 | @Override 27 | public String exec(Tuple t) throws IOException { 28 | if (t.size() != 1) 29 | throw new GeoException("AsHex expects one geometry argument"); 30 | OGCGeometry geom = geometryParser.parseGeom(t.get(0)); 31 | return ESRIGeometryParser.bytesToHex(geom.asBinary().array()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/AsText.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.Tuple; 13 | 14 | import com.esri.core.geometry.ogc.OGCGeometry; 15 | 16 | /** 17 | * Returns the Well-Known Text (WKT) representation of a geometry object. 18 | * @author Ahmed Eldawy 19 | * 20 | */ 21 | public class AsText extends EvalFunc { 22 | 23 | private ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 24 | 25 | @Override 26 | public String exec(Tuple t) throws IOException { 27 | if (t.size() != 1) 28 | throw new GeoException("ST_AsText expects one geometry argument"); 29 | OGCGeometry geom = geometryParser.parseGeom(t.get(0)); 30 | try { 31 | return geom.asText(); 32 | } catch (Exception e) { 33 | throw new GeoException(e); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Boundary.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import org.locationtech.jts.geom.Geometry; 17 | import org.locationtech.jts.io.WKBWriter; 18 | 19 | 20 | /** 21 | * A UDF that returns the outer boundary of a shape 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class Boundary extends EvalFunc { 26 | 27 | private final WKBWriter WKB_WRITER = new WKBWriter(); 28 | private final JTSGeometryParser GEOMETRY_PARSER = new JTSGeometryParser(); 29 | 30 | @Override 31 | public DataByteArray exec(Tuple input) throws IOException { 32 | Geometry geom = null; 33 | try { 34 | Object v = input.get(0); 35 | geom = GEOMETRY_PARSER.parseGeom(v); 36 | Geometry boundary = geom.getBoundary(); 37 | return new DataByteArray(WKB_WRITER.write(boundary)); 38 | } catch (ExecException e) { 39 | throw new GeoException(geom, e); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Break.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.Vector; 11 | 12 | import org.apache.pig.EvalFunc; 13 | import org.apache.pig.data.BagFactory; 14 | import org.apache.pig.data.DataBag; 15 | import org.apache.pig.data.DataType; 16 | import org.apache.pig.data.Tuple; 17 | import org.apache.pig.data.TupleFactory; 18 | import org.apache.pig.impl.logicalLayer.schema.Schema; 19 | import org.apache.pig.impl.logicalLayer.schema.Schema.FieldSchema; 20 | 21 | import org.locationtech.jts.geom.Coordinate; 22 | import org.locationtech.jts.geom.Geometry; 23 | import org.locationtech.jts.geom.GeometryCollection; 24 | import org.locationtech.jts.geom.LineString; 25 | import org.locationtech.jts.geom.Point; 26 | import org.locationtech.jts.geom.Polygon; 27 | 28 | /** 29 | * Breaks down a linestring or a polygon into straight line segments. 30 | * The generated segments are returned as a bag of tuples with the common 31 | * schema (Segment_ID, x1, y1, x2, y2) 32 | * 33 | * @author Ahmed Eldawy 34 | */ 35 | public class Break extends EvalFunc{ 36 | 37 | private JTSGeometryParser geometryParser = new JTSGeometryParser(); 38 | 39 | @Override 40 | public DataBag exec(Tuple b) throws IOException { 41 | if (b.size() != 1) 42 | throw new GeoException("Invalid number of arguments. Expected 1 but found "+b.size()); 43 | Geometry geom = geometryParser.parseGeom(b.get(0)); 44 | Vector segments = new Vector(); 45 | breakGeom(geom, segments); 46 | DataBag segmentsBag = BagFactory.getInstance().newDefaultBag(); 47 | for (int i = 0; i < segments.size(); i++) { 48 | Tuple segmentTuple = TupleFactory.getInstance().newTuple(5); 49 | segmentTuple.set(0, i); 50 | segmentTuple.set(1, segments.get(i)[0].x); 51 | segmentTuple.set(2, segments.get(i)[0].y); 52 | segmentTuple.set(3, segments.get(i)[1].x); 53 | segmentTuple.set(4, segments.get(i)[1].y); 54 | segmentsBag.add(segmentTuple); 55 | } 56 | return segmentsBag; 57 | } 58 | 59 | private void breakGeom(Geometry geom, Vector segments) { 60 | if (geom == null) 61 | return; 62 | if (geom instanceof LineString) { 63 | LineString linestring = (LineString) geom; 64 | Coordinate[] coordinates = linestring.getCoordinates(); 65 | for (int i = 1; i < coordinates.length; i++) { 66 | Coordinate[] segment = new Coordinate[2]; 67 | segment[0] = new Coordinate(coordinates[i-1]); 68 | segment[1] = new Coordinate(coordinates[i]); 69 | segments.add(segment); 70 | } 71 | } else if (geom instanceof Polygon) { 72 | Polygon polygon = (Polygon) geom; 73 | breakGeom(polygon.getExteriorRing(), segments); 74 | for (int n = 0; n < polygon.getNumInteriorRing(); n++) { 75 | breakGeom(polygon.getInteriorRingN(n), segments); 76 | } 77 | } else if (geom instanceof GeometryCollection) { 78 | GeometryCollection geomCollection = (GeometryCollection) geom; 79 | for (int n = 0; n < geomCollection.getNumGeometries(); n++) { 80 | breakGeom(geomCollection.getGeometryN(n), segments); 81 | } 82 | } else if (geom instanceof Point) { 83 | // Skip 84 | } else { 85 | throw new RuntimeException("Cannot break geometry of type "+geom.getClass()); 86 | } 87 | } 88 | 89 | public Schema outputSchema(Schema input) { 90 | try { 91 | Schema segmentSchema = new Schema(); 92 | segmentSchema.add(new Schema.FieldSchema("position", DataType.INTEGER)); 93 | segmentSchema.add(new Schema.FieldSchema("x1", DataType.DOUBLE)); 94 | segmentSchema.add(new Schema.FieldSchema("y1", DataType.DOUBLE)); 95 | segmentSchema.add(new Schema.FieldSchema("x2", DataType.DOUBLE)); 96 | segmentSchema.add(new Schema.FieldSchema("y2", DataType.DOUBLE)); 97 | 98 | FieldSchema breakSchema = new Schema.FieldSchema("segments", segmentSchema); 99 | breakSchema.type = DataType.BAG; 100 | 101 | return new Schema(breakSchema); 102 | } catch (Exception e) { 103 | return null; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Buffer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import com.esri.core.geometry.ogc.OGCGeometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the minimal bounding rectangle (MBR) of a shape. 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class Buffer extends EvalFunc { 25 | 26 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 27 | 28 | @Override 29 | public DataByteArray exec(Tuple input) throws IOException { 30 | OGCGeometry geom = null; 31 | try { 32 | Object v = input.get(0); 33 | geom = geometryParser.parseGeom(v); 34 | double dist; 35 | Object distance = input.get(1); 36 | if (distance instanceof Double) 37 | dist = (Double) distance; 38 | else if (distance instanceof Float) 39 | dist = (Float) distance; 40 | else if (distance instanceof Integer) 41 | dist = (Integer) distance; 42 | else if (distance instanceof Long) 43 | dist = (Long) distance; 44 | else if (distance instanceof String) 45 | dist = Double.parseDouble((String) distance); 46 | else if (distance instanceof DataByteArray) 47 | dist = Double.parseDouble(new String(((DataByteArray) distance).get())); 48 | else 49 | throw new GeoException("Invalid second argument in call to Buffer. Expecting Double, Integer or Long"); 50 | return new DataByteArray(geom.buffer(dist).asBinary().array()); 51 | } catch (ExecException ee) { 52 | throw new GeoException(geom, ee); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Connect.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.Iterator; 11 | import java.util.Vector; 12 | 13 | import org.apache.pig.EvalFunc; 14 | import org.apache.pig.backend.executionengine.ExecException; 15 | import org.apache.pig.data.DataBag; 16 | import org.apache.pig.data.DataByteArray; 17 | import org.apache.pig.data.Tuple; 18 | 19 | import com.esri.core.geometry.Line; 20 | import com.esri.core.geometry.MultiPath; 21 | import com.esri.core.geometry.Point; 22 | import com.esri.core.geometry.Polygon; 23 | import com.esri.core.geometry.Polyline; 24 | import com.esri.core.geometry.Segment; 25 | import com.esri.core.geometry.SpatialReference; 26 | import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; 27 | import com.esri.core.geometry.ogc.OGCGeometry; 28 | import com.esri.core.geometry.ogc.OGCGeometryCollection; 29 | import com.esri.core.geometry.ogc.OGCLineString; 30 | import com.esri.core.geometry.ogc.OGCPolygon; 31 | 32 | /** 33 | * Connects together a set of Linestrings to form a longer Linestring or 34 | * a polygon. 35 | * @author Ahmed Eldawy 36 | */ 37 | public class Connect extends EvalFunc{ 38 | 39 | private ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 40 | 41 | @Override 42 | public DataByteArray exec(Tuple b) throws IOException { 43 | try { 44 | // Read information from input 45 | Iterator firstPointIdIter = ((DataBag) b.get(0)).iterator(); 46 | Iterator lastPointIdIter = ((DataBag) b.get(1)).iterator(); 47 | Iterator shapesIter = ((DataBag) b.get(2)).iterator(); 48 | 49 | // Shapes that are created after connected line segments 50 | Vector createdShapes = new Vector(); 51 | Vector linestrings = new Vector(); 52 | Vector firstPointId = new Vector(); 53 | Vector lastPointId = new Vector(); 54 | 55 | while (firstPointIdIter.hasNext() && lastPointIdIter.hasNext() && 56 | shapesIter.hasNext()) { 57 | OGCGeometry geom = geometryParser.parseGeom(shapesIter.next().get(0)); 58 | long first_point_id = (Long) firstPointIdIter.next().get(0); 59 | long last_point_id = (Long)lastPointIdIter.next().get(0); 60 | if (geom.isEmpty()) { 61 | // Skip empty geometries 62 | } else if (geom instanceof OGCPolygon) { 63 | // Copy to output directly. Polygons cannot be connected to other shapes. 64 | createdShapes.add(geom); 65 | } else if (geom instanceof OGCLineString) { 66 | linestrings.add((OGCLineString) geom); 67 | firstPointId.add(first_point_id); 68 | lastPointId.add(last_point_id); 69 | } else { 70 | throw new GeoException("Cannot connect shapes of type "+geom.getClass()); 71 | } 72 | 73 | } 74 | 75 | if (firstPointIdIter.hasNext() || lastPointIdIter.hasNext() || 76 | shapesIter.hasNext()) { 77 | throw new ExecException("All parameters should be of the same size (" 78 | + firstPointId.size() + "," + lastPointId.size() + "," 79 | + linestrings.size() + ")"); 80 | } 81 | 82 | // Stores an ordered list of line segments in current connected block 83 | Vector connected_lines = new Vector(); 84 | // Total number of points in all visited linestrings 85 | int sumPoints = 0; 86 | // Which linestrings to reverse upon connection 87 | Vector reverse = new Vector(); 88 | long first_point_id = -1; 89 | long last_point_id = -1; 90 | 91 | // Reorder linestrings to form a contiguous list of connected linestrings 92 | while (!linestrings.isEmpty()) { 93 | // Loop invariant: 94 | // At the beginning of each iteration, the lines in connected_lines are connected. 95 | // In each iteration, we move one linestring from linestrings to connected_lines 96 | // while keeping them connected 97 | int size_before = connected_lines.size(); 98 | for (int i = 0; i < linestrings.size();) { 99 | if (connected_lines.isEmpty()) { 100 | // First linestring 101 | first_point_id = firstPointId.remove(i); 102 | last_point_id = lastPointId.remove(i); 103 | reverse.add(false); 104 | sumPoints += linestrings.get(i).numPoints(); 105 | connected_lines.add(linestrings.remove(i)); 106 | } else if (lastPointId.get(i) == first_point_id) { 107 | // This linestring goes to the beginning of the list as-is 108 | lastPointId.remove(i); 109 | first_point_id = firstPointId.remove(i); 110 | sumPoints += linestrings.get(i).numPoints(); 111 | connected_lines.add(0, linestrings.remove(i)); 112 | reverse.add(0, false); 113 | } else if (firstPointId.get(i) == first_point_id) { 114 | // Should go to the beginning after being reversed 115 | firstPointId.remove(i); 116 | first_point_id = lastPointId.remove(i); 117 | sumPoints += linestrings.get(i).numPoints(); 118 | connected_lines.add(0, linestrings.remove(i)); 119 | reverse.add(0, true); 120 | } else if (firstPointId.get(i) == last_point_id) { 121 | // This linestring goes to the end of the list as-is 122 | firstPointId.remove(i); 123 | last_point_id = lastPointId.remove(i); 124 | sumPoints += linestrings.get(i).numPoints(); 125 | connected_lines.add(linestrings.remove(i)); 126 | reverse.add(false); 127 | } else if (lastPointId.get(i) == last_point_id) { 128 | // Should go to the end after being reversed 129 | lastPointId.remove(i); 130 | last_point_id = firstPointId.remove(i); 131 | sumPoints += linestrings.get(i).numPoints(); 132 | connected_lines.add(linestrings.remove(i)); 133 | reverse.add(true); 134 | } else { 135 | i++; 136 | } 137 | } 138 | 139 | if (connected_lines.size() == size_before || linestrings.isEmpty()) { 140 | // Cannot connect any more lines to the current block. Emit as a shape 141 | boolean isPolygon = first_point_id == last_point_id; 142 | Point[] points = new Point[sumPoints - connected_lines.size() + (isPolygon? 0 : 1)]; 143 | int n = 0; 144 | for (int i = 0; i < connected_lines.size(); i++) { 145 | OGCLineString linestring = connected_lines.get(i); 146 | boolean isReverse = reverse.get(i); 147 | int last_i = (isPolygon || i < connected_lines.size() - 1)? 148 | linestring.numPoints() - 1 : linestring.numPoints(); 149 | for (int i_point = 0; i_point < last_i; i_point++) { 150 | points[n++] = (Point) linestring.pointN( 151 | isReverse? linestring.numPoints() - 1 - i_point : i_point 152 | ).getEsriGeometry(); 153 | } 154 | } 155 | 156 | MultiPath multi_path = isPolygon ? new Polygon() : new Polyline(); 157 | for (int i = 1; i 1) { 179 | OGCGeometryCollection collection = new OGCConcreteGeometryCollection(createdShapes, createdShapes.get(0).getEsriSpatialReference()); 180 | return new DataByteArray(collection.asBinary().array()); 181 | } else { 182 | throw new GeoException("No shapes to connect"); 183 | } 184 | } catch (Exception e) { 185 | throw new GeoException(e); 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Contains.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | 18 | /** 19 | * A UDF that tests whether a geometry contains another geometry or not 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class Contains extends FilterFunc { 24 | 25 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 26 | 27 | @Override 28 | public Boolean exec(Tuple input) throws IOException { 29 | OGCGeometry geom1 = null, geom2 = null; 30 | try { 31 | geom1 = geometryParser.parseGeom(input.get(0)); 32 | geom2 = geometryParser.parseGeom(input.get(1)); 33 | return geom1.contains(geom2); 34 | } catch (ExecException ee) { 35 | throw new GeoException(geom1, geom2, ee); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/ConvexHull.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | 12 | import org.apache.pig.Algebraic; 13 | import org.apache.pig.EvalFunc; 14 | import org.apache.pig.backend.executionengine.ExecException; 15 | import org.apache.pig.data.DataBag; 16 | import org.apache.pig.data.DataByteArray; 17 | import org.apache.pig.data.Tuple; 18 | import org.apache.pig.data.TupleFactory; 19 | 20 | import com.esri.core.geometry.GeometryException; 21 | import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; 22 | import com.esri.core.geometry.ogc.OGCGeometry; 23 | import com.esri.core.geometry.ogc.OGCGeometryCollection; 24 | 25 | /** 26 | * Finds the convex null of a set of shapes. This is an algebraic function which 27 | * works more efficiently by computing convex hull of a subset of the shapes and 28 | * finally computing the overall convex hull. 29 | * 30 | * @author Ahmed Eldawy 31 | * 32 | */ 33 | public class ConvexHull extends EvalFunc implements Algebraic { 34 | 35 | private static final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 36 | 37 | @Override 38 | public DataByteArray exec(Tuple input) throws IOException { 39 | OGCGeometry geom = null; 40 | try { 41 | if (input.get(0) instanceof DataBag) { 42 | return new DataByteArray(convexHull(input).asBinary().array()); 43 | } 44 | geom = geometryParser.parseGeom(input.get(0)); 45 | try { 46 | return new DataByteArray(geom.convexHull().asBinary().array()); 47 | } catch (ArrayIndexOutOfBoundsException e) { 48 | e.printStackTrace(); 49 | throw new RuntimeException(geom.asText(), e); 50 | } 51 | } catch (GeometryException e) { 52 | throw new GeoException(geom, e); 53 | } catch (ArrayIndexOutOfBoundsException e) { 54 | throw new GeoException(geom, e); 55 | } 56 | } 57 | 58 | @Override 59 | public String getInitial() { return Initial.class.getName();} 60 | 61 | @Override 62 | public String getIntermed() { return Intermed.class.getName();} 63 | 64 | @Override 65 | public String getFinal() { return Final.class.getName(); } 66 | 67 | static public class Initial extends EvalFunc { 68 | @Override 69 | public Tuple exec(Tuple input) throws IOException { 70 | // Retrieve the first element (tuple) in the given bag 71 | return ((DataBag)input.get(0)).iterator().next(); 72 | } 73 | } 74 | 75 | static public class Intermed extends EvalFunc { 76 | @Override 77 | public Tuple exec(Tuple input) throws IOException { 78 | return TupleFactory.getInstance().newTuple( 79 | new DataByteArray(convexHull(input).asBinary().array())); 80 | } 81 | } 82 | 83 | static public class Final extends EvalFunc { 84 | @Override 85 | public DataByteArray exec(Tuple input) throws IOException { 86 | return new DataByteArray(convexHull(input).asBinary().array()); 87 | } 88 | } 89 | 90 | static protected OGCGeometry convexHull(Tuple input) throws ExecException { 91 | DataBag values = (DataBag)input.get(0); 92 | if (values.size() == 0) 93 | return null; 94 | ArrayList all_geoms = 95 | new ArrayList(); 96 | for (Tuple one_geom : values) { 97 | OGCGeometry parsedGeom = geometryParser.parseGeom(one_geom.get(0)); 98 | all_geoms.add(parsedGeom); 99 | } 100 | 101 | // Do a convex null of all_geometries 102 | OGCGeometryCollection geom_collection = new OGCConcreteGeometryCollection( 103 | all_geoms, all_geoms.get(0).getEsriSpatialReference()); 104 | return geom_collection.convexHull(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Crosses.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.Geometry; 16 | import com.esri.core.geometry.ogc.OGCGeometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the area of a geometry as calculated by 21 | * {@link OGCGeometry#crosses(OGCGeometry)} ()} 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class Crosses extends FilterFunc { 26 | 27 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 28 | 29 | @Override 30 | public Boolean exec(Tuple input) throws IOException { 31 | OGCGeometry geom1 = null, geom2 = null; 32 | try { 33 | geom1 = geometryParser.parseGeom(input.get(0)); 34 | geom2 = geometryParser.parseGeom(input.get(1)); 35 | return geom1.crosses(geom2); 36 | } catch (ExecException ee) { 37 | throw new GeoException(geom1, geom2, ee); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Decompose.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.Stack; 11 | import java.util.Vector; 12 | 13 | import org.apache.pig.EvalFunc; 14 | import org.apache.pig.backend.executionengine.ExecException; 15 | import org.apache.pig.data.BagFactory; 16 | import org.apache.pig.data.DataBag; 17 | import org.apache.pig.data.DataByteArray; 18 | import org.apache.pig.data.DataType; 19 | import org.apache.pig.data.Tuple; 20 | import org.apache.pig.data.TupleFactory; 21 | import org.apache.pig.impl.logicalLayer.schema.Schema; 22 | import org.apache.pig.impl.logicalLayer.schema.Schema.FieldSchema; 23 | 24 | import org.locationtech.jts.geom.Coordinate; 25 | import org.locationtech.jts.geom.Geometry; 26 | import org.locationtech.jts.geom.GeometryCollection; 27 | import org.locationtech.jts.geom.GeometryFactory; 28 | import org.locationtech.jts.geom.Polygon; 29 | import org.locationtech.jts.io.WKBWriter; 30 | 31 | /** 32 | * Decomposes a polygon into smaller polygons using a Quad-tree-based 33 | * decomposition 34 | * 35 | * @author Ahmed Eldawy 36 | */ 37 | public class Decompose extends EvalFunc { 38 | 39 | /**Default size threshold*/ 40 | public static final int DefaultThreshold = 100; 41 | 42 | private final WKBWriter wkbWriter = new WKBWriter(); 43 | 44 | private JTSGeometryParser geometryParser = new JTSGeometryParser(); 45 | 46 | /**Geometry factory to create smaller geometries*/ 47 | private GeometryFactory geomFactory = new GeometryFactory(); 48 | 49 | @Override 50 | public DataBag exec(Tuple b) throws IOException { 51 | Geometry geom = geometryParser.parseGeom(b.get(0)); 52 | int threshold = b.size() == 1? DefaultThreshold : (Integer)b.get(1); 53 | if (threshold < 4) 54 | throw new GeoException("Size threshold must be at least 4"); 55 | DataBag output = BagFactory.getInstance().newDefaultBag(); 56 | Stack toDecompose = new Stack(); 57 | toDecompose.push(geom); 58 | while (!toDecompose.isEmpty()) { 59 | geom = toDecompose.pop(); 60 | if (NumPoints.getGeometrySize(geom) <= threshold) { 61 | // Simple enough. Add to the output 62 | DataByteArray data = new DataByteArray(wkbWriter.write(geom)); 63 | Tuple tuple = TupleFactory.getInstance().newTuple(1); 64 | tuple.set(0, data); 65 | output.add(tuple); 66 | } else { 67 | // Large geometry. Decompose into four 68 | Geometry[] parts = decompose(geom); 69 | for (Geometry part : parts) 70 | if (!part.isEmpty()) 71 | toDecompose.push(part); 72 | } 73 | } 74 | return output; 75 | } 76 | 77 | /** 78 | * Decomposes a geometry into smaller one. 79 | * @param geom 80 | * @return 81 | */ 82 | public Geometry[] decompose(Geometry geom) { 83 | if (geom instanceof GeometryCollection) { 84 | GeometryCollection coll = (GeometryCollection) geom; 85 | Vector output = new Vector(); 86 | for (int i = 0; i < coll.getNumGeometries(); i++) { 87 | geom = coll.getGeometryN(i); 88 | Geometry[] partialAnswer = decompose(geom); 89 | for (Geometry g : partialAnswer) 90 | if (!g.isEmpty()) 91 | output.add(g); 92 | } 93 | return output.toArray(new Geometry[output.size()]); 94 | } 95 | 96 | Geometry[] parts = new Geometry[4]; 97 | Geometry envelope = geom.getEnvelope(); 98 | Coordinate[] coords = envelope.getCoordinates(); 99 | Coordinate[][] corners = new Coordinate[3][3]; 100 | double x1 = Math.min(coords[0].x, coords[2].x); 101 | double x2 = Math.max(coords[0].x, coords[2].x); 102 | double y1 = Math.min(coords[0].y, coords[2].y); 103 | double y2 = Math.max(coords[0].y, coords[2].y); 104 | double cx = (x1 + x2) / 2; 105 | double cy = (y1 + y2) / 2; 106 | corners[0][0] = new Coordinate(x1, y1); 107 | corners[0][1] = new Coordinate(x1, cy); 108 | corners[0][2] = new Coordinate(x1, y2); 109 | corners[1][0] = new Coordinate(cx, y1); 110 | corners[1][1] = new Coordinate(cx, cy); 111 | corners[1][2] = new Coordinate(cx, y2); 112 | corners[2][0] = new Coordinate(x2, y1); 113 | corners[2][1] = new Coordinate(x2, cy); 114 | corners[2][2] = new Coordinate(x2, y2); 115 | 116 | Polygon q0 = geomFactory 117 | .createPolygon( 118 | geomFactory.createLinearRing(new Coordinate[] { corners[0][0], 119 | corners[1][0], corners[1][1], corners[0][1], corners[0][0] }), 120 | null); 121 | parts[0] = q0.intersection(geom); 122 | Polygon q1 = geomFactory 123 | .createPolygon( 124 | geomFactory.createLinearRing(new Coordinate[] { corners[0][1], 125 | corners[1][1], corners[1][2], corners[0][2], corners[0][1]}), 126 | null); 127 | parts[1] = q1.intersection(geom); 128 | Polygon q2 = geomFactory 129 | .createPolygon( 130 | geomFactory.createLinearRing(new Coordinate[] { corners[1][0], 131 | corners[2][0], corners[2][1], corners[1][1], corners[1][0] }), 132 | null); 133 | parts[2] = q2.intersection(geom); 134 | Polygon q3 = geomFactory 135 | .createPolygon( 136 | geomFactory.createLinearRing(new Coordinate[] { corners[1][1], 137 | corners[2][1], corners[2][2], corners[1][2], corners[1][1] }), 138 | null); 139 | parts[3] = q3.intersection(geom); 140 | 141 | return parts; 142 | } 143 | 144 | public Schema outputSchema(Schema input) { 145 | try { 146 | Schema partSchema = new Schema(); 147 | partSchema.add(new Schema.FieldSchema("geom", DataType.BYTEARRAY)); 148 | 149 | FieldSchema outSchema = new Schema.FieldSchema("geometries", partSchema); 150 | outSchema.type = DataType.BAG; 151 | 152 | return new Schema(outSchema); 153 | } catch (Exception e) { 154 | return null; 155 | } 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Difference.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import com.esri.core.geometry.ogc.OGCGeometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the spatial difference of two shapes as calculated by 21 | * {@link OGCGeometry#difference(OGCGeometry)} 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class Difference extends EvalFunc { 26 | 27 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 28 | 29 | @Override 30 | public DataByteArray exec(Tuple input) throws IOException { 31 | OGCGeometry geom1 = null, geom2 = null; 32 | try { 33 | geom1 = geometryParser.parseGeom(input.get(0)); 34 | geom2 = geometryParser.parseGeom(input.get(1)); 35 | return new DataByteArray(geom1.difference(geom2).asBinary().array()); 36 | } catch (ExecException ee) { 37 | throw new GeoException(geom1, geom2, ee); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/ESRIGeometryParser.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.nio.ByteBuffer; 10 | 11 | import org.apache.pig.backend.executionengine.ExecException; 12 | import org.apache.pig.data.DataByteArray; 13 | 14 | import com.esri.core.geometry.ogc.OGCGeometry; 15 | 16 | /** 17 | * Retrieves a geometry from a pig attribute. It detects the type of the column 18 | * and the data stored in that column and automatically detects its format 19 | * and tries to get the geometry object from it. In particular, here are the 20 | * checks done in order: 21 | * 1- If the object is of type bytearray, it is parsed as a well known binary 22 | * (WKB). If the parsing fails with a parse exception, the binary array 23 | * is converted to a string and the next step is carried out. 24 | * 2- If the object is of type chararrray or step 1 fails, the string is parsed 25 | * as a well known text (WKT). If the parsing fails and the string contains 26 | * only hex characters (0-9 and A-Z), it is converted to binary and parsed 27 | * as a well known binary (WKB). 28 | * @author Ahmed Eldawy 29 | * 30 | */ 31 | public class ESRIGeometryParser { 32 | 33 | public OGCGeometry parseGeom(Object o) throws ExecException { 34 | if (o == null) 35 | return null; 36 | if (o instanceof DataByteArray) { 37 | try { 38 | // Parse data as well known binary (WKB) 39 | byte[] bytes = ((DataByteArray) o).get(); 40 | return OGCGeometry.fromBinary(ByteBuffer.wrap(bytes)); 41 | } catch (RuntimeException e) { 42 | // Treat it as an encoded string (WKT) 43 | o = new String(((DataByteArray) o).get()); 44 | } 45 | } 46 | if (o instanceof String) { 47 | try { 48 | // Parse string as well known text (WKT) 49 | return OGCGeometry.fromText((String) o); 50 | } catch (IllegalArgumentException e) { 51 | try { 52 | // Error parsing from WKT, try hex string instead 53 | byte[] binary = hexToBytes((String) o); 54 | return OGCGeometry.fromBinary(ByteBuffer.wrap(binary)); 55 | } catch (RuntimeException e1) { 56 | // Cannot parse text. 57 | throw new ExecException("Cannot parse '"+o+"'", e); 58 | } 59 | } 60 | } 61 | throw new ExecException("Cannot parse unknown type '"+o+"'"); 62 | } 63 | 64 | public static double parseDouble(Object o) { 65 | if (o instanceof Integer) 66 | return (Integer)o; 67 | if (o instanceof Double) 68 | return (Double)o; 69 | if (o instanceof DataByteArray) 70 | return Double.parseDouble(new String(((DataByteArray) o).get())); 71 | throw new RuntimeException("Cannot parse "+o+" into double"); 72 | } 73 | 74 | private static final byte[] HexLookupTable = { 75 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 76 | 'F' 77 | }; 78 | 79 | /** 80 | * Convert binary array to a hex string. 81 | * @param binary 82 | * @return 83 | */ 84 | public static String bytesToHex(byte[] binary) { 85 | // Each byte is converted to two hex values 86 | byte[] hex = new byte[binary.length * 2]; 87 | for (int i = 0; i < binary.length; i++) { 88 | hex[2*i] = HexLookupTable[(binary[i] & 0xFF) >>> 4]; 89 | hex[2*i+1] = HexLookupTable[binary[i] & 0xF]; 90 | } 91 | return new String(hex); 92 | } 93 | 94 | /** 95 | * Convert a string containing a hex string to a byte array of binary. 96 | * For example, the string "AABB" is converted to the byte array {0xAA, 0XBB} 97 | * @param hex 98 | * @return 99 | */ 100 | public static byte[] hexToBytes(String hex) { 101 | byte[] bytes = new byte[(hex.length() + 1) / 2]; 102 | for (int i = 0; i < hex.length(); i++) { 103 | byte x = (byte) hex.charAt(i); 104 | if (x >= '0' && x <= '9') 105 | x -= '0'; 106 | else if (x >= 'a' && x <= 'f') 107 | x = (byte) ((x - 'a') + 0xa); 108 | else if (x >= 'A' && x <= 'F') 109 | x = (byte) ((x - 'A') + 0xA); 110 | else 111 | throw new RuntimeException("Invalid hex char "+x); 112 | if (i % 2 == 0) 113 | x <<= 4; 114 | bytes[i / 2] |= x; 115 | } 116 | return bytes; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/ESRIShapeFromText.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | /** 18 | * @author Carlos Balduz 19 | * 20 | */ 21 | public class ESRIShapeFromText extends EvalFunc { 22 | 23 | @Override 24 | public DataByteArray exec(Tuple input) throws IOException { 25 | 26 | if (input.size() != 1) 27 | throw new IOException("ESRIShapeFromText takes one bytearray argument"); 28 | 29 | String s = input.get(0).toString(); 30 | return new DataByteArray(OGCGeometryToESRIShapeParser.ogcGeomToEsriShape(s)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/ESRIShapeFromWKB.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | /** 16 | * @author Carlos Balduz 17 | * 18 | */ 19 | public class ESRIShapeFromWKB extends EvalFunc { 20 | 21 | @Override 22 | public DataByteArray exec(Tuple input) throws IOException { 23 | 24 | if (input.size() != 1) 25 | throw new IOException("ESRIShapeFromWKB takes one bytearray argument"); 26 | 27 | Object o = input.get(0); 28 | return new DataByteArray(OGCGeometryToESRIShapeParser.ogcGeomToEsriShape(o)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Envelope.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import org.locationtech.jts.geom.Geometry; 17 | import org.locationtech.jts.io.WKBWriter; 18 | 19 | 20 | /** 21 | * A UDF that returns the minimal bounding rectangle (MBR) of a shape. 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class Envelope extends EvalFunc { 26 | 27 | private final WKBWriter WKB_WRITER = new WKBWriter(); 28 | private final JTSGeometryParser GEOMETRY_PARSER = new JTSGeometryParser(); 29 | 30 | @Override 31 | public DataByteArray exec(Tuple input) throws IOException { 32 | Geometry geom = null; 33 | try { 34 | Object v = input.get(0); 35 | geom = GEOMETRY_PARSER.parseGeom(v); 36 | Geometry envelope = geom.getEnvelope(); 37 | return new DataByteArray(WKB_WRITER.write(envelope)); 38 | } catch (ExecException ee) { 39 | throw new GeoException(geom, ee); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Extent.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | 12 | import org.apache.pig.Algebraic; 13 | import org.apache.pig.EvalFunc; 14 | import org.apache.pig.backend.executionengine.ExecException; 15 | import org.apache.pig.data.DataBag; 16 | import org.apache.pig.data.DataByteArray; 17 | import org.apache.pig.data.Tuple; 18 | import org.apache.pig.data.TupleFactory; 19 | 20 | import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; 21 | import com.esri.core.geometry.ogc.OGCGeometry; 22 | import com.esri.core.geometry.ogc.OGCGeometryCollection; 23 | 24 | /** 25 | * Finds the minimal bounding rectangle (MBR) of a set of shapes. 26 | * @author Ahmed Eldawy 27 | * 28 | */ 29 | public class Extent extends EvalFunc implements Algebraic { 30 | 31 | private static final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 32 | 33 | @Override 34 | public DataByteArray exec(Tuple input) throws IOException { 35 | return new DataByteArray(extent(input).asBinary().array()); 36 | } 37 | 38 | @Override 39 | public String getInitial() { return Initial.class.getName();} 40 | 41 | @Override 42 | public String getIntermed() { return Intermed.class.getName();} 43 | 44 | @Override 45 | public String getFinal() { return Final.class.getName(); } 46 | 47 | static public class Initial extends EvalFunc { 48 | @Override 49 | public Tuple exec(Tuple input) throws IOException { 50 | // Retrieve the first element (tuple) in the given bag 51 | return ((DataBag)input.get(0)).iterator().next(); 52 | } 53 | } 54 | 55 | static public class Intermed extends EvalFunc { 56 | @Override 57 | public Tuple exec(Tuple input) throws IOException { 58 | return TupleFactory.getInstance().newTuple( 59 | new DataByteArray(extent(input).asBinary().array())); 60 | } 61 | } 62 | 63 | static public class Final extends EvalFunc { 64 | @Override 65 | public DataByteArray exec(Tuple input) throws IOException { 66 | return new DataByteArray(extent(input).asBinary().array()); 67 | } 68 | } 69 | 70 | static protected OGCGeometry extent(Tuple input) throws ExecException { 71 | DataBag values = (DataBag)input.get(0); 72 | if (values.size() == 0) 73 | return null; 74 | ArrayList all_geoms = new ArrayList(); 75 | for (Tuple one_geom : values) { 76 | OGCGeometry parsedGeom = geometryParser.parseGeom(one_geom.get(0)); 77 | all_geoms.add(parsedGeom); 78 | } 79 | 80 | // Do a union of all_geometries in the recommended way (using buffer(0)) 81 | OGCGeometryCollection geom_collection = new OGCConcreteGeometryCollection( 82 | all_geoms, all_geoms.get(0).getEsriSpatialReference()); 83 | return geom_collection.envelope(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/GeoException.java: -------------------------------------------------------------------------------- 1 | package edu.umn.cs.pigeon; 2 | 3 | import com.esri.core.geometry.ogc.OGCGeometry; 4 | import org.locationtech.jts.geom.Geometry; 5 | import org.apache.pig.backend.executionengine.ExecException; 6 | 7 | /** 8 | * An exception to signal an error with any geo function 9 | */ 10 | public class GeoException extends ExecException { 11 | public GeoException(Geometry geom, Exception e) { 12 | super("Error processing the shape " + geom == null? "" : geom.toText(), e); 13 | } 14 | 15 | public GeoException(Geometry geom1, Geometry geom2, Exception e) { 16 | super("Error processing the shape " + (geom1 == null? "" : geom1.toText())+ 17 | " & "+(geom2 == null? "" : geom2.toText()), e); 18 | } 19 | 20 | public GeoException(Geometry geom) { 21 | super("Error processing the shape " + (geom == null? "" : geom.toText())); 22 | } 23 | 24 | public GeoException(OGCGeometry geom, Exception e) { 25 | super("Error processing the shape " + (geom == null? "" : geom.asText()), e); 26 | } 27 | 28 | public GeoException(OGCGeometry geom1, OGCGeometry geom2, Exception e) { 29 | super("Error processing the shape " + (geom1 == null? "" : geom1.asText())+ 30 | " & "+(geom2 == null? "" : geom2.asText()), e); 31 | } 32 | 33 | 34 | public GeoException(OGCGeometry geom) { 35 | super("Error processing the shape " + (geom == null? "" : geom.asText())); 36 | } 37 | 38 | 39 | public GeoException(String message) { 40 | super(message); 41 | } 42 | 43 | public GeoException(Throwable e) { 44 | super(e); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/GeometryFromText.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | /** 18 | * @author Tuan Pham 19 | * 20 | */ 21 | public class GeometryFromText extends EvalFunc { 22 | 23 | @Override 24 | public DataByteArray exec(Tuple input) throws IOException { 25 | 26 | if (input.size() != 1) 27 | throw new GeoException("GeometryFromText takes one bytearray argument"); 28 | 29 | ESRIGeometryParser gp = new ESRIGeometryParser(); 30 | OGCGeometry geom = gp.parseGeom(input.get(0).toString()); 31 | return new DataByteArray(geom.asBinary().array()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/GeometryFromWKB.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | /** 18 | * @author Tuan Pham 19 | * 20 | */ 21 | public class GeometryFromWKB extends EvalFunc { 22 | 23 | @Override 24 | public DataByteArray exec(Tuple input) throws IOException { 25 | 26 | if (input.size() != 1) 27 | throw new GeoException("GeometryFromWKB takes one bytearray argument"); 28 | 29 | ESRIGeometryParser gp = new ESRIGeometryParser(); 30 | OGCGeometry geom = gp.parseGeom(input.get(0)); 31 | return new DataByteArray(geom.asBinary().array()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/GridCell.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Coordinate; 16 | import org.locationtech.jts.geom.Geometry; 17 | import org.locationtech.jts.geom.GeometryFactory; 18 | import org.locationtech.jts.geom.Polygon; 19 | import org.locationtech.jts.io.WKBWriter; 20 | 21 | /** 22 | * Returns the boundaries of one cell 23 | * GridPartition(cellid, gridMBR, gridSize) 24 | * where: 25 | * cellid: the ID of the cell to retrieve. 26 | * gridMBR: the rectangle that defines the boundaries of the grid 27 | * gridSize: number of rows and columns assuming a uniform grid 28 | * @author Ahmed Eldawy 29 | */ 30 | public class GridCell extends EvalFunc { 31 | 32 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 33 | private GeometryFactory geometryFactory = new GeometryFactory(); 34 | private WKBWriter wkbWriter = new WKBWriter(); 35 | 36 | @Override 37 | public DataByteArray exec(Tuple b) throws IOException { 38 | int cellID = (Integer) b.get(0); 39 | Geometry gridMBR = geometryParser.parseGeom(b.get(1)).getEnvelope(); 40 | int gridSize = (Integer)b.get(2); 41 | 42 | Coordinate[] gridCoords = gridMBR.getCoordinates(); 43 | double gridX1 = Math.min(gridCoords[0].x, gridCoords[2].x); 44 | double gridY1 = Math.min(gridCoords[0].y, gridCoords[2].y); 45 | double gridX2 = Math.max(gridCoords[0].x, gridCoords[2].x); 46 | double gridY2 = Math.max(gridCoords[0].y, gridCoords[2].y); 47 | 48 | int column = cellID % gridSize; 49 | int row = cellID / gridSize; 50 | double cellX1 = column * (gridX2 - gridX1) / gridSize + gridX1; 51 | double cellX2 = (column + 1) * (gridX2 - gridX1) / gridSize + gridX1; 52 | double cellY1 = row * (gridY2 - gridY1) / gridSize + gridY1; 53 | double cellY2 = (row + 1) * (gridY2 - gridY1) / gridSize + gridY1; 54 | 55 | Coordinate[] corners = new Coordinate[5]; 56 | corners[0] = new Coordinate(cellX1, cellY1); 57 | corners[1] = new Coordinate(cellX1, cellY2); 58 | corners[2] = new Coordinate(cellX2, cellY2); 59 | corners[3] = new Coordinate(cellX2, cellY1); 60 | corners[4] = corners[0]; 61 | 62 | Polygon box = geometryFactory.createPolygon(geometryFactory.createLinearRing(corners), null); 63 | 64 | return new DataByteArray(wkbWriter.write(box)); 65 | } 66 | 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/GridPartition.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.BagFactory; 13 | import org.apache.pig.data.DataBag; 14 | import org.apache.pig.data.DataType; 15 | import org.apache.pig.data.Tuple; 16 | import org.apache.pig.data.TupleFactory; 17 | import org.apache.pig.impl.logicalLayer.schema.Schema; 18 | import org.apache.pig.impl.logicalLayer.schema.Schema.FieldSchema; 19 | 20 | import org.locationtech.jts.geom.Coordinate; 21 | import org.locationtech.jts.geom.Geometry; 22 | 23 | /** 24 | * Checks which grid cells overlap a geometric shape based on its MBR. 25 | * General format 26 | * GridPartition(geom, gridMBR, gridSize) 27 | * where: 28 | * geom: the geometry to test 29 | * gridMBR: the rectangle that defines the boundaries of the grid 30 | * gridSize: number of rows and columns assuming a uniform grid 31 | * @author Ahmed Eldawy 32 | */ 33 | public class GridPartition extends EvalFunc { 34 | 35 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 36 | 37 | @Override 38 | public DataBag exec(Tuple b) throws IOException { 39 | Geometry geomMBR = geometryParser.parseGeom(b.get(0)).getEnvelope(); 40 | Geometry gridMBR = geometryParser.parseGeom(b.get(1)).getEnvelope(); 41 | int gridSize = (Integer)b.get(2); 42 | 43 | DataBag output = BagFactory.getInstance().newDefaultBag(); 44 | 45 | Coordinate[] gridCoords = gridMBR.getCoordinates(); 46 | double gridX1 = Math.min(gridCoords[0].x, gridCoords[2].x); 47 | double gridY1 = Math.min(gridCoords[0].y, gridCoords[2].y); 48 | double gridX2 = Math.max(gridCoords[0].x, gridCoords[2].x); 49 | double gridY2 = Math.max(gridCoords[0].y, gridCoords[2].y); 50 | 51 | Coordinate[] geomCoords = geomMBR.getCoordinates(); 52 | double geomX1, geomY1, geomX2, geomY2; 53 | if (geomCoords.length == 1) { 54 | // A special case for point 55 | geomX1 = geomX2 = geomCoords[0].x; 56 | geomY1 = geomY2 = geomCoords[0].y; 57 | } else if (geomCoords.length == 2) { 58 | // An orthogonal (horizontal or vertical) line 59 | geomX1 = Math.min(geomCoords[0].x, geomCoords[1].x); 60 | geomY1 = Math.min(geomCoords[0].y, geomCoords[1].y); 61 | geomX2 = Math.max(geomCoords[0].x, geomCoords[1].x); 62 | geomY2 = Math.max(geomCoords[0].y, geomCoords[1].y); 63 | } else { 64 | geomX1 = Math.min(geomCoords[0].x, geomCoords[2].x); 65 | geomY1 = Math.min(geomCoords[0].y, geomCoords[2].y); 66 | geomX2 = Math.max(geomCoords[0].x, geomCoords[2].x); 67 | geomY2 = Math.max(geomCoords[0].y, geomCoords[2].y); 68 | } 69 | 70 | 71 | int col1 = (int) (Math.floor((geomX1 - gridX1) * gridSize / (gridX2 - gridX1))); 72 | int row1 = (int) (Math.floor((geomY1 - gridY1) * gridSize / (gridY2 - gridY1))); 73 | int col2 = (int) (Math.ceil((geomX2 - gridX1) * gridSize / (gridX2 - gridX1))); 74 | int row2 = (int) (Math.ceil((geomY2 - gridY1) * gridSize / (gridY2 - gridY1))); 75 | 76 | for (int col = col1; col < col2; col++) { 77 | for (int row = row1; row < row2; row++) { 78 | int cellID = row * gridSize + col; 79 | Tuple tuple = TupleFactory.getInstance().newTuple(1); 80 | tuple.set(0, cellID); 81 | output.add(tuple); 82 | } 83 | } 84 | return output; 85 | } 86 | 87 | public Schema outputSchema(Schema input) { 88 | try { 89 | Schema partSchema = new Schema(); 90 | partSchema.add(new Schema.FieldSchema("cellID", DataType.INTEGER)); 91 | 92 | FieldSchema outSchema = new Schema.FieldSchema("overlapCells", partSchema); 93 | outSchema.type = DataType.BAG; 94 | 95 | return new Schema(outSchema); 96 | } catch (Exception e) { 97 | return null; 98 | } 99 | } 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Intersection.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import com.esri.core.geometry.ogc.OGCGeometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the spatial intersection of two shapes as calculated by 21 | * {@link OGCGeometry#intersection(OGCGeometry)} 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class Intersection extends EvalFunc { 26 | 27 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 28 | 29 | @Override 30 | public DataByteArray exec(Tuple input) throws IOException { 31 | OGCGeometry geom1 = null, geom2 = null; 32 | try { 33 | geom1 = geometryParser.parseGeom(input.get(0)); 34 | geom2 = geometryParser.parseGeom(input.get(1)); 35 | return new DataByteArray(geom1.intersection(geom2).asBinary().array()); 36 | } catch (ExecException ee) { 37 | throw new GeoException(geom1, geom2, ee); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Intersects.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | 18 | /** 19 | * A UDF that tests whether a geometry intersects another geometry or not 20 | * @author Tuan Pham 21 | * 22 | */ 23 | public class Intersects extends FilterFunc { 24 | 25 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 26 | 27 | @Override 28 | public Boolean exec(Tuple input) throws IOException { 29 | OGCGeometry geom1 = null, geom2 = null; 30 | try { 31 | geom1 = geometryParser.parseGeom(input.get(0)); 32 | geom2 = geometryParser.parseGeom(input.get(1)); 33 | return geom1.intersects(geom2); 34 | } catch (ExecException ee) { 35 | throw new GeoException(geom1, geom2, ee); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/IsEmpty.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | 18 | /** 19 | * A predicate that tells whether a specific geometry is empty or not. 20 | * A wrapper call to {@link OGCGeometry#isEmpty()} 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class IsEmpty extends FilterFunc { 25 | 26 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 27 | 28 | @Override 29 | public Boolean exec(Tuple input) throws IOException { 30 | OGCGeometry geom = null; 31 | try { 32 | Object v = input.get(0); 33 | geom = geometryParser.parseGeom(v); 34 | return geom.isEmpty(); 35 | } catch (ExecException ee) { 36 | throw new GeoException(geom, ee); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/IsValid.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | import org.locationtech.jts.geom.Geometry; 17 | 18 | 19 | /** 20 | * A predicate that tells whether a specific geometry is empty or not. 21 | * A wrapper call to {@link OGCGeometry#isEmpty()} 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class IsValid extends FilterFunc { 26 | 27 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 28 | 29 | @Override 30 | public Boolean exec(Tuple input) throws IOException { 31 | Geometry geom = null; 32 | try { 33 | Object v = input.get(0); 34 | geom = geometryParser.parseGeom(v); 35 | return geom.isValid(); 36 | } catch (ExecException ee) { 37 | throw new GeoException(geom, ee); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/JTSGeometryParser.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import org.locationtech.jts.io.WKBWriter; 10 | import org.apache.pig.backend.executionengine.ExecException; 11 | import org.apache.pig.data.DataByteArray; 12 | 13 | import org.locationtech.jts.geom.Geometry; 14 | import org.locationtech.jts.io.ParseException; 15 | import org.locationtech.jts.io.WKBReader; 16 | import org.locationtech.jts.io.WKTReader; 17 | 18 | /** 19 | * Retrieves a geometry from a pig attribute. It detects the type of the column 20 | * and the data stored in that column and automatically detects its format 21 | * and tries to get the geometry object from it. In particular, here are the 22 | * checks done in order: 23 | * 1- If the object is of type bytearray, it is parsed as a well known binary 24 | * (WKB). If the parsing fails with a parse exception, the binary array 25 | * is converted to a string and the next step is carried out. 26 | * 2- If the object is of type chararrray or step 1 fails, the string is parsed 27 | * as a well known text (WKT). If the parsing fails and the string contains 28 | * only hex characters (0-9 and A-Z), it is converted to binary and parsed 29 | * as a well known binary (WKB). 30 | * @author Ahmed Eldawy 31 | * 32 | */ 33 | public class JTSGeometryParser { 34 | 35 | private final WKTReader wkt_reader = new WKTReader(); 36 | private final WKBReader wkb_reader = new WKBReader(); 37 | private final WKBWriter wkb_writer = new WKBWriter(); 38 | 39 | public Geometry parseGeom(Object o) throws ExecException { 40 | if (o == null) 41 | return null; 42 | if (o instanceof DataByteArray) { 43 | byte[] bytes = ((DataByteArray) o).get(); 44 | try { 45 | // Parse data as well known binary (WKB) 46 | return wkb_reader.read(bytes); 47 | } catch (ParseException e) { 48 | // Convert bytes to text and try text parser 49 | o = new String(bytes); 50 | } 51 | } 52 | if (o instanceof String) { 53 | try { 54 | // Parse string as well known text (WKT) 55 | return wkt_reader.read((String) o); 56 | } catch (ParseException e) { 57 | // Parse string as a hex string of a well known binary (WKB) 58 | String hex = (String) o; 59 | boolean isHex = true; 60 | for (int i = 0; isHex && i < hex.length(); i++) { 61 | char digit = hex.charAt(i); 62 | isHex = (digit >= '0' && digit <= '9') || 63 | (digit >= 'a' && digit <= 'f') || 64 | (digit >= 'A' && digit <= 'F'); 65 | } 66 | if (isHex) { 67 | byte[] binary = WKBReader.hexToBytes(hex); 68 | try { 69 | return wkb_reader.read(binary); 70 | } catch (ParseException e1) { 71 | throw new ExecException("Error parsing '"+o+"'", e); 72 | } 73 | } 74 | } 75 | } 76 | throw new ExecException("Error parsing unknown type '"+o+"'"); 77 | } 78 | 79 | public static double parseDouble(Object o) { 80 | if (o instanceof Integer) 81 | return (Integer)o; 82 | if (o instanceof Double) 83 | return (Double)o; 84 | if (o instanceof DataByteArray) 85 | return Double.parseDouble(new String(((DataByteArray) o).get())); 86 | throw new RuntimeException("Cannot parse "+o+" into double"); 87 | } 88 | 89 | public DataByteArray geomToBytes(Geometry geom) { 90 | return new DataByteArray(wkb_writer.write(geom)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/MakeBox.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Coordinate; 16 | import org.locationtech.jts.geom.GeometryFactory; 17 | import org.locationtech.jts.geom.Polygon; 18 | import org.locationtech.jts.io.WKBWriter; 19 | 20 | /** 21 | * A UDF to create a box (rectangle) from the coordinates of the two corners 22 | * x1, y1, x2, and y2. 23 | * @author Ahmed Eldawy 24 | * 25 | */ 26 | public class MakeBox extends EvalFunc { 27 | 28 | private GeometryFactory geometryFactory = new GeometryFactory(); 29 | private WKBWriter wkbWriter = new WKBWriter(); 30 | 31 | @Override 32 | public DataByteArray exec(Tuple input) throws IOException { 33 | if (input.size() != 4) 34 | throw new GeoException("MakeBox takes four numerical arguments"); 35 | double x1 = ESRIGeometryParser.parseDouble(input.get(0)); 36 | double y1 = ESRIGeometryParser.parseDouble(input.get(1)); 37 | double x2 = ESRIGeometryParser.parseDouble(input.get(2)); 38 | double y2 = ESRIGeometryParser.parseDouble(input.get(3)); 39 | Coordinate[] corners = new Coordinate[5]; 40 | corners[0] = new Coordinate(x1, y1); 41 | corners[1] = new Coordinate(x1, y2); 42 | corners[2] = new Coordinate(x2, y2); 43 | corners[3] = new Coordinate(x2, y1); 44 | corners[4] = corners[0]; 45 | 46 | Polygon box = geometryFactory.createPolygon(geometryFactory.createLinearRing(corners), null); 47 | 48 | return new DataByteArray(wkbWriter.write(box)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/MakeLine.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataBag; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import org.locationtech.jts.geom.Coordinate; 17 | import org.locationtech.jts.geom.Geometry; 18 | import org.locationtech.jts.geom.GeometryFactory; 19 | import org.locationtech.jts.io.WKBWriter; 20 | 21 | /** 22 | * Generates a geometry of type LineString out of a bag of points. 23 | * @author Ahmed Eldawy 24 | */ 25 | public class MakeLine extends EvalFunc{ 26 | 27 | private GeometryFactory geometryFactory = new GeometryFactory(); 28 | private JTSGeometryParser geometryParser = new JTSGeometryParser(); 29 | private WKBWriter wkbWriter = new WKBWriter(); 30 | 31 | @Override 32 | public DataByteArray exec(Tuple b) throws IOException { 33 | DataBag points = (DataBag) b.get(0); 34 | Coordinate[] coordinates = new Coordinate[(int) points.size()]; 35 | int i = 0; 36 | for (Tuple t : points) { 37 | Geometry point = geometryParser.parseGeom(t.get(0)); 38 | coordinates[i++] = point.getCoordinate(); 39 | } 40 | Geometry line = geometryFactory.createLineString(coordinates); 41 | return new DataByteArray(wkbWriter.write(line)); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/MakeLinePolygon.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.Iterator; 11 | 12 | import org.apache.pig.EvalFunc; 13 | import org.apache.pig.data.DataBag; 14 | import org.apache.pig.data.DataByteArray; 15 | import org.apache.pig.data.Tuple; 16 | 17 | import org.locationtech.jts.geom.Coordinate; 18 | import org.locationtech.jts.geom.Geometry; 19 | import org.locationtech.jts.geom.GeometryFactory; 20 | import org.locationtech.jts.io.WKBWriter; 21 | 22 | /** 23 | * Takes a list of point locations and IDs and creates either a linestring or 24 | * polygon based on whether the last point is the same as the first point or not. 25 | * @author Ahmed Eldawy 26 | */ 27 | public class MakeLinePolygon extends EvalFunc{ 28 | 29 | private GeometryFactory geometryFactory = new GeometryFactory(); 30 | private JTSGeometryParser geometryParser = new JTSGeometryParser(); 31 | private WKBWriter wkbWriter = new WKBWriter(); 32 | 33 | @Override 34 | public DataByteArray exec(Tuple b) throws IOException { 35 | DataBag pointIDs = (DataBag) b.get(0); 36 | DataBag pointLocations = (DataBag) b.get(1); 37 | Coordinate[] coordinates = new Coordinate[(int) pointLocations.size()]; 38 | int i = 0; 39 | Iterator iter_id = pointIDs.iterator(); 40 | long first_point_id = -1; 41 | boolean is_polygon = false; 42 | for (Tuple t : pointLocations) { 43 | Object point_id_obj = iter_id.next().get(0); 44 | Geometry point = geometryParser.parseGeom(t.get(0)); 45 | long point_id = point_id_obj instanceof Integer? 46 | (Integer) point_id_obj : 47 | (Long) point_id_obj; 48 | if (i == 0) { 49 | first_point_id = point_id; 50 | coordinates[i++] = point.getCoordinate(); 51 | } else if (i == pointIDs.size() - 1) { 52 | is_polygon = point_id == first_point_id; 53 | if (is_polygon) 54 | coordinates[i++] = coordinates[0]; 55 | else 56 | coordinates[i++] = point.getCoordinate(); 57 | } else { 58 | coordinates[i++] = point.getCoordinate(); 59 | } 60 | } 61 | Geometry shape; 62 | if (coordinates.length == 1 || (coordinates.length == 2 && is_polygon)) { 63 | // A point 64 | shape = geometryFactory.createPoint(coordinates[0]); 65 | } else { 66 | if (is_polygon && coordinates.length <= 3) { 67 | // Cannot create a polygon with two corners, convert to Linestring 68 | Coordinate[] new_coords = new Coordinate[coordinates.length - 1]; 69 | System.arraycopy(coordinates, 0, new_coords, 0, new_coords.length); 70 | coordinates = new_coords; 71 | is_polygon = false; 72 | } 73 | if (is_polygon) { 74 | shape = geometryFactory.createPolygon(geometryFactory.createLinearRing(coordinates), null); 75 | } else { 76 | shape = geometryFactory.createLineString(coordinates); 77 | } 78 | } 79 | return new DataByteArray(wkbWriter.write(shape)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/MakePoint.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataByteArray; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.Point; 16 | import com.esri.core.geometry.SpatialReference; 17 | import com.esri.core.geometry.ogc.OGCPoint; 18 | 19 | /** 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class MakePoint extends EvalFunc { 24 | 25 | @Override 26 | public DataByteArray exec(Tuple input) throws IOException { 27 | if (input.size() != 2) 28 | throw new GeoException("MakePoint takes two numerical arguments"); 29 | double x = ESRIGeometryParser.parseDouble(input.get(0)); 30 | double y = ESRIGeometryParser.parseDouble(input.get(1)); 31 | Point point = new Point(x, y); 32 | OGCPoint ogc_point = new OGCPoint(point, SpatialReference.create(4326)); 33 | return new DataByteArray(ogc_point.asBinary().array()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/MakePolygon.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.data.DataBag; 13 | import org.apache.pig.data.DataByteArray; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import com.esri.core.geometry.Line; 17 | import com.esri.core.geometry.Point; 18 | import com.esri.core.geometry.Polygon; 19 | import com.esri.core.geometry.Segment; 20 | import com.esri.core.geometry.SpatialReference; 21 | import com.esri.core.geometry.ogc.OGCPolygon; 22 | 23 | /** 24 | * Generates a geometry of type Polygon out of a bag of points. The last point 25 | * in the bag should be the same as the first point. 26 | * @author Ahmed Eldawy 27 | */ 28 | public class MakePolygon extends EvalFunc{ 29 | 30 | private ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 31 | 32 | @Override 33 | public DataByteArray exec(Tuple b) throws IOException { 34 | DataBag points = (DataBag) b.get(0); 35 | Point[] coordinates = new Point[(int) points.size()]; 36 | int i = 0; 37 | for (Tuple t : points) { 38 | coordinates[i++] = 39 | (Point) (geometryParser.parseGeom(t.get(0))).getEsriGeometry(); 40 | } 41 | Polygon multi_path = new Polygon(); 42 | for (i = 1; i { 30 | 31 | private JTSGeometryParser geometryParser = new JTSGeometryParser(); 32 | 33 | @Override 34 | public DataBag exec(Tuple b) throws IOException { 35 | DataBag pointIDs = (DataBag) b.get(0); 36 | DataBag pointLocations = (DataBag) b.get(1); 37 | long[] ids = new long[(int) pointIDs.size()]; 38 | Coordinate[] coordinates = new Coordinate[(int) pointLocations.size()]; 39 | int i = 0; 40 | Iterator iter_id = pointIDs.iterator(); 41 | for (Tuple t : pointLocations) { 42 | Object point_id_obj = iter_id.next().get(0); 43 | Geometry point = geometryParser.parseGeom(t.get(0)); 44 | long point_id = point_id_obj instanceof Integer? 45 | (Integer) point_id_obj : 46 | (Long) point_id_obj; 47 | ids[i] = point_id; 48 | coordinates[i++] = point.getCoordinate(); 49 | } 50 | 51 | DataBag segmentsBag = BagFactory.getInstance().newDefaultBag(); 52 | for (int n = 1; n < coordinates.length; n++) { 53 | Tuple segmentTuple = TupleFactory.getInstance().newTuple(7); 54 | segmentTuple.set(0, n - 1); 55 | segmentTuple.set(1, ids[n-1]); 56 | segmentTuple.set(2, coordinates[n-1].x); 57 | segmentTuple.set(3, coordinates[n-1].y); 58 | segmentTuple.set(4, ids[n]); 59 | segmentTuple.set(5, coordinates[n].x); 60 | segmentTuple.set(6, coordinates[n].y); 61 | 62 | segmentsBag.add(segmentTuple); 63 | } 64 | return segmentsBag; 65 | } 66 | 67 | public Schema outputSchema(Schema input) { 68 | try { 69 | Schema segmentSchema = new Schema(); 70 | segmentSchema.add(new Schema.FieldSchema("position", DataType.INTEGER)); 71 | segmentSchema.add(new Schema.FieldSchema("id1", DataType.LONG)); 72 | segmentSchema.add(new Schema.FieldSchema("x1", DataType.DOUBLE)); 73 | segmentSchema.add(new Schema.FieldSchema("y1", DataType.DOUBLE)); 74 | segmentSchema.add(new Schema.FieldSchema("id2", DataType.LONG)); 75 | segmentSchema.add(new Schema.FieldSchema("x2", DataType.DOUBLE)); 76 | segmentSchema.add(new Schema.FieldSchema("y2", DataType.DOUBLE)); 77 | 78 | FieldSchema breakSchema = new Schema.FieldSchema("segments", segmentSchema); 79 | breakSchema.type = DataType.BAG; 80 | 81 | return new Schema(breakSchema); 82 | } catch (Exception e) { 83 | return null; 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/NumPoints.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Geometry; 16 | import org.locationtech.jts.geom.GeometryCollection; 17 | import org.locationtech.jts.geom.LineString; 18 | import org.locationtech.jts.geom.Point; 19 | import org.locationtech.jts.geom.Polygon; 20 | 21 | 22 | /** 23 | * Returns the size of a geometry in terms of number of points. 24 | * For {@link Point} it always returns one. 25 | * For {@link LineString} it returns number of points. 26 | * For {@link Polygon} it returns number of edges. 27 | * @author Ahmed Eldawy 28 | * 29 | */ 30 | public class NumPoints extends EvalFunc { 31 | 32 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 33 | 34 | @Override 35 | public Integer exec(Tuple input) throws IOException { 36 | Geometry geom = null; 37 | try { 38 | Object v = input.get(0); 39 | geom = geometryParser.parseGeom(v); 40 | return getGeometrySize(geom); 41 | } catch (ExecException ee) { 42 | throw new GeoException(geom, ee); 43 | } 44 | } 45 | 46 | protected static int getGeometrySize(Geometry geom) throws ExecException { 47 | if (geom instanceof Point) { 48 | return 1; 49 | } else if (geom instanceof LineString) { 50 | return ((LineString)geom).getNumPoints(); 51 | } else if (geom instanceof Polygon) { 52 | Polygon poly = (Polygon) geom; 53 | int size = 0; 54 | size += getGeometrySize(poly.getExteriorRing()) - 1; 55 | for (int i = 0; i < poly.getNumInteriorRing(); i++) 56 | size += getGeometrySize(poly.getInteriorRingN(i)) - 1; 57 | return size; 58 | } else if (geom instanceof GeometryCollection) { 59 | int size = 0; 60 | GeometryCollection coll = (GeometryCollection) geom; 61 | for (int i = 0; i < coll.getNumGeometries(); i++) 62 | size += getGeometrySize(coll.getGeometryN(i)); 63 | return size; 64 | } else { 65 | throw new GeoException("size() not defined for shapes of type: "+geom.getClass()); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/OGCGeometryToESRIShapeParser.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import org.apache.pig.backend.executionengine.ExecException; 10 | import org.apache.pig.data.DataByteArray; 11 | 12 | import com.esri.core.geometry.ogc.OGCGeometry; 13 | import com.esri.core.geometry.GeometryEngine; 14 | 15 | /** 16 | * Converts an OGCGeometry to an ESRI Shape byte array so it can later be used 17 | * in Hive without the need to perform additional parsings in Hive. 18 | * @author Carlos Balduz 19 | */ 20 | public class OGCGeometryToESRIShapeParser { 21 | private static final int SIZE_WKID = 4; 22 | private static final int SIZE_TYPE = 1; 23 | 24 | public static byte[] ogcGeomToEsriShape(Object o) throws ExecException { 25 | ESRIGeometryParser parser = new ESRIGeometryParser(); 26 | OGCGeometry geom = parser.parseGeom(o); 27 | int wkid = geom.SRID(); 28 | int geomType = getGeometryType(geom); 29 | 30 | byte[] shape = GeometryEngine.geometryToEsriShape(geom.getEsriGeometry()); 31 | byte[] shapeWithData = new byte[shape.length + SIZE_TYPE + SIZE_WKID]; 32 | 33 | System.arraycopy(shape, 0, shapeWithData, SIZE_WKID + SIZE_TYPE, shape.length); 34 | System.arraycopy(intToBytes(wkid), 0, shapeWithData, 0, SIZE_WKID); 35 | shapeWithData[SIZE_WKID] = (byte) geomType; 36 | 37 | return shapeWithData; 38 | } 39 | 40 | /** 41 | * Convert int to byte array. 42 | * @param i 43 | * @return 44 | */ 45 | private static byte[] intToBytes(int value) { 46 | return new byte[] { 47 | (byte) (value >>> 24), 48 | (byte) (value >>> 16), 49 | (byte) (value >>> 8), 50 | (byte) value 51 | }; 52 | } 53 | 54 | /** 55 | * Get the geometry type of a OGCGeometry object. 56 | * @param i 57 | * @return 58 | */ 59 | private static int getGeometryType(OGCGeometry geom) { 60 | String typeName = geom.geometryType(); 61 | int ogcType = 0; 62 | 63 | if (typeName.equals("Point")) 64 | ogcType = 1; 65 | else if (typeName.equals("LineString")) 66 | ogcType = 2; 67 | else if (typeName.equals("Polygon")) 68 | ogcType = 3; 69 | else if (typeName.equals("MultiPoint")) 70 | ogcType = 4; 71 | else if (typeName.equals("MultiLineString")) 72 | ogcType = 5; 73 | else if (typeName.equals("MultiPolygon")) 74 | ogcType = 6; 75 | 76 | return ogcType; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Overlaps.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | 18 | /** 19 | * A UDF that tests whether two geometries overlap or not 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class Overlaps extends FilterFunc { 24 | 25 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 26 | 27 | @Override 28 | public Boolean exec(Tuple input) throws IOException { 29 | OGCGeometry geom1 = null, geom2 = null; 30 | try { 31 | geom1 = geometryParser.parseGeom(input.get(0)); 32 | geom2 = geometryParser.parseGeom(input.get(1)); 33 | return geom1.overlaps(geom2); 34 | } catch (ExecException ee) { 35 | throw new GeoException(geom1, geom2, ee); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/SJPlaneSweep.java: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * Copyright (c) 2015 by Regents of the University of Minnesota. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.opensource.org/licenses/apache2.0.php. 7 | * 8 | *************************************************************************/ 9 | package edu.umn.cs.pigeon; 10 | 11 | import java.io.IOException; 12 | import java.util.Iterator; 13 | import java.util.List; 14 | 15 | import org.apache.hadoop.util.IndexedSortable; 16 | import org.apache.hadoop.util.QuickSort; 17 | import org.apache.pig.EvalFunc; 18 | import org.apache.pig.backend.executionengine.ExecException; 19 | import org.apache.pig.data.BagFactory; 20 | import org.apache.pig.data.DataBag; 21 | import org.apache.pig.data.DataType; 22 | import org.apache.pig.data.Tuple; 23 | import org.apache.pig.data.TupleFactory; 24 | import org.apache.pig.impl.logicalLayer.schema.Schema; 25 | import org.apache.pig.impl.logicalLayer.schema.Schema.FieldSchema; 26 | 27 | import org.locationtech.jts.geom.Coordinate; 28 | import org.locationtech.jts.geom.Geometry; 29 | 30 | /** 31 | * Performs a spatial join using the plane-sweep algorithm. 32 | * General usage 33 | * SJPlaneSweep(dataset1, dataset2, duplicate-avoidance-rectangle, 34 | * column-index1, column-index2) 35 | * dataset1: The left dataset 36 | * dataset2: The right dataset 37 | * duplicate-avoidance-rectangle: The rectangle to use to perform duplicate 38 | * avoidance. If not set, no duplicate avoidance is carried out. 39 | * column-index1: The index (position) of the geometric column in dataset1 40 | * column-index2: The index (position) of the geometric column in dataset2 41 | * @author Ahmed Eldawy 42 | * 43 | */ 44 | public class SJPlaneSweep extends EvalFunc { 45 | 46 | private final JTSGeometryParser geomParser = new JTSGeometryParser(); 47 | private TupleFactory tupleFactory; 48 | 49 | @Override 50 | public DataBag exec(Tuple input) throws IOException { 51 | DataBag lRelation = (DataBag) input.get(0); 52 | DataBag rRelation = (DataBag) input.get(1); 53 | 54 | boolean dupAvoidance = false; 55 | double mbrX1 = 0, mbrY1 = 0, mbrX2 = 0, mbrY2 = 0; 56 | if (input.size() > 2) { 57 | // Implement duplicate avoidance based on the MBR as specified by 58 | // the third argument 59 | Geometry cellMBR = geomParser.parseGeom(input.get(2)); 60 | if (cellMBR != null) { 61 | dupAvoidance = true; 62 | Coordinate[] mbrCoords = cellMBR.getCoordinates(); 63 | mbrX1 = Math.min(mbrCoords[0].x, mbrCoords[2].x); 64 | mbrY1 = Math.min(mbrCoords[0].y, mbrCoords[2].y); 65 | mbrX2 = Math.max(mbrCoords[0].x, mbrCoords[2].x); 66 | mbrY2 = Math.max(mbrCoords[0].y, mbrCoords[2].y); 67 | } 68 | } 69 | 70 | Iterator irGeoms = null; 71 | if (input.size() > 4 && input.get(4) instanceof DataBag) { 72 | // User specified a column of values for right geometries 73 | DataBag rGeoms = (DataBag) input.get(4); 74 | if (rGeoms.size() != rRelation.size()) 75 | throw new ExecException(String.format( 76 | "Mismatched sizes of right records column (%d) and geometry column (%d)", 77 | rRelation.size(), rGeoms.size())); 78 | irGeoms = rGeoms.iterator(); 79 | } 80 | 81 | // TODO ensure that the left bag is the smaller one for efficiency 82 | if (lRelation.size() > Integer.MAX_VALUE) 83 | throw new ExecException("Size of left dataset is too large "+lRelation.size()); 84 | 85 | // Read all of the left dataset in memory 86 | final Tuple[] lTuples = new Tuple[(int) lRelation.size()]; 87 | int leftSize = 0; 88 | tupleFactory = TupleFactory.getInstance(); 89 | for (Tuple t : lRelation) { 90 | lTuples[leftSize++] = tupleFactory.newTupleNoCopy(t.getAll()); 91 | } 92 | 93 | // Extract MBRs of objects for filter-refine approach 94 | final double[] lx1 = new double[(int) lRelation.size()]; 95 | final double[] ly1 = new double[(int) lRelation.size()]; 96 | final double[] lx2 = new double[(int) lRelation.size()]; 97 | final double[] ly2 = new double[(int) lRelation.size()]; 98 | 99 | if (input.size() > 3 && input.get(3) instanceof DataBag) { 100 | // User specified a column of values that contain the geometries 101 | DataBag lGeoms = (DataBag) input.get(3); 102 | if (lRelation.size() != lGeoms.size()) 103 | throw new ExecException(String.format( 104 | "Mismatched sizes of left records column (%d) and geometry column (%d)", 105 | lRelation.size(), lGeoms.size())); 106 | Iterator ilGeoms = lGeoms.iterator(); 107 | for (int i = 0; i < lTuples.length; i++) { 108 | Geometry geom = geomParser.parseGeom(ilGeoms.next().get(0)); 109 | Coordinate[] mbrCoords = geom.getEnvelope().getCoordinates(); 110 | lx1[i] = Math.min(mbrCoords[0].x, mbrCoords[2].x); 111 | ly1[i] = Math.min(mbrCoords[0].y, mbrCoords[2].y); 112 | lx2[i] = Math.max(mbrCoords[0].x, mbrCoords[2].x); 113 | ly2[i] = Math.max(mbrCoords[0].y, mbrCoords[2].y); 114 | } 115 | } else { 116 | int lGeomColumn; 117 | if (input.size() > 3 && input.get(3) instanceof Integer) 118 | lGeomColumn = (Integer) input.get(3); 119 | else if (input.size() > 3 && input.get(3) instanceof Long) 120 | lGeomColumn = (int) ((long)(Long) input.get(3)); 121 | else 122 | lGeomColumn = detectGeomColumn(lTuples[0]); 123 | 124 | for (int i = 0; i < lTuples.length; i++) { 125 | Geometry geom = geomParser.parseGeom(lTuples[i].get(lGeomColumn)); 126 | Coordinate[] mbrCoords = geom.getEnvelope().getCoordinates(); 127 | lx1[i] = Math.min(mbrCoords[0].x, mbrCoords[2].x); 128 | ly1[i] = Math.min(mbrCoords[0].y, mbrCoords[2].y); 129 | lx2[i] = Math.max(mbrCoords[0].x, mbrCoords[2].x); 130 | ly2[i] = Math.max(mbrCoords[0].y, mbrCoords[2].y); 131 | } 132 | } 133 | 134 | // Sort left MBRs by x to prepare for the plane-sweep algorithm 135 | IndexedSortable lSortable = new IndexedSortable() { 136 | @Override 137 | public void swap(int i, int j) { 138 | Tuple tt = lTuples[i]; lTuples[i] = lTuples[j]; lTuples[j] = tt; 139 | double td = lx1[i]; lx1[i] = lx1[j]; lx1[j] = td; 140 | td = ly1[i]; ly1[i] = ly1[j]; ly1[j] = td; 141 | td = lx2[i]; lx2[i] = lx2[j]; lx2[j] = td; 142 | td = ly2[i]; ly2[i] = ly2[j]; ly2[j] = td; 143 | } 144 | 145 | @Override 146 | public int compare(int i, int j) { 147 | if (lx1[i] < lx1[j]) 148 | return -1; 149 | if (lx2[i] > lx2[j]) 150 | return 1; 151 | return 0; 152 | } 153 | }; 154 | QuickSort quickSort = new QuickSort(); 155 | quickSort.sort(lSortable, 0, lTuples.length); 156 | 157 | // Retrieve objects from the right relation in batches and join with left 158 | Iterator ri = rRelation.iterator(); 159 | final int batchSize = 10000; 160 | final Tuple[] rTuples = new Tuple[batchSize]; 161 | final double[] rx1 = new double[batchSize]; 162 | final double[] ry1 = new double[batchSize]; 163 | final double[] rx2 = new double[batchSize]; 164 | final double[] ry2 = new double[batchSize]; 165 | IndexedSortable rSortable = new IndexedSortable() { 166 | @Override 167 | public void swap(int i, int j) { 168 | Tuple tt = rTuples[i]; rTuples[i] = rTuples[j]; rTuples[j] = tt; 169 | double td = rx1[i]; rx1[i] = rx1[j]; rx1[j] = td; 170 | td = ry1[i]; ry1[i] = ry1[j]; ry1[j] = td; 171 | td = rx2[i]; rx2[i] = rx2[j]; rx2[j] = td; 172 | td = ry2[i]; ry2[i] = ry2[j]; ry2[j] = td; 173 | } 174 | 175 | @Override 176 | public int compare(int i, int j) { 177 | if (rx1[i] < rx1[j]) return -1; 178 | if (rx2[i] > rx2[j]) return 1; 179 | return 0; 180 | } 181 | }; 182 | int rSize = 0; 183 | DataBag output = BagFactory.getInstance().newDefaultBag(); 184 | int rGeomColumn = -1; 185 | while (ri.hasNext()) { 186 | rTuples[rSize++] = tupleFactory.newTupleNoCopy(ri.next().getAll()); 187 | if (rSize == batchSize || !ri.hasNext()) { 188 | // Extract MBRs of geometries on the right 189 | if (irGeoms != null) { 190 | for (int i = 0; i < rSize; i++) { 191 | Geometry geom = geomParser.parseGeom(irGeoms.next().get(0)); 192 | Coordinate[] mbrCoords = geom.getEnvelope().getCoordinates(); 193 | rx1[i] = Math.min(mbrCoords[0].x, mbrCoords[2].x); 194 | ry1[i] = Math.min(mbrCoords[0].y, mbrCoords[2].y); 195 | rx2[i] = Math.max(mbrCoords[0].x, mbrCoords[2].x); 196 | ry2[i] = Math.max(mbrCoords[0].y, mbrCoords[2].y); 197 | } 198 | } else { 199 | if (rGeomColumn == -1) 200 | rGeomColumn = detectGeomColumn(rTuples[0]); 201 | for (int i = 0; i < rSize; i++) { 202 | Geometry geom = geomParser.parseGeom(rTuples[i].get(rGeomColumn)); 203 | Coordinate[] mbrCoords = geom.getEnvelope().getCoordinates(); 204 | rx1[i] = Math.min(mbrCoords[0].x, mbrCoords[2].x); 205 | ry1[i] = Math.min(mbrCoords[0].y, mbrCoords[2].y); 206 | rx2[i] = Math.max(mbrCoords[0].x, mbrCoords[2].x); 207 | ry2[i] = Math.max(mbrCoords[0].y, mbrCoords[2].y); 208 | } 209 | } 210 | 211 | 212 | // Perform the join now 213 | quickSort.sort(rSortable, 0, rSize); 214 | int i = 0, j = 0; 215 | 216 | while (i < lTuples.length && j < rSize) { 217 | if (lx1[i] < rx1[j]) { 218 | int jj = j; 219 | // Compare left object i to all right object jj 220 | while (jj < rSize && rx1[jj] <= lx2[i]) { 221 | if (lx2[i] > rx1[jj] && rx2[jj] > lx1[i] && 222 | ly2[i] > ry1[jj] && ry2[jj] > ly1[i]) { 223 | boolean report = true; 224 | if (dupAvoidance) { 225 | // Performs the reference point technique to avoid duplicates 226 | double intersectX = Math.max(lx1[i], rx1[jj]); 227 | if (intersectX >= mbrX1 && intersectX < mbrX2) { 228 | double intersectY = Math.max(ly1[i], ry1[jj]); 229 | report = intersectY >= mbrY1 && intersectY < mbrY2; 230 | } else { 231 | report = false; 232 | } 233 | } 234 | if (report) { 235 | addToAnswer(output, lTuples[i], rTuples[jj]); 236 | } 237 | } 238 | jj++; 239 | progress(); 240 | } 241 | i++; 242 | } else { 243 | int ii = i; 244 | // Compare all left objects ii to the right object j 245 | while (ii < lTuples.length && lx1[ii] <= rx2[j]) { 246 | if (lx2[ii] > rx1[j] && rx2[j] > lx1[ii] && 247 | ly2[ii] > ry1[j] && ry2[j] > ly1[ii]) { 248 | boolean report = true; 249 | if (dupAvoidance) { 250 | // Performs the reference point technique to avoid duplicates 251 | double intersectX = Math.max(lx1[ii], rx1[j]); 252 | if (intersectX >= mbrX1 && intersectX < mbrX2) { 253 | double intersectY = Math.max(ly1[ii], ry1[j]); 254 | report = intersectY >= mbrY1 && intersectY < mbrY2; 255 | } else { 256 | report = false; 257 | } 258 | } 259 | if (report) { 260 | addToAnswer(output, lTuples[ii], rTuples[j]); 261 | } 262 | } 263 | ii++; 264 | progress(); 265 | } 266 | j++; 267 | } 268 | progress(); 269 | } 270 | } 271 | } 272 | return output; 273 | } 274 | 275 | 276 | private void addToAnswer(DataBag output, Tuple lTuple, Tuple rTuple) { 277 | List attrs = lTuple.getAll(); 278 | attrs.addAll(rTuple.getAll()); 279 | Tuple outTuple = tupleFactory.newTuple(attrs); 280 | output.add(outTuple); 281 | } 282 | 283 | 284 | private int detectGeomColumn(Tuple t) throws ExecException { 285 | for (int i = 0; i < t.size(); i++) { 286 | try { 287 | geomParser.parseGeom(t.get(i)); 288 | return i; 289 | } catch (ExecException e) { 290 | // Nothing to do. Not in this column 291 | } 292 | } 293 | throw new ExecException("Cannot detect a geometry column in "+t); 294 | } 295 | 296 | public Schema outputSchema(Schema input) { 297 | try { 298 | // Column $0 is a bag of tuples that represent the left dataset 299 | Schema leftSchema = input.getField(0).schema.getField(0).schema; 300 | // Column $1 is a bag of tuples that represent the right dataset 301 | Schema rightSchema = input.getField(1).schema.getField(0).schema; 302 | 303 | Schema tupleSchema = new Schema(); 304 | for (FieldSchema leftAttr : leftSchema.getFields()) { 305 | leftAttr = leftAttr.clone(); 306 | leftAttr.alias = "left::"+leftAttr.alias; 307 | tupleSchema.add(leftAttr); 308 | } 309 | for (FieldSchema rightAttr : rightSchema.getFields()) { 310 | rightAttr = rightAttr.clone(); 311 | rightAttr.alias = "right::"+rightAttr.alias; 312 | tupleSchema.add(rightAttr); 313 | 314 | } 315 | 316 | FieldSchema outSchema = new Schema.FieldSchema("result", tupleSchema); 317 | outSchema.type = DataType.BAG; 318 | 319 | return new Schema(outSchema); 320 | } catch (Exception e) { 321 | return null; 322 | } 323 | } 324 | 325 | } 326 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Touches.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.FilterFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import com.esri.core.geometry.ogc.OGCGeometry; 16 | 17 | 18 | /** 19 | * A UDF that tests whether a geometry touches another geometry or not 20 | * @author Tuan Pham 21 | * 22 | */ 23 | public class Touches extends FilterFunc { 24 | 25 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 26 | 27 | @Override 28 | public Boolean exec(Tuple input) throws IOException { 29 | OGCGeometry geom1 = null, geom2 = null; 30 | try { 31 | geom1 = geometryParser.parseGeom(input.get(0)); 32 | geom2 = geometryParser.parseGeom(input.get(1)); 33 | return geom1.touches(geom2); 34 | } catch (ExecException ee) { 35 | throw new GeoException(geom1, geom2, ee); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Union.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | 12 | import org.apache.pig.Algebraic; 13 | import org.apache.pig.EvalFunc; 14 | import org.apache.pig.backend.executionengine.ExecException; 15 | import org.apache.pig.data.DataBag; 16 | import org.apache.pig.data.DataByteArray; 17 | import org.apache.pig.data.Tuple; 18 | import org.apache.pig.data.TupleFactory; 19 | 20 | import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; 21 | import com.esri.core.geometry.ogc.OGCGeometry; 22 | import com.esri.core.geometry.ogc.OGCGeometryCollection; 23 | 24 | /** 25 | * Finds the union of a set of shapes. This is an algebraic function which works 26 | * more efficiently by computing sub-unions of some shapes and finally computing 27 | * the overall union. 28 | * @author Ahmed Eldawy 29 | * 30 | */ 31 | public class Union extends EvalFunc implements Algebraic { 32 | 33 | private static final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 34 | 35 | @Override 36 | public DataByteArray exec(Tuple input) throws IOException { 37 | return new DataByteArray(union(input).asBinary().array()); 38 | } 39 | 40 | @Override 41 | public String getInitial() { return Initial.class.getName();} 42 | 43 | @Override 44 | public String getIntermed() { return Intermed.class.getName();} 45 | 46 | @Override 47 | public String getFinal() { return Final.class.getName(); } 48 | 49 | static public class Initial extends EvalFunc { 50 | @Override 51 | public Tuple exec(Tuple input) throws IOException { 52 | // Retrieve the first element (tuple) in the given bag 53 | return ((DataBag)input.get(0)).iterator().next(); 54 | } 55 | } 56 | 57 | static public class Intermed extends EvalFunc { 58 | @Override 59 | public Tuple exec(Tuple input) throws IOException { 60 | return TupleFactory.getInstance().newTuple( 61 | new DataByteArray(union(input).asBinary().array())); 62 | } 63 | } 64 | 65 | static public class Final extends EvalFunc { 66 | @Override 67 | public DataByteArray exec(Tuple input) throws IOException { 68 | return new DataByteArray(union(input).asBinary().array()); 69 | } 70 | } 71 | 72 | static protected OGCGeometry union(Tuple input) throws ExecException { 73 | DataBag values = (DataBag)input.get(0); 74 | if (values.size() == 0) 75 | return null; 76 | ArrayList all_geoms = new ArrayList(); 77 | for (Tuple one_geom : values) { 78 | OGCGeometry parsedGeom = geometryParser.parseGeom(one_geom.get(0)); 79 | all_geoms.add(parsedGeom); 80 | } 81 | 82 | // Do a union of all_geometries in the recommended way (using buffer(0)) 83 | OGCGeometryCollection geom_collection = new OGCConcreteGeometryCollection( 84 | all_geoms, all_geoms.get(0).getEsriSpatialReference()); 85 | return geom_collection.union(all_geoms.get(0)); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/Within.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import java.io.IOException; 11 | 12 | import org.apache.pig.FilterFunc; 13 | import org.apache.pig.backend.executionengine.ExecException; 14 | import org.apache.pig.data.Tuple; 15 | 16 | import com.esri.core.geometry.ogc.OGCGeometry; 17 | 18 | 19 | /** 20 | * A UDF that tests whether a geometry is within another geometry or not 21 | * @author Tuan Pham 22 | * 23 | */ 24 | public class Within extends FilterFunc { 25 | 26 | private final ESRIGeometryParser geometryParser = new ESRIGeometryParser(); 27 | 28 | @Override 29 | public Boolean exec(Tuple input) throws IOException { 30 | OGCGeometry geom1 = null, geom2 = null; 31 | try { 32 | geom1 = geometryParser.parseGeom(input.get(0)); 33 | geom2 = geometryParser.parseGeom(input.get(1)); 34 | return geom1.within(geom2); 35 | } catch (ExecException ee) { 36 | throw new GeoException(geom1, geom2, ee); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/XMax.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Coordinate; 16 | import org.locationtech.jts.geom.Geometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the left bound of the object 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class XMax extends EvalFunc { 25 | 26 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 27 | 28 | @Override 29 | public Double exec(Tuple input) throws IOException { 30 | Geometry geom = null; 31 | try { 32 | Object v = input.get(0); 33 | geom = geometryParser.parseGeom(v); 34 | Coordinate[] coords = geom.getEnvelope().getCoordinates(); 35 | if (coords.length == 0) 36 | throw new ExecException("XMax cannot work on empty geometires"); 37 | if (coords.length == 1) 38 | return coords[0].x; 39 | if (coords.length == 2) 40 | return Math.max(coords[0].x, coords[1].x); 41 | return Math.max(coords[0].x, coords[2].x); 42 | } catch (ExecException ee) { 43 | throw new GeoException(geom, ee); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/XMin.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Coordinate; 16 | import org.locationtech.jts.geom.Geometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the left bound of the object 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class XMin extends EvalFunc { 25 | 26 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 27 | 28 | @Override 29 | public Double exec(Tuple input) throws IOException { 30 | Object v = input.get(0); 31 | Geometry geom = geometryParser.parseGeom(v); 32 | Coordinate[] coords = geom.getEnvelope().getCoordinates(); 33 | if (coords.length == 0) 34 | throw new ExecException("XMin cannot work on empty geometires"); 35 | if (coords.length == 1) 36 | return coords[0].x; 37 | if (coords.length == 2) 38 | return Math.min(coords[0].x, coords[1].x); 39 | return Math.min(coords[0].x, coords[2].x); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/YMax.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Coordinate; 16 | import org.locationtech.jts.geom.Geometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the left bound of the object 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class YMax extends EvalFunc { 25 | 26 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 27 | 28 | @Override 29 | public Double exec(Tuple input) throws IOException { 30 | Geometry geom = null; 31 | try { 32 | Object v = input.get(0); 33 | geom = geometryParser.parseGeom(v); 34 | Coordinate[] coords = geom.getEnvelope().getCoordinates(); 35 | if (coords.length == 0) 36 | throw new ExecException("YMax cannot work on empty geometires"); 37 | if (coords.length == 1) 38 | return coords[0].y; 39 | if (coords.length == 2) 40 | return Math.max(coords[0].y, coords[1].y); 41 | return Math.max(coords[0].y, coords[2].y); 42 | } catch (ExecException ee) { 43 | throw new GeoException(geom, ee); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/edu/umn/cs/pigeon/YMin.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import java.io.IOException; 10 | 11 | import org.apache.pig.EvalFunc; 12 | import org.apache.pig.backend.executionengine.ExecException; 13 | import org.apache.pig.data.Tuple; 14 | 15 | import org.locationtech.jts.geom.Coordinate; 16 | import org.locationtech.jts.geom.Geometry; 17 | 18 | 19 | /** 20 | * A UDF that returns the left bound of the object 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class YMin extends EvalFunc { 25 | 26 | private final JTSGeometryParser geometryParser = new JTSGeometryParser(); 27 | 28 | @Override 29 | public Double exec(Tuple input) throws IOException { 30 | Geometry geom = null; 31 | try { 32 | Object v = input.get(0); 33 | geom = geometryParser.parseGeom(v); 34 | Coordinate[] coords = geom.getEnvelope().getCoordinates(); 35 | if (coords.length == 0) 36 | throw new ExecException("YMin cannot work on empty geometires"); 37 | if (coords.length == 1) 38 | return coords[0].y; 39 | if (coords.length == 2) 40 | return Math.min(coords[0].y, coords[1].y); 41 | return Math.min(coords[0].y, coords[2].y); 42 | } catch (ExecException ee) { 43 | throw new GeoException(geom, ee); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/pigeon_import.pig: -------------------------------------------------------------------------------- 1 | DEFINE ST_Area edu.umn.cs.pigeon.Area; 2 | DEFINE ST_AsHex edu.umn.cs.pigeon.AsHex; 3 | DEFINE ST_AsText edu.umn.cs.pigeon.AsText; 4 | DEFINE ST_Boundary edu.umn.cs.pigeon.Boundary; 5 | DEFINE ST_Break edu.umn.cs.pigeon.Break; 6 | DEFINE ST_Buffer edu.umn.cs.pigeon.Buffer; 7 | DEFINE ST_Connect edu.umn.cs.pigeon.Connect; 8 | DEFINE ST_Contains edu.umn.cs.pigeon.Contains; 9 | DEFINE ST_ConvexHull edu.umn.cs.pigeon.ConvexHull; 10 | DEFINE ST_Crosses edu.umn.cs.pigeon.Crosses; 11 | DEFINE ST_Decompose edu.umn.cs.pigeon.Decompose; 12 | DEFINE ST_Difference edu.umn.cs.pigeon.Difference; 13 | DEFINE ST_Envelope edu.umn.cs.pigeon.Envelope; 14 | DEFINE ST_ESRIFromWKB edu.umn.cs.pigeon.ESRIShapeFromWKB; 15 | DEFINE ST_ESRIFromText edu.umn.cs.pigeon.ESRIShapeFromText; 16 | DEFINE ST_Extent edu.umn.cs.pigeon.Extent; 17 | DEFINE ST_GeomFromWKB edu.umn.cs.pigeon.GeometryFromWKB; 18 | DEFINE ST_GeomFromText edu.umn.cs.pigeon.GeometryFromText; 19 | DEFINE ST_GridCell edu.umn.cs.pigeon.GridCell; 20 | DEFINE ST_GridPartition edu.umn.cs.pigeon.GridPartition; 21 | DEFINE ST_Intersection edu.umn.cs.pigeon.Intersection; 22 | DEFINE ST_Intersects edu.umn.cs.pigeon.Intersects; 23 | DEFINE ST_IsEmpty edu.umn.cs.pigeon.IsEmpty; 24 | DEFINE ST_IsValid edu.umn.cs.pigeon.IsValid; 25 | DEFINE ST_MakeBox edu.umn.cs.pigeon.MakeBox; 26 | DEFINE ST_MakeLine edu.umn.cs.pigeon.MakeLine; 27 | DEFINE ST_MakeLinePolygon edu.umn.cs.pigeon.MakeLinePolygon; 28 | DEFINE ST_MakePoint edu.umn.cs.pigeon.MakePoint; 29 | DEFINE ST_MakePolygon edu.umn.cs.pigeon.MakePolygon; 30 | DEFINE ST_MakeSegments edu.umn.cs.pigeon.MakeSegments; 31 | DEFINE ST_NumPoints edu.umn.cs.pigeon.NumPoints; 32 | DEFINE ST_Overlaps edu.umn.cs.pigeon.Overlaps; 33 | DEFINE ST_SJPlaneSweep edu.umn.cs.pigeon.SJPlaneSweep; 34 | DEFINE ST_Touches edu.umn.cs.pigeon.Touches; 35 | DEFINE ST_Union edu.umn.cs.pigeon.Union; 36 | DEFINE ST_Within edu.umn.cs.pigeon.Within; 37 | DEFINE ST_XMin edu.umn.cs.pigeon.XMin; 38 | DEFINE ST_XMax edu.umn.cs.pigeon.XMax; 39 | DEFINE ST_YMin edu.umn.cs.pigeon.YMin; 40 | DEFINE ST_YMax edu.umn.cs.pigeon.YMax; 41 | 42 | DEFINE PBSM(dataset1, dataset2, geom1, geom2) RETURNS overlappingPairs { 43 | dataset1_mbrs = FOREACH $dataset1 GENERATE (*) AS tuple1, ST_Envelope($geom1) AS mbr1; 44 | dataset2_mbrs = FOREACH $dataset2 GENERATE (*) AS tuple2, ST_Envelope($geom2) AS mbr2; 45 | mbrs1 = FOREACH dataset1_mbrs GENERATE mbr1 AS mbr; 46 | mbrs2 = FOREACH dataset2_mbrs GENERATE mbr2 AS mbr; 47 | allmbrs = UNION mbrs1, mbrs2; 48 | gallmbrs = GROUP allmbrs ALL; 49 | gridInfo = FOREACH gallmbrs GENERATE ST_Extent(allmbrs.mbr) AS gridMBR, 50 | (INT)CEIL(SQRT((DOUBLE)(COUNT(allmbrs))/10000.0)) AS gridSize; 51 | dataset1xgrid = CROSS dataset1_mbrs, gridInfo; 52 | partitioned1 = FOREACH dataset1xgrid GENERATE *, 53 | FLATTEN(ST_GridPartition(tuple1.$geom1, gridMBR, gridSize)) AS cellid; 54 | dataset2xgrid = CROSS dataset2_mbrs, gridInfo; 55 | partitioned2 = FOREACH dataset2xgrid GENERATE *, 56 | FLATTEN(ST_GridPartition(tuple2.$geom2, gridMBR, gridSize)) AS cellid; 57 | allpartitioned = COGROUP partitioned1 BY (cellid, gridMBR, gridSize), partitioned2 BY (cellid, gridMBR, gridSize); 58 | finalResult = FOREACH allpartitioned GENERATE FLATTEN 59 | (ST_SJPlaneSweep(partitioned1.tuple1, partitioned2.tuple2, ST_GridCell(group.cellid, group.gridMBR, group.gridSize), partitioned1.mbr1, partitioned2.mbr2)); 60 | $overlappingPairs = FOREACH finalResult GENERATE FLATTEN(tuple1), FLATTEN(tuple2); 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /src/main/resources/sample.pig: -------------------------------------------------------------------------------- 1 | REGISTER pigeon-0.2.2.jar 2 | REGISTER esri-geometry-api-1.2.1.jar; 3 | REGISTER jts-1.8.jar; 4 | 5 | IMPORT 'pigeon_import.pig'; 6 | 7 | /* Your code goes here */ -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestArea.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | /** 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class TestArea extends TestCase { 25 | 26 | public void testShouldWorkWithWKT() throws Exception { 27 | ArrayList data = new ArrayList(); 28 | data.add(new String[] {"1", "POLYGON ((0 0, 0 3, 5 5, 10 0, 0 0))"}); 29 | String datafile = TestHelper.createTempFile(data, "\t"); 30 | datafile = datafile.replace("\\", "\\\\"); 31 | PigServer pig = new PigServer(LOCAL); 32 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 33 | "B = FOREACH A GENERATE "+Area.class.getName()+"(geom);"; 34 | pig.registerQuery(query); 35 | Iterator it = pig.openIterator("B"); 36 | ArrayList correct_result = new ArrayList(); 37 | correct_result.add(15 + 5 + 12.5); 38 | Iterator areas = correct_result.iterator(); 39 | while (it.hasNext() && areas.hasNext()) { 40 | Tuple tuple = (Tuple) it.next(); 41 | if (tuple == null) 42 | break; 43 | Double area = (Double) tuple.get(0); 44 | assertEquals(areas.next(), area); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestAsText.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import static org.apache.pig.ExecType.LOCAL; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | 14 | import junit.framework.TestCase; 15 | 16 | import org.apache.pig.PigServer; 17 | import org.apache.pig.data.Tuple; 18 | 19 | import com.esri.core.geometry.ogc.OGCGeometry; 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestAsText extends TestCase { 26 | 27 | private ArrayList geometries; 28 | private ArrayList data; 29 | 30 | 31 | public TestAsText() { 32 | geometries = new ArrayList(); 33 | geometries.add(OGCGeometry.fromText("Polygon ((0 0, 0 3, 4 5, 10 0, 0 0))")); 34 | 35 | data = new ArrayList(); 36 | for (int i = 0; i < geometries.size(); i++) { 37 | data.add(new String[] {Integer.toString(i), geometries.get(i).asText()}); 38 | } 39 | } 40 | 41 | public void testShouldWorkWithWKT() throws Exception { 42 | String datafile = TestHelper.createTempFile(data, "\t"); 43 | datafile = datafile.replace("\\", "\\\\"); 44 | PigServer pig = new PigServer(LOCAL); 45 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 46 | "B = FOREACH A GENERATE "+AsText.class.getName()+"(geom);"; 47 | pig.registerQuery(query); 48 | Iterator it = pig.openIterator("B"); 49 | Iterator geoms = geometries.iterator(); 50 | while (it.hasNext() && geoms.hasNext()) { 51 | Tuple tuple = (Tuple) it.next(); 52 | OGCGeometry geom = geoms.next(); 53 | if (tuple == null) 54 | break; 55 | String wkt = (String) tuple.get(0); 56 | assertEquals(geom.asText(), wkt); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestBounds.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestBounds extends TestCase { 26 | 27 | public void testShouldWorkWithWKT() throws Exception { 28 | // Create polygons 29 | ArrayList data = new ArrayList(); 30 | data.add(new String[] {"1", "POLYGON((3 2, 8 2, 3 7, 3 2))"}); 31 | String datafile = TestHelper.createTempFile(data, "\t"); 32 | datafile = datafile.replace("\\", "\\\\"); 33 | PigServer pig = new PigServer(LOCAL); 34 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" 35 | + "B = FOREACH A GENERATE " + XMin.class.getName() + "(geom), " 36 | + YMin.class.getName() + "(geom), " + XMax.class.getName() + "(geom), " 37 | + YMax.class.getName() + "(geom);"; 38 | pig.registerQuery(query); 39 | Iterator it = pig.openIterator("B"); 40 | 41 | double[] correct_bounds = {3.0, 2.0, 8.0, 7.0}; 42 | 43 | while (it.hasNext()) { 44 | Tuple tuple = (Tuple) it.next(); 45 | if (tuple == null) 46 | break; 47 | for (int i = 0; i < correct_bounds.length; i++) 48 | assertEquals(correct_bounds[i], (double)(Double)tuple.get(i)); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestBreak.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.Vector; 15 | 16 | import junit.framework.TestCase; 17 | 18 | import org.apache.pig.PigServer; 19 | import org.apache.pig.data.Tuple; 20 | 21 | 22 | /** 23 | * Breaks down a linestring or polygon into line segments each containing 24 | * two points only. 25 | * @author Ahmed Eldawy 26 | * 27 | */ 28 | public class TestBreak extends TestCase { 29 | 30 | public void testShouldWorkWithLinestring() throws Exception { 31 | ArrayList data = new ArrayList(); 32 | data.add(new String[] {"0", "LINESTRING (0 0, 0 1, 1 3)"}); 33 | String datafile = TestHelper.createTempFile(data, "\t"); 34 | datafile = datafile.replace("\\", "\\\\"); 35 | PigServer pig = new PigServer(LOCAL); 36 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, geom);\n" + 37 | "B = FOREACH A GENERATE geom_id, FLATTEN("+Break.class.getName()+"(geom));"; 38 | pig.registerQuery(query); 39 | Iterator it = pig.openIterator("B"); 40 | Vector expectedResults = new Vector(); 41 | expectedResults.add(new String[] {"0", "0", "0", "0", "0", "1"}); 42 | expectedResults.add(new String[] {"0", "1", "0", "1", "1", "3"}); 43 | Iterator expected = expectedResults.iterator(); 44 | int count = 0; 45 | while (it.hasNext() && expected.hasNext()) { 46 | Tuple tuple = (Tuple) it.next(); 47 | String[] expectedResult = expected.next(); 48 | if (tuple == null) 49 | break; 50 | assertEquals(Integer.parseInt(expectedResult[0]), tuple.get(0)); 51 | assertEquals(Integer.parseInt(expectedResult[1]), tuple.get(1)); 52 | assertEquals(Double.parseDouble(expectedResult[2]), tuple.get(2)); 53 | assertEquals(Double.parseDouble(expectedResult[3]), tuple.get(3)); 54 | assertEquals(Double.parseDouble(expectedResult[4]), tuple.get(4)); 55 | assertEquals(Double.parseDouble(expectedResult[5]), tuple.get(5)); 56 | count++; 57 | } 58 | assertEquals(expectedResults.size(), count); 59 | } 60 | 61 | public void testShouldWorkWithPolygon() throws Exception { 62 | ArrayList data = new ArrayList(); 63 | data.add(new String[] {"1", "POLYGON ((0 0, 0 1, 1 0, 0 0))"}); 64 | String datafile = TestHelper.createTempFile(data, "\t"); 65 | datafile = datafile.replace("\\", "\\\\"); 66 | PigServer pig = new PigServer(LOCAL); 67 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, geom);\n" + 68 | "B = FOREACH A GENERATE geom_id, FLATTEN("+Break.class.getName()+"(geom));"; 69 | pig.registerQuery(query); 70 | Iterator it = pig.openIterator("B"); 71 | Vector expectedResults = new Vector(); 72 | expectedResults.add(new String[] {"1", "0", "0", "0", "0", "1"}); 73 | expectedResults.add(new String[] {"1", "1", "0", "1", "1", "0"}); 74 | expectedResults.add(new String[] {"1", "2", "1", "0", "0", "0"}); 75 | Iterator expected = expectedResults.iterator(); 76 | int count = 0; 77 | while (it.hasNext() && expected.hasNext()) { 78 | Tuple tuple = (Tuple) it.next(); 79 | String[] expectedResult = expected.next(); 80 | if (tuple == null) 81 | break; 82 | assertEquals(Integer.parseInt(expectedResult[0]), tuple.get(0)); 83 | assertEquals(Integer.parseInt(expectedResult[1]), tuple.get(1)); 84 | assertEquals(Double.parseDouble(expectedResult[2]), tuple.get(2)); 85 | assertEquals(Double.parseDouble(expectedResult[3]), tuple.get(3)); 86 | assertEquals(Double.parseDouble(expectedResult[4]), tuple.get(4)); 87 | assertEquals(Double.parseDouble(expectedResult[5]), tuple.get(5)); 88 | count++; 89 | } 90 | assertEquals(expectedResults.size(), count); 91 | } 92 | 93 | public void testShouldWorkWithGeometryCollection() throws Exception { 94 | ArrayList data = new ArrayList(); 95 | data.add(new String[] {"1", "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0)))"}); 96 | String datafile = TestHelper.createTempFile(data, "\t"); 97 | datafile = datafile.replace("\\", "\\\\"); 98 | PigServer pig = new PigServer(LOCAL); 99 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, geom);\n" + 100 | "B = FOREACH A GENERATE geom_id, FLATTEN("+Break.class.getName()+"(geom));"; 101 | pig.registerQuery(query); 102 | Iterator it = pig.openIterator("B"); 103 | Vector expectedResults = new Vector(); 104 | expectedResults.add(new String[] {"1", "0", "0", "0", "0", "1"}); 105 | expectedResults.add(new String[] {"1", "1", "0", "1", "1", "0"}); 106 | expectedResults.add(new String[] {"1", "2", "1", "0", "0", "0"}); 107 | Iterator expected = expectedResults.iterator(); 108 | int count = 0; 109 | while (it.hasNext() && expected.hasNext()) { 110 | Tuple tuple = (Tuple) it.next(); 111 | String[] expectedResult = expected.next(); 112 | if (tuple == null) 113 | break; 114 | assertEquals(Integer.parseInt(expectedResult[0]), tuple.get(0)); 115 | assertEquals(Integer.parseInt(expectedResult[1]), tuple.get(1)); 116 | assertEquals(Double.parseDouble(expectedResult[2]), tuple.get(2)); 117 | assertEquals(Double.parseDouble(expectedResult[3]), tuple.get(3)); 118 | assertEquals(Double.parseDouble(expectedResult[4]), tuple.get(4)); 119 | assertEquals(Double.parseDouble(expectedResult[5]), tuple.get(5)); 120 | count++; 121 | } 122 | assertEquals(expectedResults.size(), count); 123 | } 124 | 125 | public void testShouldSkipPoints() throws Exception { 126 | ArrayList data = new ArrayList(); 127 | data.add(new String[] {"1", "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0)), POINT (5 5))"}); 128 | String datafile = TestHelper.createTempFile(data, "\t"); 129 | datafile = datafile.replace("\\", "\\\\"); 130 | PigServer pig = new PigServer(LOCAL); 131 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, geom);\n" + 132 | "B = FOREACH A GENERATE geom_id, FLATTEN("+Break.class.getName()+"(geom));"; 133 | pig.registerQuery(query); 134 | Iterator it = pig.openIterator("B"); 135 | Vector expectedResults = new Vector(); 136 | expectedResults.add(new String[] {"1", "0", "0", "0", "0", "1"}); 137 | expectedResults.add(new String[] {"1", "1", "0", "1", "1", "0"}); 138 | expectedResults.add(new String[] {"1", "2", "1", "0", "0", "0"}); 139 | Iterator expected = expectedResults.iterator(); 140 | int count = 0; 141 | while (it.hasNext() && expected.hasNext()) { 142 | Tuple tuple = (Tuple) it.next(); 143 | String[] expectedResult = expected.next(); 144 | if (tuple == null) 145 | break; 146 | assertEquals(Integer.parseInt(expectedResult[0]), tuple.get(0)); 147 | assertEquals(Integer.parseInt(expectedResult[1]), tuple.get(1)); 148 | assertEquals(Double.parseDouble(expectedResult[2]), tuple.get(2)); 149 | assertEquals(Double.parseDouble(expectedResult[3]), tuple.get(3)); 150 | assertEquals(Double.parseDouble(expectedResult[4]), tuple.get(4)); 151 | assertEquals(Double.parseDouble(expectedResult[5]), tuple.get(5)); 152 | count++; 153 | } 154 | assertEquals(expectedResults.size(), count); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestBuffer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import static org.apache.pig.ExecType.LOCAL; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | 14 | import junit.framework.TestCase; 15 | 16 | import org.apache.pig.PigServer; 17 | import org.apache.pig.data.Tuple; 18 | 19 | import com.esri.core.geometry.ogc.OGCGeometry; 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestBuffer extends TestCase { 26 | 27 | 28 | 29 | public TestBuffer() { 30 | 31 | } 32 | 33 | public void testShouldWorkWithDoubleBuffer() throws Exception { 34 | ArrayList data = new ArrayList(); 35 | data.add(new String[] {"1", "LINESTRING (0 0, 0 3, 4 5, 10 0)"}); 36 | String datafile = TestHelper.createTempFile(data, "\t"); 37 | datafile = datafile.replace("\\", "\\\\"); 38 | PigServer pig = new PigServer(LOCAL); 39 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 40 | "B = FOREACH A GENERATE "+ Buffer.class.getName()+"(geom, 1.0);"; 41 | pig.registerQuery(query); 42 | Iterator it = pig.openIterator("B"); 43 | ArrayList geometries = new ArrayList(); 44 | geometries.add(OGCGeometry.fromText("LINESTRING (0 0, 0 3, 4 5, 10 0)").buffer(1.0).asText()); 45 | Iterator geoms = geometries.iterator(); 46 | while (it.hasNext() && geoms.hasNext()) { 47 | Tuple tuple = (Tuple) it.next(); 48 | if (tuple == null) 49 | break; 50 | String exptected_result = geoms.next(); 51 | TestHelper.assertGeometryEqual(exptected_result, tuple.get(0)); 52 | } 53 | } 54 | 55 | public void testShouldWorkWithIntegerBuffer() throws Exception { 56 | ArrayList data = new ArrayList(); 57 | data.add(new String[] {"1", "LINESTRING (0 0, 0 3, 4 5, 10 0)"}); 58 | String datafile = TestHelper.createTempFile(data, "\t"); 59 | datafile = datafile.replace("\\", "\\\\"); 60 | PigServer pig = new PigServer(LOCAL); 61 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 62 | "B = FOREACH A GENERATE "+ Buffer.class.getName()+"(geom, 1);"; 63 | pig.registerQuery(query); 64 | Iterator it = pig.openIterator("B"); 65 | ArrayList geometries = new ArrayList(); 66 | geometries.add(OGCGeometry.fromText("LINESTRING (0 0, 0 3, 4 5, 10 0)").buffer(1.0).asText()); 67 | Iterator geoms = geometries.iterator(); 68 | while (it.hasNext() && geoms.hasNext()) { 69 | Tuple tuple = (Tuple) it.next(); 70 | if (tuple == null) 71 | break; 72 | String exptected_result = geoms.next(); 73 | TestHelper.assertGeometryEqual(exptected_result, tuple.get(0)); 74 | } 75 | } 76 | 77 | public void testShouldWorkWithUntypedColumn() throws Exception { 78 | ArrayList data = new ArrayList(); 79 | data.add(new String[] {"1", "LINESTRING (0 0, 0 3, 4 5, 10 0)"}); 80 | String datafile = TestHelper.createTempFile(data, "\t"); 81 | datafile = datafile.replace("\\", "\\\\"); 82 | PigServer pig = new PigServer(LOCAL); 83 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 84 | "B = FOREACH A GENERATE "+ Buffer.class.getName()+"(geom, id);"; 85 | pig.registerQuery(query); 86 | Iterator it = pig.openIterator("B"); 87 | ArrayList geometries = new ArrayList(); 88 | geometries.add(OGCGeometry.fromText("LINESTRING (0 0, 0 3, 4 5, 10 0)").buffer(1.0).asText()); 89 | Iterator geoms = geometries.iterator(); 90 | while (it.hasNext() && geoms.hasNext()) { 91 | Tuple tuple = (Tuple) it.next(); 92 | if (tuple == null) 93 | break; 94 | String exptected_result = geoms.next(); 95 | TestHelper.assertGeometryEqual(exptected_result, tuple.get(0)); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestConnect.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import static org.apache.pig.ExecType.LOCAL; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | 14 | import junit.framework.TestCase; 15 | 16 | import org.apache.pig.PigServer; 17 | import org.apache.pig.data.Tuple; 18 | 19 | 20 | /** 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class TestConnect extends TestCase { 25 | 26 | public void testShouldWorkWithDirectPolygon() throws Exception { 27 | ArrayList data = new ArrayList(); 28 | data.add(new String[] {"1", "2", "LINESTRING(0 0, 2 0, 3 1)"}); 29 | data.add(new String[] {"2", "3", "LINESTRING(3 1, 2 2)"}); 30 | data.add(new String[] {"3", "1", "LINESTRING(2 2, 1 2, 0 0)"}); 31 | 32 | String datafile = TestHelper.createTempFile(data, "\t"); 33 | datafile = datafile.replace("\\", "\\\\"); 34 | PigServer pig = new PigServer(LOCAL); 35 | String query = "A = LOAD 'file:" + datafile + "' AS (first_point: long, last_point: long, linestring: chararray);\n" + 36 | "B = GROUP A ALL;" + 37 | "C = FOREACH B GENERATE "+Connect.class.getName()+"(A.first_point, A.last_point, A.linestring);"; 38 | pig.registerQuery(query); 39 | Iterator it = pig.openIterator("C"); 40 | String expectedResult = "Polygon((0 0, 2 0, 3 1, 2 2, 1 2, 0 0))"; 41 | int count = 0; 42 | while (it.hasNext()) { 43 | Tuple tuple = (Tuple) it.next(); 44 | if (tuple == null) 45 | break; 46 | TestHelper.assertGeometryEqual(expectedResult, tuple.get(0)); 47 | count++; 48 | } 49 | assertEquals(1, count); 50 | } 51 | 52 | 53 | public void testShouldWorkWithSomeReversedSegments() throws Exception { 54 | ArrayList data = new ArrayList(); 55 | data.add(new String[] {"1", "2", "LINESTRING(0 0, 2 0, 3 1)"}); 56 | data.add(new String[] {"3", "2", "LINESTRING(2 2, 3 1)"}); 57 | data.add(new String[] {"3", "1", "LINESTRING(2 2, 1 2, 0 0)"}); 58 | 59 | String datafile = TestHelper.createTempFile(data, "\t"); 60 | datafile = datafile.replace("\\", "\\\\"); 61 | PigServer pig = new PigServer(LOCAL); 62 | String query = "A = LOAD 'file:" + datafile + "' AS (first_point: long, last_point: long, linestring: chararray);\n" + 63 | "B = GROUP A ALL;" + 64 | "C = FOREACH B GENERATE "+Connect.class.getName()+"(A.first_point, A.last_point, A.linestring);"; 65 | pig.registerQuery(query); 66 | Iterator it = pig.openIterator("C"); 67 | String expectedResult = "Polygon((0 0, 2 0, 3 1, 2 2, 1 2, 0 0))"; 68 | int count = 0; 69 | while (it.hasNext()) { 70 | Tuple tuple = (Tuple) it.next(); 71 | if (tuple == null) 72 | break; 73 | TestHelper.assertGeometryEqual(expectedResult, tuple.get(0)); 74 | count++; 75 | } 76 | assertEquals(1, count); 77 | } 78 | 79 | public void testShouldMakeGeometryCollectionForDisconnectedShapes() throws Exception { 80 | ArrayList data = new ArrayList(); 81 | data.add(new String[] {"1", "2", "LINESTRING(0 0, 2 0, 3 1)"}); 82 | data.add(new String[] {"2", "3", "LINESTRING(3 1, 2 2)"}); 83 | data.add(new String[] {"3", "1", "LINESTRING(2 2, 1 2, 0 0)"}); 84 | data.add(new String[] {"7", "8", "LINESTRING(10 8, 8 5)"}); 85 | 86 | String datafile = TestHelper.createTempFile(data, "\t"); 87 | datafile = datafile.replace("\\", "\\\\"); 88 | PigServer pig = new PigServer(LOCAL); 89 | String query = "A = LOAD 'file:" + datafile + "' AS (first_point: long, last_point: long, linestring: chararray);\n" + 90 | "B = GROUP A ALL;" + 91 | "C = FOREACH B GENERATE "+Connect.class.getName()+"(A.first_point, A.last_point, A.linestring);"; 92 | pig.registerQuery(query); 93 | Iterator it = pig.openIterator("C"); 94 | int count = 0; 95 | String expectedResult = "GEOMETRYCOLLECTION(LINESTRING(10 8, 8 5), " 96 | + "POLYGON((0 0, 2 0, 3 1, 2 2, 1 2, 0 0)))"; 97 | while (it.hasNext()) { 98 | Tuple tuple = (Tuple) it.next(); 99 | if (tuple == null) 100 | break; 101 | TestHelper.assertGeometryEqual(expectedResult, tuple.get(0)); 102 | count++; 103 | } 104 | assertEquals(1, count); 105 | } 106 | 107 | public void testShouldWorkWithEntriesOfTypePolygon() throws Exception { 108 | ArrayList data = new ArrayList(); 109 | data.add(new String[] {"1", "1", "POLYGON((0 0, 2 0, 3 1, 0 0))"}); 110 | data.add(new String[] {"2", "2", "POLYGON((5 5, 5 6, 6 5, 5 5))"}); 111 | data.add(new String[] {"7", "8", "LINESTRING(10 8, 8 5)"}); 112 | String expectedResult = "GeometryCollection(LineString(10 8, 8 5), " 113 | + "Polygon((0 0, 2 0, 3 1, 0 0)), Polygon((5 5, 5 6, 6 5, 5 5)))"; 114 | 115 | String datafile = TestHelper.createTempFile(data, "\t"); 116 | datafile = datafile.replace("\\", "\\\\"); 117 | PigServer pig = new PigServer(LOCAL); 118 | String query = "A = LOAD 'file:" + datafile + "' AS (first_point: long, last_point: long, linestring: chararray);\n" + 119 | "B = GROUP A ALL;" + 120 | "C = FOREACH B GENERATE "+Connect.class.getName()+"(A.first_point, A.last_point, A.linestring);"; 121 | pig.registerQuery(query); 122 | Iterator it = pig.openIterator("C"); 123 | int count = 0; 124 | while (it.hasNext()) { 125 | Tuple tuple = (Tuple) it.next(); 126 | if (tuple == null) 127 | break; 128 | TestHelper.assertGeometryEqual(expectedResult, tuple.get(0)); 129 | count++; 130 | } 131 | assertEquals(1, count); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestConvexHull.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import static org.apache.pig.ExecType.LOCAL; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | 14 | import junit.framework.TestCase; 15 | 16 | import org.apache.pig.PigServer; 17 | import org.apache.pig.data.Tuple; 18 | 19 | /** 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class TestConvexHull extends TestCase { 24 | 25 | public void testShouldWorkWithWKT() throws Exception { 26 | ArrayList data = new ArrayList(); 27 | data.add(new String[] {"0", "POLYGON ((0 0, 6 0, 0 5, 0 0))"}); 28 | data.add(new String[] {"1", "POLYGON ((2 2, 7 2, 2 6, 2 2))"}); 29 | data.add(new String[] {"2", "POLYGON ((3 -2, 8 -1, 8 4, 3 -2))"}); 30 | String datafile = TestHelper.createTempFile(data, "\t"); 31 | datafile = datafile.replace("\\", "\\\\"); 32 | PigServer pig = new PigServer(LOCAL); 33 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 34 | "B = GROUP A ALL;\n" + 35 | "C = FOREACH B GENERATE "+ ConvexHull.class.getName()+"(A.geom);"; 36 | pig.registerQuery(query); 37 | Iterator it = pig.openIterator("C"); 38 | 39 | // Calculate the convex hull outside Pig 40 | String true_convex_hull = "POLYGON ((0 0, 3 -2, 8 -1, 8 4, 2 6, 0 5, 0 0))"; 41 | 42 | int output_size = 0; 43 | 44 | while (it.hasNext()) { 45 | Tuple tuple = (Tuple) it.next(); 46 | if (tuple == null) 47 | break; 48 | output_size++; 49 | TestHelper.assertGeometryEqual(true_convex_hull, tuple.get(0)); 50 | } 51 | assertEquals(1, output_size); 52 | } 53 | 54 | public void testShouldWorkWithOneObject() throws Exception { 55 | ArrayList data = new ArrayList(); 56 | data.add(new String[] {"1", "POLYGON ((0 0, 0 2, 1 1, 2 2, 2 0, 0 0))"}); 57 | String datafile = TestHelper.createTempFile(data, "\t"); 58 | datafile = datafile.replace("\\", "\\\\"); 59 | PigServer pig = new PigServer(LOCAL); 60 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 61 | "B = GROUP A ALL;\n" + 62 | "C = FOREACH B GENERATE "+ConvexHull.class.getName()+"(A.geom);"; 63 | pig.registerQuery(query); 64 | Iterator it = pig.openIterator("C"); 65 | 66 | // Calculate the convex hull outside Pig 67 | String true_convex_hull = "POLYGON ((0 0, 0 2, 2 2, 2 0, 0 0))"; 68 | 69 | int output_size = 0; 70 | 71 | while (it.hasNext()) { 72 | Tuple tuple = (Tuple) it.next(); 73 | if (tuple == null) 74 | break; 75 | output_size++; 76 | TestHelper.assertGeometryEqual(true_convex_hull, tuple.get(0)); 77 | } 78 | assertEquals(1, output_size); 79 | } 80 | 81 | public void testShouldWorkAsUnaryOperation() throws Exception { 82 | ArrayList data = new ArrayList(); 83 | data.add(new String[] {"0", "POLYGON ((0 0, 6 0, 0 5, 0 0))"}); 84 | data.add(new String[] {"1", "POLYGON ((0 0, 0 2, 1 1, 2 2, 2 0, 0 0))"}); 85 | data.add(new String[] {"2", "POLYGON ((3 -2, 8 -1, 8 4, 3 -2))"}); 86 | String datafile = TestHelper.createTempFile(data, "\t"); 87 | datafile = datafile.replace("\\", "\\\\"); 88 | PigServer pig = new PigServer(LOCAL); 89 | 90 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 91 | "B = FOREACH A GENERATE "+ConvexHull.class.getName()+"(geom);"; 92 | pig.registerQuery(query); 93 | Iterator it = pig.openIterator("B"); 94 | 95 | int output_size = 0; 96 | 97 | ArrayList expected_results = new ArrayList(); 98 | expected_results.add("POLYGON ((0 0, 6 0, 0 5, 0 0))"); 99 | expected_results.add("POLYGON ((0 0, 0 2, 2 2, 2 0, 0 0))"); 100 | expected_results.add("POLYGON ((3 -2, 8 -1, 8 4, 3 -2))"); 101 | Iterator geoms = expected_results.iterator(); 102 | while (it.hasNext()) { 103 | Tuple tuple = (Tuple) it.next(); 104 | if (tuple == null) 105 | break; 106 | output_size++; 107 | TestHelper.assertGeometryEqual(geoms.next(), tuple.get(0)); 108 | } 109 | assertEquals(3, output_size); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestCrosses.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import static org.apache.pig.ExecType.LOCAL; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | 14 | import junit.framework.TestCase; 15 | 16 | import org.apache.pig.PigServer; 17 | import org.apache.pig.data.Tuple; 18 | 19 | /** 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class TestCrosses extends TestCase { 24 | 25 | public void testShouldWorkWithWKT() throws Exception { 26 | ArrayList data = new ArrayList(); 27 | data.add(new String[] {"0", "LINESTRING (0 0, 3 3)", "LINESTRING (0 3, 3 0)"}); 28 | data.add(new String[] {"1", "LINESTRING (0 0, 0 3)", "LINESTRING (0 3, 3 0)"}); 29 | data.add(new String[] {"2", "LINESTRING (0 0, 3 0)", "LINESTRING (0 3, 3 3)"}); 30 | String datafile = TestHelper.createTempFile(data, "\t"); 31 | datafile = datafile.replace("\\", "\\\\"); 32 | PigServer pig = new PigServer(LOCAL); 33 | String query = "A = LOAD 'file:" + datafile + "' as (id: int, geom1, geom2);\n" + 34 | "B = FILTER A BY "+Crosses.class.getName()+"(geom1, geom2);"; 35 | pig.registerQuery(query); 36 | 37 | Iterator it = pig.openIterator("B"); 38 | 39 | ArrayList expected_results = new ArrayList(); 40 | expected_results.add(0); 41 | Iterator result_ids = expected_results.iterator(); 42 | int count = 0; 43 | while (it.hasNext() && result_ids.hasNext()) { 44 | Tuple tuple = (Tuple) it.next(); 45 | count++; 46 | assertEquals(result_ids.next(), (Integer)tuple.get(0)); 47 | } 48 | assertFalse(it.hasNext()); 49 | assertEquals(1, count); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestDifference.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | package edu.umn.cs.pigeon; 8 | 9 | import static org.apache.pig.ExecType.LOCAL; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | 14 | import junit.framework.TestCase; 15 | 16 | import org.apache.pig.PigServer; 17 | import org.apache.pig.data.Tuple; 18 | 19 | /** 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class TestDifference extends TestCase { 24 | 25 | public void testShouldWorkWithWKT() throws Exception { 26 | ArrayList data = new ArrayList(); 27 | data.add(new String[] {"0", "POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0))", "POLYGON ((4 2, 4 4, 2 4, 4 2))"}); 28 | data.add(new String[] {"1", "POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0))", "POLYGON ((4 0, 4 2, 2 2, 4 0))"}); 29 | String datafile = TestHelper.createTempFile(data, "\t"); 30 | datafile = datafile.replace("\\", "\\\\"); 31 | PigServer pig = new PigServer(LOCAL); 32 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom1, geom2);\n" + 33 | "B = FOREACH A GENERATE "+ Difference.class.getName()+"(geom1, geom2);"; 34 | pig.registerQuery(query); 35 | Iterator it = pig.openIterator("B"); 36 | 37 | ArrayList expected_results = new ArrayList(); 38 | expected_results.add("POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0))"); 39 | expected_results.add("POLYGON ((0 0, 3 0, 3 1, 2 2, 3 2, 3 3, 0 3, 0 0))"); 40 | Iterator geom = expected_results.iterator(); 41 | 42 | while (it.hasNext() && geom.hasNext()) { 43 | Tuple tuple = (Tuple) it.next(); 44 | if (tuple == null) 45 | break; 46 | TestHelper.assertGeometryEqual(geom.next(), tuple.get(0)); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestEnvelope.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | /** 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class TestEnvelope extends TestCase { 25 | 26 | public void testShouldWorkWithWKT() throws Exception { 27 | ArrayList data = new ArrayList(); 28 | data.add(new String[] {"0", "LINESTRING (0 0, 0 3, 4 5, 10 0)"}); 29 | String datafile = TestHelper.createTempFile(data, "\t"); 30 | datafile = datafile.replace("\\", "\\\\"); 31 | PigServer pig = new PigServer(LOCAL); 32 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 33 | "B = FOREACH A GENERATE "+ Envelope.class.getName()+"(geom);"; 34 | pig.registerQuery(query); 35 | Iterator it = pig.openIterator("B"); 36 | 37 | ArrayList geometries = new ArrayList(); 38 | geometries.add("POLYGON ((0 0, 10 0, 10 5, 0 5, 0 0))"); 39 | Iterator geoms = geometries.iterator(); 40 | while (it.hasNext() && geoms.hasNext()) { 41 | String expected_result = geoms.next(); 42 | Tuple tuple = (Tuple) it.next(); 43 | if (tuple == null) 44 | break; 45 | TestHelper.assertGeometryEqual(expected_result, tuple.get(0)); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestExtent.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestExtent extends TestCase { 26 | 27 | public void testShouldWorkWithWKT() throws Exception { 28 | ArrayList data = new ArrayList(); 29 | data.add(new String[] {"0", "POLYGON ((0 0, 6 0, 0 5, 0 0))"}); 30 | data.add(new String[] {"0", "LINESTRING (2 2, 7 2, 2 6)"}); 31 | data.add(new String[] {"0", "POINT (3 -2)"}); 32 | String datafile = TestHelper.createTempFile(data, "\t"); 33 | datafile = datafile.replace("\\", "\\\\"); 34 | PigServer pig = new PigServer(LOCAL); 35 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 36 | "B = GROUP A ALL;\n" + 37 | "C = FOREACH B GENERATE "+ Extent.class.getName()+"(A.geom);"; 38 | pig.registerQuery(query); 39 | Iterator it = pig.openIterator("C"); 40 | 41 | // Calculate the union outside Pig 42 | String true_mbr = "POLYGON ((0 -2, 7 -2, 7 6, 0 6, 0 -2))"; 43 | 44 | int output_size = 0; 45 | 46 | while (it.hasNext()) { 47 | Tuple tuple = (Tuple) it.next(); 48 | if (tuple == null) 49 | break; 50 | output_size++; 51 | TestHelper.assertGeometryEqual(true_mbr, tuple.get(0)); 52 | } 53 | assertEquals(1, output_size); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestGeometryParser.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import java.util.Arrays; 11 | 12 | import junit.framework.TestCase; 13 | 14 | import org.apache.pig.backend.executionengine.ExecException; 15 | import org.apache.pig.data.DataByteArray; 16 | 17 | import com.esri.core.geometry.ogc.OGCGeometry; 18 | 19 | public class TestGeometryParser extends TestCase { 20 | 21 | private ESRIGeometryParser geometry_parser = new ESRIGeometryParser(); 22 | private OGCGeometry polygon; 23 | 24 | public TestGeometryParser() { 25 | polygon = OGCGeometry.fromText("Polygon ((0 0, 0 3, 4 5, 10 0, 0 0))"); 26 | } 27 | 28 | public void testShouldConvertBinaryToHex() throws Exception { 29 | byte[][] binaryTable = new byte[][] { 30 | {(byte)0xaa, (byte)0xbb, (byte)0xcd, 0x0f}, 31 | {} 32 | }; 33 | String[] hexTable = { 34 | "AABBCD0F", 35 | "" 36 | }; 37 | for (int i = 0; i < binaryTable.length; i ++) { 38 | byte[] binary = binaryTable[i]; 39 | String hex = hexTable[i]; 40 | assertTrue(hex.equals(ESRIGeometryParser.bytesToHex(binary))); 41 | } 42 | } 43 | 44 | public void testShouldConvertHexToBinary() throws Exception { 45 | byte[][] binaryTable = new byte[][] { 46 | {(byte)0xaa, (byte)0xbb, (byte)0xcd, 0x0f}, 47 | {} 48 | }; 49 | String[] hexTable = { 50 | "AABBCD0F", 51 | "" 52 | }; 53 | for (int i = 0; i < binaryTable.length; i ++) { 54 | byte[] binary = binaryTable[i]; 55 | String hex = hexTable[i]; 56 | assertTrue(Arrays.equals(binary, ESRIGeometryParser.hexToBytes(hex))); 57 | } 58 | } 59 | 60 | public void testShouldParseWKT() throws Exception { 61 | String wkt = polygon.asText(); 62 | OGCGeometry parsed = geometry_parser.parseGeom(wkt); 63 | assertTrue(polygon.equals(parsed)); 64 | } 65 | 66 | public void testShouldParseHexString() throws Exception { 67 | byte[] binary = polygon.asBinary().array(); 68 | String hex = ESRIGeometryParser.bytesToHex(binary); 69 | OGCGeometry parsed = geometry_parser.parseGeom(hex); 70 | assertTrue(polygon.equals(parsed)); 71 | } 72 | 73 | public void testShouldParseWKB() throws Exception { 74 | byte[] binary = polygon.asBinary().array(); 75 | DataByteArray barray = new DataByteArray(binary); 76 | OGCGeometry parsed = geometry_parser.parseGeom(barray); 77 | assertTrue(polygon.equals(parsed)); 78 | } 79 | 80 | public void testShouldParseWKTEncodedInBinary() throws Exception { 81 | String wkt = polygon.asText(); 82 | DataByteArray barray = new DataByteArray(wkt); 83 | OGCGeometry parsed = geometry_parser.parseGeom(barray); 84 | assertTrue(polygon.equals(parsed)); 85 | } 86 | 87 | public void testShouldThrowAnExceptionOnGarbageText() throws Exception { 88 | boolean exceptionThrown = false; 89 | try { 90 | geometry_parser.parseGeom("asdfasdf"); 91 | } catch (ExecException e) { 92 | exceptionThrown = true; 93 | } 94 | assertTrue(exceptionThrown); 95 | } 96 | 97 | public void testShouldThrowAnExceptionOnGarbageBinary() throws Exception { 98 | boolean exceptionThrown = false; 99 | try { 100 | geometry_parser.parseGeom(new DataByteArray(new byte[] {0, 1, 2, 3})); 101 | } catch (ExecException e) { 102 | exceptionThrown = true; 103 | } 104 | assertTrue(exceptionThrown); 105 | } 106 | 107 | public void testShouldReturnNullOnNullInput() throws Exception { 108 | OGCGeometry parsed = geometry_parser.parseGeom(null); 109 | assertNull(parsed); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestGridPartition.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.awt.Point; 13 | import java.util.ArrayList; 14 | import java.util.Iterator; 15 | import java.util.Vector; 16 | 17 | import junit.framework.TestCase; 18 | 19 | import org.apache.pig.PigServer; 20 | import org.apache.pig.data.Tuple; 21 | 22 | 23 | /** 24 | * @author Ahmed Eldawy 25 | * 26 | */ 27 | public class TestGridPartition extends TestCase { 28 | 29 | public void testShouldWorkWithWKT() throws Exception { 30 | // Create polygons 31 | ArrayList data = new ArrayList(); 32 | data.add(new String[] {"1", "LINESTRING(0.5 0.5, 1.5 1.5)"}); 33 | data.add(new String[] {"2", "POINT(0.5 1.5)"}); 34 | data.add(new String[] {"3", "LINESTRING(0.5 0.5, 0.5 1.5)"}); 35 | String datafile = TestHelper.createTempFile(data, "\t"); 36 | datafile = datafile.replace("\\", "\\\\"); 37 | PigServer pig = new PigServer(LOCAL); 38 | String query = "A = LOAD 'file:" + datafile + "' as (id: int, geom);\n" + 39 | "B = FOREACH A GENERATE id, FLATTEN("+ 40 | GridPartition.class.getName()+"(geom, 'MULTIPOINT(0 0, 2 2)', 2));"; 41 | pig.registerQuery(query); 42 | Iterator it = pig.openIterator("B"); 43 | 44 | Vector correctResult = new Vector(); 45 | correctResult.add(new Point(1, 0)); 46 | correctResult.add(new Point(1, 1)); 47 | correctResult.add(new Point(1, 2)); 48 | correctResult.add(new Point(1, 3)); 49 | correctResult.add(new Point(2, 2)); 50 | correctResult.add(new Point(3, 0)); 51 | correctResult.add(new Point(3, 2)); 52 | 53 | while (it.hasNext()) { 54 | Tuple tuple = (Tuple) it.next(); 55 | if (tuple == null) 56 | break; 57 | Point resultPair = new Point((Integer)tuple.get(0), (Integer)tuple.get(1)); 58 | assertTrue("Could not find the pair "+resultPair, 59 | correctResult.remove(resultPair)); 60 | } 61 | assertTrue(correctResult.isEmpty()); 62 | } 63 | 64 | public void testShouldWorkWithUnitGrid() throws Exception { 65 | // Create polygons 66 | ArrayList data = new ArrayList(); 67 | data.add(new String[] {"1", "LINESTRING(0.5 0.5, 1.5 1.5)"}); 68 | String datafile = TestHelper.createTempFile(data, "\t"); 69 | datafile = datafile.replace("\\", "\\\\"); 70 | PigServer pig = new PigServer(LOCAL); 71 | String query = "A = LOAD 'file:" + datafile + "' as (id: int, geom);\n" + 72 | "B = FOREACH A GENERATE id, FLATTEN("+ 73 | GridPartition.class.getName()+"(geom, 'MULTIPOINT(0 0, 5 5)', 1));"; 74 | pig.registerQuery(query); 75 | Iterator it = pig.openIterator("B"); 76 | 77 | Vector correctResult = new Vector(); 78 | correctResult.add(new Point(1, 0)); 79 | 80 | while (it.hasNext()) { 81 | Tuple tuple = (Tuple) it.next(); 82 | if (tuple == null) 83 | break; 84 | Point resultPair = new Point((Integer)tuple.get(0), (Integer)tuple.get(1)); 85 | assertTrue("Could not find the pair "+resultPair, 86 | correctResult.remove(resultPair)); 87 | } 88 | assertTrue(correctResult.isEmpty()); 89 | } 90 | 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestHelper.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import java.io.File; 11 | import java.io.PrintWriter; 12 | import java.nio.ByteBuffer; 13 | import java.util.ArrayList; 14 | import java.util.Vector; 15 | 16 | import junit.framework.TestCase; 17 | 18 | import org.apache.pig.data.DataByteArray; 19 | import org.junit.Test; 20 | 21 | import com.esri.core.geometry.ogc.OGCGeometry; 22 | import com.esri.core.geometry.ogc.OGCGeometryCollection; 23 | 24 | public class TestHelper extends TestCase { 25 | @Test 26 | public void testTest() { 27 | assertTrue(true); 28 | } 29 | 30 | public static void assertGeometryEqual(Object expected, Object test) { 31 | OGCGeometry expected_geom = expected instanceof String? 32 | OGCGeometry.fromText((String)expected) : 33 | OGCGeometry.fromBinary(ByteBuffer.wrap(((DataByteArray)expected).get())); 34 | OGCGeometry test_geom = test instanceof String? 35 | OGCGeometry.fromText((String)test) : 36 | OGCGeometry.fromBinary(ByteBuffer.wrap(((DataByteArray)test).get())); 37 | if (expected_geom instanceof OGCGeometryCollection && 38 | test_geom instanceof OGCGeometryCollection) { 39 | OGCGeometryCollection expected_coln = (OGCGeometryCollection) expected_geom; 40 | OGCGeometryCollection test_coln = (OGCGeometryCollection) test_geom; 41 | assertEquals(expected_coln.numGeometries(), test_coln.numGeometries()); 42 | Vector expectedGeometries = new Vector(); 43 | for (int i = 0; i < expected_coln.numGeometries(); i++) { 44 | expectedGeometries.add(expected_coln.geometryN(i)); 45 | } 46 | for (int i = 0; i < test_coln.numGeometries(); i++) { 47 | OGCGeometry geom = test_coln.geometryN(i); 48 | int j = 0; 49 | while (j < expectedGeometries.size() && !geom.equals(expectedGeometries.get(j))) 50 | j++; 51 | 52 | assertTrue(j < expectedGeometries.size()); 53 | expectedGeometries.remove(j++); 54 | } 55 | } else { 56 | assertTrue("Exepcted geometry to be '"+expected+"' but found '"+test+"'", 57 | expected_geom.equals(test_geom)); 58 | } 59 | } 60 | 61 | private static String join(String delimiter, String[] strings) { 62 | String string = strings[0].toString(); 63 | for (int i = 1; i < strings.length; i++) { 64 | string += delimiter + strings[i].toString(); 65 | } 66 | return string; 67 | } 68 | 69 | public static String createTempFile(ArrayList myData, 70 | String delimiter) throws Exception { 71 | File tmpFile = File.createTempFile("test", ".txt"); 72 | if (tmpFile.exists()) { 73 | tmpFile.delete(); 74 | } 75 | PrintWriter pw = new PrintWriter(tmpFile); 76 | for (int i = 0; i < myData.size(); i++) { 77 | pw.println(join(delimiter, myData.get(i))); 78 | System.err.println(join(delimiter, myData.get(i))); 79 | } 80 | pw.close(); 81 | tmpFile.deleteOnExit(); 82 | return tmpFile.getAbsolutePath(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestMakeBox.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.Vector; 15 | 16 | import junit.framework.TestCase; 17 | 18 | import org.apache.pig.PigServer; 19 | import org.apache.pig.data.Tuple; 20 | 21 | 22 | /** 23 | * @author Ahmed Eldawy 24 | * 25 | */ 26 | public class TestMakeBox extends TestCase { 27 | 28 | public void testShouldWorkWithWKT() throws Exception { 29 | ArrayList data = new ArrayList(); 30 | data.add(new String[] {"0"}); 31 | String datafile = TestHelper.createTempFile(data, "\t"); 32 | datafile = datafile.replace("\\", "\\\\"); 33 | PigServer pig = new PigServer(LOCAL); 34 | String query = "A = LOAD 'file:" + datafile + "' as (dummy);\n" + 35 | "B = FOREACH A GENERATE "+MakeBox.class.getName()+"(1, 2, 5, 7);"; 36 | pig.registerQuery(query); 37 | Iterator it = pig.openIterator("B"); 38 | Vector expectedResult = new Vector(); 39 | expectedResult.add("POLYGON((1 2, 5 2, 5 7, 1 7, 1 2))"); 40 | 41 | Iterator geoms = expectedResult.iterator(); 42 | int count = 0; 43 | while (it.hasNext() && geoms.hasNext()) { 44 | Tuple tuple = (Tuple) it.next(); 45 | String expected_result = geoms.next(); 46 | if (tuple == null) 47 | break; 48 | TestHelper.assertGeometryEqual(expected_result, tuple.get(0)); 49 | count++; 50 | } 51 | assertEquals(expectedResult.size(), count); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestMakeLine.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.Vector; 15 | 16 | import junit.framework.TestCase; 17 | 18 | import org.apache.pig.PigServer; 19 | import org.apache.pig.data.Tuple; 20 | 21 | 22 | /** 23 | * @author Ahmed Eldawy 24 | * 25 | */ 26 | public class TestMakeLine extends TestCase { 27 | 28 | public void testShouldWorkWithWKT() throws Exception { 29 | ArrayList data = new ArrayList(); 30 | data.add(new String[] {"0", "0", "POINT (0.0 0.0)"}); 31 | data.add(new String[] {"0", "1", "POINT (0.0 3.0)"}); 32 | data.add(new String[] {"0", "2", "POINT (4.0 5.0)"}); 33 | data.add(new String[] {"0", "3", "POINT (10.0 0.0)"}); 34 | data.add(new String[] {"1", "0", "POINT (5.0 6.0)"}); 35 | data.add(new String[] {"1", "1", "POINT (10.0 3.0)"}); 36 | data.add(new String[] {"1", "2", "POINT (7.0 13.0)"}); 37 | String datafile = TestHelper.createTempFile(data, "\t"); 38 | datafile = datafile.replace("\\", "\\\\"); 39 | PigServer pig = new PigServer(LOCAL); 40 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id, point_pos, point);\n" + 41 | "B = ORDER A BY point_pos;" + 42 | "C = GROUP B BY geom_id;" + 43 | "D = FOREACH C GENERATE group, "+MakeLine.class.getName()+"(B.point);"; 44 | pig.registerQuery(query); 45 | Iterator it = pig.openIterator("D"); 46 | Vector expectedResult = new Vector(); 47 | expectedResult.add("LINESTRING(0 0, 0 3, 4 5, 10 0)"); 48 | expectedResult.add("LINESTRING(5 6, 10 3, 7 13)"); 49 | 50 | Iterator geoms = expectedResult.iterator(); 51 | int count = 0; 52 | while (it.hasNext() && geoms.hasNext()) { 53 | Tuple tuple = (Tuple) it.next(); 54 | String expected_result = geoms.next(); 55 | if (tuple == null) 56 | break; 57 | TestHelper.assertGeometryEqual(expected_result, tuple.get(1)); 58 | count++; 59 | } 60 | assertEquals(expectedResult.size(), count); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestMakeLinePolygon.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestMakeLinePolygon extends TestCase { 26 | 27 | public void testShouldWorkWithWKT() throws Exception { 28 | ArrayList data = new ArrayList(); 29 | data.add(new String[] {"1", "1", "0", "POINT (0.0 0.0)"}); 30 | data.add(new String[] {"1", "2", "1", "POINT (0.0 3.0)"}); 31 | data.add(new String[] {"1", "3", "2", "POINT (4.0 5.0)"}); 32 | data.add(new String[] {"1", "4", "3", "POINT (10.0 0.0)"}); 33 | data.add(new String[] {"2", "1", "0", "POINT (5.0 6.0)"}); 34 | data.add(new String[] {"2", "2", "1", "POINT (10.0 3.0)"}); 35 | data.add(new String[] {"2", "3", "2", "POINT (7.0 13.0)"}); 36 | data.add(new String[] {"3", "1", "0", "POINT (0.0 0.0)"}); 37 | data.add(new String[] {"3", "2", "1", "POINT (10.0 10.0)"}); 38 | data.add(new String[] {"3", "3", "2", "POINT (18.0 5.0)"}); 39 | data.add(new String[] {"3", "1", "3", "POINT (0.0 0.0)"}); 40 | String datafile = TestHelper.createTempFile(data, "\t"); 41 | datafile = datafile.replace("\\", "\\\\"); 42 | PigServer pig = new PigServer(LOCAL); 43 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, point_id: int, point_pos: int, point);\n" + 44 | "B = ORDER A BY point_pos;" + 45 | "C = GROUP B BY geom_id;" + 46 | "D = FOREACH C GENERATE group, "+MakeLinePolygon.class.getName()+"(B.point_id, B.point);"; 47 | pig.registerQuery(query); 48 | Iterator it = pig.openIterator("D"); 49 | 50 | ArrayList expectedResult = new ArrayList(); 51 | expectedResult.add("LINESTRING(0 0, 0 3, 4 5, 10 0)"); 52 | expectedResult.add("LINESTRING(5 6, 10 3, 7 13)"); 53 | expectedResult.add("POLYGON((0 0, 10 10, 18 5, 0 0))"); 54 | Iterator geoms = expectedResult.iterator(); 55 | int count = 0; 56 | while (it.hasNext() && geoms.hasNext()) { 57 | Tuple tuple = (Tuple) it.next(); 58 | String expected_result = geoms.next(); 59 | if (tuple == null) 60 | break; 61 | TestHelper.assertGeometryEqual(expected_result, tuple.get(1)); 62 | count++; 63 | } 64 | assertEquals(expectedResult.size(), count); 65 | } 66 | 67 | public void testShouldFallBackToLinestringForShortLists() throws Exception { 68 | ArrayList data = new ArrayList(); 69 | data.add(new String[] {"1", "1", "0", "POINT (0 0)"}); 70 | data.add(new String[] {"1", "2", "1", "POINT (4 5)"}); 71 | data.add(new String[] {"1", "1", "2", "POINT (0 0)"}); 72 | String datafile = TestHelper.createTempFile(data, "\t"); 73 | datafile = datafile.replace("\\", "\\\\"); 74 | PigServer pig = new PigServer(LOCAL); 75 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, point_id: int, point_pos: int, point);\n" + 76 | "B = ORDER A BY point_pos;" + 77 | "C = GROUP B BY geom_id;" + 78 | "D = FOREACH C GENERATE group, "+AsText.class.getName()+"("+ 79 | MakeLinePolygon.class.getName()+"(B.point_id, B.point));"; 80 | pig.registerQuery(query); 81 | Iterator it = pig.openIterator("D"); 82 | int count = 0; 83 | while (it.hasNext()) { 84 | Tuple tuple = (Tuple) it.next(); 85 | if (tuple == null) 86 | break; 87 | String expected_result = "LINESTRING(0 0, 4 5)"; 88 | TestHelper.assertGeometryEqual(expected_result, tuple.get(1)); 89 | count++; 90 | } 91 | assertEquals(1, count); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestMakePoint.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | /** 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class TestMakePoint extends TestCase { 25 | 26 | protected void innerTest(String schema) throws Exception { 27 | 28 | ArrayList data = new ArrayList(); 29 | data.add(new String[] {"0", "1.0", "1.0"}); 30 | data.add(new String[] {"1", "-1.0", "-3.55"}); 31 | data.add(new String[] {"2", "0.0", "0.0"}); 32 | 33 | String datafile = TestHelper.createTempFile(data, "\t"); 34 | datafile = datafile.replace("\\", "\\\\"); 35 | PigServer pig = new PigServer(LOCAL); 36 | String query = "A = LOAD 'file:" + datafile + "' as "+schema+";\n" + 37 | "B = FOREACH A GENERATE "+ MakePoint.class.getName()+"(x, y);"; 38 | pig.registerQuery(query); 39 | Iterator it = pig.openIterator("B"); 40 | 41 | ArrayList points = new ArrayList(); 42 | points.add("POINT (1 1)"); 43 | points.add("POINT (-1 -3.55)"); 44 | points.add("POINT (0 0)"); 45 | Iterator i_point = points.iterator(); 46 | while (it.hasNext() && i_point.hasNext()) { 47 | Tuple tuple = (Tuple) it.next(); 48 | String expected_result = i_point.next(); 49 | if (tuple == null) 50 | break; 51 | TestHelper.assertGeometryEqual(expected_result, tuple.get(0)); 52 | } 53 | } 54 | 55 | public void testShouldMakeAPointFromDoubles() throws Exception { 56 | innerTest("(id:int, x:double, y:double)"); 57 | } 58 | 59 | public void testShouldMakeAPointFromDataByteArray() throws Exception { 60 | innerTest("(id, x, y)"); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestMakePolygon.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | /** 21 | * @author Ahmed Eldawy 22 | * 23 | */ 24 | public class TestMakePolygon extends TestCase { 25 | 26 | public void testShouldWorkWithWKT() throws Exception { 27 | ArrayList data = new ArrayList(); 28 | data.add(new String[] {"0", "0", "POINT (0.0 0.0)"}); 29 | data.add(new String[] {"0", "1", "POINT (0.0 3.0)"}); 30 | data.add(new String[] {"0", "2", "POINT (4.0 5.0)"}); 31 | data.add(new String[] {"0", "3", "POINT (10.0 0.0)"}); 32 | data.add(new String[] {"1", "0", "POINT (5.0 6.0)"}); 33 | data.add(new String[] {"1", "1", "POINT (7.0 13.0)"}); 34 | data.add(new String[] {"1", "2", "POINT (10.0 3.0)"}); 35 | String datafile = TestHelper.createTempFile(data, "\t"); 36 | datafile = datafile.replace("\\", "\\\\"); 37 | PigServer pig = new PigServer(LOCAL); 38 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id, point_pos, point);\n" + 39 | "B = ORDER A BY point_pos;" + 40 | "C = GROUP B BY geom_id;" + 41 | "D = FOREACH C GENERATE group, "+MakePolygon.class.getName()+"(B.point);"; 42 | pig.registerQuery(query); 43 | Iterator it = pig.openIterator("D"); 44 | 45 | ArrayList geometries = new ArrayList(); 46 | geometries.add("Polygon((0 0, 0 3, 4 5, 10 0, 0 0))"); 47 | geometries.add("Polygon((5 6, 10 3, 7 13, 5 6))"); 48 | Iterator geoms = geometries.iterator(); 49 | 50 | int count = 0; 51 | while (it.hasNext() && geoms.hasNext()) { 52 | Tuple tuple = (Tuple) it.next(); 53 | String geom = geoms.next(); 54 | if (tuple == null) 55 | break; 56 | TestHelper.assertGeometryEqual(geom, tuple.get(1)); 57 | count++; 58 | } 59 | assertEquals(geometries.size(), count); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestMakeSegments.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestMakeSegments extends TestCase { 26 | 27 | public void testShouldWorkWithWKT() throws Exception { 28 | ArrayList data = new ArrayList(); 29 | data.add(new String[] {"1", "1", "0", "POINT (0.0 0.0)"}); 30 | data.add(new String[] {"1", "2", "1", "POINT (0.0 3.0)"}); 31 | data.add(new String[] {"1", "3", "2", "POINT (4.0 5.0)"}); 32 | data.add(new String[] {"1", "4", "3", "POINT (10.0 0.0)"}); 33 | data.add(new String[] {"2", "5", "0", "POINT (5.0 6.0)"}); 34 | data.add(new String[] {"2", "6", "1", "POINT (10.0 3.0)"}); 35 | data.add(new String[] {"2", "7", "2", "POINT (7.0 13.0)"}); 36 | data.add(new String[] {"3", "1", "0", "POINT (0.0 0.0)"}); 37 | data.add(new String[] {"3", "8", "1", "POINT (10.0 10.0)"}); 38 | data.add(new String[] {"3", "9", "2", "POINT (18.0 5.0)"}); 39 | data.add(new String[] {"3", "1", "3", "POINT (0.0 0.0)"}); 40 | String datafile = TestHelper.createTempFile(data, "\t"); 41 | datafile = datafile.replace("\\", "\\\\"); 42 | PigServer pig = new PigServer(LOCAL); 43 | String query = "A = LOAD 'file:" + datafile + "' as (geom_id: int, point_id: int, point_pos: int, point);\n" + 44 | "B = ORDER A BY point_pos;" + 45 | "C = GROUP B BY geom_id;" + 46 | "D = FOREACH C GENERATE group, FLATTEN("+MakeSegments.class.getName()+"(B.point_id, B.point));"; 47 | pig.registerQuery(query); 48 | Iterator it = pig.openIterator("D"); 49 | 50 | ArrayList expectedResults = new ArrayList(); 51 | expectedResults.add(new String[] { "1", "0", "1", "0.0", "0.0", "2", "0.0", "3.0"}); 52 | expectedResults.add(new String[] { "1", "1", "2", "0.0", "3.0", "3", "4.0", "5.0"}); 53 | expectedResults.add(new String[] { "1", "2", "3", "4.0", "5.0", "4", "10.0", "0.0"}); 54 | expectedResults.add(new String[] { "2", "0", "5", "5.0", "6.0", "6", "10.0", "3.0"}); 55 | expectedResults.add(new String[] { "2", "1", "6", "10.0", "3.0", "7", "7.0", "13.0"}); 56 | expectedResults.add(new String[] { "3", "0", "1", "0.0", "0.0", "8", "10.0", "10.0"}); 57 | expectedResults.add(new String[] { "3", "1", "8", "10.0", "10.0", "9", "18.0", "5.0"}); 58 | expectedResults.add(new String[] { "3", "2", "9", "18.0", "5.0", "1", "0.0", "0.0"}); 59 | Iterator expectedResultIter = expectedResults.iterator(); 60 | int count = 0; 61 | while (it.hasNext() && expectedResultIter.hasNext()) { 62 | Tuple tuple = (Tuple) it.next(); 63 | String[] expectedResult = expectedResultIter.next(); 64 | if (tuple == null) 65 | break; 66 | assertEquals(Integer.parseInt(expectedResult[0]), tuple.get(0)); 67 | assertEquals(Integer.parseInt(expectedResult[1]), tuple.get(1)); 68 | assertEquals(Long.parseLong(expectedResult[2]), tuple.get(2)); 69 | assertEquals(Double.parseDouble(expectedResult[3]), tuple.get(3)); 70 | assertEquals(Double.parseDouble(expectedResult[4]), tuple.get(4)); 71 | assertEquals(Long.parseLong(expectedResult[5]), tuple.get(5)); 72 | assertEquals(Double.parseDouble(expectedResult[6]), tuple.get(6)); 73 | assertEquals(Double.parseDouble(expectedResult[7]), tuple.get(7)); 74 | count++; 75 | } 76 | assertEquals(expectedResults.size(), count); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestNumPoints.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestNumPoints extends TestCase { 26 | 27 | public void testShouldWorkWithGeometries() throws Exception { 28 | // Create polygons 29 | ArrayList data = new ArrayList(); 30 | data.add(new String[] {"0", "LINESTRING(0 0, 6 0, 0 6, 0 0)"}); 31 | data.add(new String[] {"1", "POLYGON((3 2, 8 2, 3 7, 3 2))"}); 32 | data.add(new String[] {"2", "POINT(3 2)"}); 33 | data.add(new String[] {"3", "GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(2 -2, 9 -2, 9 5, 2 10))"}); 34 | String datafile = TestHelper.createTempFile(data, "\t"); 35 | datafile = datafile.replace("\\", "\\\\"); 36 | PigServer pig = new PigServer(LOCAL); 37 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 38 | "B = FOREACH A GENERATE "+NumPoints.class.getName()+"(geom);"; 39 | pig.registerQuery(query); 40 | Iterator it = pig.openIterator("B"); 41 | 42 | int output_size = 0; 43 | int[] correct_sizes = {4, 3, 1, 5}; 44 | 45 | while (it.hasNext()) { 46 | Tuple tuple = (Tuple) it.next(); 47 | if (tuple == null) 48 | break; 49 | assertEquals(correct_sizes[output_size], (int)(Integer)tuple.get(0)); 50 | output_size++; 51 | } 52 | assertEquals(correct_sizes.length, output_size); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestUnion.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import static org.apache.pig.ExecType.LOCAL; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | import junit.framework.TestCase; 16 | 17 | import org.apache.pig.PigServer; 18 | import org.apache.pig.data.Tuple; 19 | 20 | 21 | /** 22 | * @author Ahmed Eldawy 23 | * 24 | */ 25 | public class TestUnion extends TestCase { 26 | 27 | public void testShouldWorkWithWKT() throws Exception { 28 | // Create polygons 29 | ArrayList data = new ArrayList(); 30 | data.add(new String[] {"0", "POLYGON((0 0, 6 0, 0 6, 0 0))"}); 31 | data.add(new String[] {"1", "POLYGON((3 2, 8 2, 3 7, 3 2))"}); 32 | data.add(new String[] {"2", "POLYGON((2 -2, 9 -2, 9 5, 2 -2))"}); 33 | String datafile = TestHelper.createTempFile(data, "\t"); 34 | datafile = datafile.replace("\\", "\\\\"); 35 | PigServer pig = new PigServer(LOCAL); 36 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 37 | "B = GROUP A ALL;\n" + 38 | "C = FOREACH B GENERATE "+Union.class.getName()+"(A.geom);"; 39 | pig.registerQuery(query); 40 | Iterator it = pig.openIterator("C"); 41 | 42 | String true_union = "POLYGON((4 0, 2 -2, 9 -2, 9 5, 7 3, 3 7, 3 3, 0 6, 0 0, 4 0)," 43 | + " (5 1, 4 2, 6 2, 5 1))"; 44 | 45 | int output_size = 0; 46 | 47 | while (it.hasNext()) { 48 | Tuple tuple = (Tuple) it.next(); 49 | if (tuple == null) 50 | break; 51 | output_size++; 52 | TestHelper.assertGeometryEqual(true_union, tuple.get(0)); 53 | } 54 | assertEquals(1, output_size); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/edu/umn/cs/pigeon/TestXMin.java: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | * Copyright (C) 2014 by Regents of the University of Minnesota. * 3 | * * 4 | * This Software is released under the Apache License, Version 2.0 * 5 | * http://www.apache.org/licenses/LICENSE-2.0 * 6 | *******************************************************************/ 7 | 8 | package edu.umn.cs.pigeon; 9 | 10 | import junit.framework.TestCase; 11 | import org.apache.pig.PigServer; 12 | import org.apache.pig.data.Tuple; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Iterator; 16 | 17 | import static org.apache.pig.ExecType.LOCAL; 18 | 19 | /** 20 | * @author Ahmed Eldawy 21 | * 22 | */ 23 | public class TestXMin extends TestCase { 24 | 25 | public void testShouldWorkWithPoints() throws Exception { 26 | ArrayList data = new ArrayList(); 27 | data.add(new String[] {"1", "POINT (0 0)"}); 28 | String datafile = TestHelper.createTempFile(data, "\t"); 29 | datafile = datafile.replace("\\", "\\\\"); 30 | PigServer pig = new PigServer(LOCAL); 31 | String query = "A = LOAD 'file:" + datafile + "' as (id, geom);\n" + 32 | "B = FOREACH A GENERATE "+XMin.class.getName()+"(geom);"; 33 | pig.registerQuery(query); 34 | Iterator it = pig.openIterator("B"); 35 | ArrayList correct_result = new ArrayList(); 36 | correct_result.add(0.0); 37 | Iterator xmins = correct_result.iterator(); 38 | while (it.hasNext() && xmins.hasNext()) { 39 | Tuple tuple = (Tuple) it.next(); 40 | if (tuple == null) 41 | break; 42 | Double xmin = (Double) tuple.get(0); 43 | assertEquals(xmins.next(), xmin); 44 | } 45 | } 46 | 47 | } 48 | --------------------------------------------------------------------------------