Skip to content

Commit 76d4718

Browse files
uros-dbcloud-fan
authored andcommitted
[SPARK-53920][GEO][SQL] Introduce GeometryType and GeographyType to Java API
### What changes were proposed in this pull request? Introduce two new geospatial data types to Java API: - `GeographyType` - `GeometryType` Note that the GEOMETRY and GEOGRAPHY logical types were recently included to Spark SQL as part of: #52491. ### Why are the changes needed? Expanding on GEOMETRY and GEOGRAPHY type support across all of the supported APIs. ### Does this PR introduce _any_ user-facing change? Yes, two new data types are now available to users of the Java API. ### How was this patch tested? Added new tests to: - `JavaGeographyTypeSuite` - `JavaGeometryTypeSuite` ### Was this patch authored or co-authored using generative AI tooling? No. Closes #52623 from uros-db/geo-java-types. Authored-by: Uros Bojanic <[email protected]> Signed-off-by: Wenchen Fan <[email protected]>
1 parent 96093bd commit 76d4718

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed

sql/api/src/main/java/org/apache/spark/sql/types/DataTypes.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,42 @@ public class DataTypes {
9494
*/
9595
public static final DataType ShortType = ShortType$.MODULE$;
9696

97+
/**
98+
* Creates a GeographyType by specifying the SRID value.
99+
*
100+
* @since 4.1.0
101+
*/
102+
public static GeographyType createGeographyType(int srid) {
103+
return GeographyType$.MODULE$.apply(srid);
104+
}
105+
106+
/**
107+
* Creates a GeographyType by specifying the CRS value.
108+
*
109+
* @since 4.1.0
110+
*/
111+
public static GeographyType createGeographyType(String crs) {
112+
return GeographyType$.MODULE$.apply(crs);
113+
}
114+
115+
/**
116+
* Creates a GeometryType by specifying the SRID value.
117+
*
118+
* @since 4.1.0
119+
*/
120+
public static GeometryType createGeometryType(int srid) {
121+
return GeometryType$.MODULE$.apply(srid);
122+
}
123+
124+
/**
125+
* Creates a GeometryType by specifying the CRS value.
126+
*
127+
* @since 4.1.0
128+
*/
129+
public static GeometryType createGeometryType(String crs) {
130+
return GeometryType$.MODULE$.apply(crs);
131+
}
132+
97133
/**
98134
* Gets the NullType object.
99135
*/
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql.types;
19+
20+
import org.apache.spark.sql.internal.types.SpatialReferenceSystemMapper;
21+
import org.junit.jupiter.api.Assertions;
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.apache.spark.SparkIllegalArgumentException;
25+
26+
import java.util.stream.Stream;
27+
28+
public class JavaGeographyTypeSuite {
29+
30+
/*
31+
* Test cases GeographyType construction based on SRID.
32+
*/
33+
34+
@Test
35+
public void geographyTypeWithSpecifiedValidSridTest() {
36+
// Valid SRID values for GEOGRAPHY. Note that only 4326 is supported for now.
37+
Stream.of(4326).forEach(srid -> {
38+
DataType geographyType = DataTypes.createGeographyType(srid);
39+
Assertions.assertEquals("GEOGRAPHY(" + srid + ")", geographyType.sql());
40+
Assertions.assertEquals("geography(" + srid + ")", geographyType.typeName());
41+
Assertions.assertEquals("geography(" + srid + ")", geographyType.simpleString());
42+
});
43+
}
44+
45+
@Test
46+
public void geographyTypeWithSpecifiedInvalidSridTest() {
47+
// Invalid SRID values for GEOGRAPHY.
48+
Stream.of(-1, -2, 0, 1, 2).forEach(srid -> {
49+
try {
50+
DataTypes.createGeographyType(srid);
51+
Assertions.fail("Expected SparkIllegalArgumentException for SRID: " + srid);
52+
} catch (SparkIllegalArgumentException e) {
53+
Assertions.assertEquals("ST_INVALID_SRID_VALUE", e.getCondition());
54+
Assertions.assertEquals(String.valueOf(srid), e.getMessageParameters().get("srid"));
55+
}
56+
});
57+
}
58+
59+
/*
60+
* Test cases GeographyType construction based on CRS.
61+
*/
62+
63+
@Test
64+
public void geographyTypeWithSpecifiedValidCrsTest() {
65+
// Valid CRS values for GEOGRAPHY. Note that only 4326 is supported for now.
66+
int srid = GeographyType.GEOGRAPHY_DEFAULT_SRID();
67+
Stream.of("OGC:CRS84", "EPSG:4326").forEach(crs -> {
68+
DataType geographyType = DataTypes.createGeographyType(crs);
69+
Assertions.assertEquals("GEOGRAPHY(" + srid + ")", geographyType.sql());
70+
Assertions.assertEquals("geography(" + srid + ")", geographyType.typeName());
71+
Assertions.assertEquals("geography(" + srid + ")", geographyType.simpleString());
72+
});
73+
}
74+
75+
@Test
76+
public void geographyTypeWithSpecifiedInvalidCrsTest() {
77+
// Invalid CRS values for GEOGRAPHY.
78+
Stream.of("0", "SRID", "SRID:-1", "SRID:4326", "CRS84", "").forEach(crs -> {
79+
try {
80+
DataTypes.createGeographyType(crs);
81+
Assertions.fail("Expected SparkIllegalArgumentException for CRS: " + crs);
82+
} catch (SparkIllegalArgumentException e) {
83+
Assertions.assertEquals("ST_INVALID_CRS_VALUE", e.getCondition());
84+
Assertions.assertEquals(crs, e.getMessageParameters().get("crs"));
85+
}
86+
});
87+
}
88+
89+
@Test
90+
public void geographyTypeWithSpecifiedAnyTest() {
91+
// Special string value "ANY" in place of CRS is used to denote a mixed GEOGRAPHY type.
92+
DataType geographyType = DataTypes.createGeographyType("ANY");
93+
Assertions.assertEquals("GEOGRAPHY(ANY)", geographyType.sql());
94+
Assertions.assertEquals("geography(any)", geographyType.typeName());
95+
Assertions.assertEquals("geography(any)", geographyType.simpleString());
96+
}
97+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql.types;
19+
20+
import org.apache.spark.sql.internal.types.SpatialReferenceSystemMapper;
21+
import org.junit.jupiter.api.Assertions;
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.apache.spark.SparkIllegalArgumentException;
25+
26+
import java.util.stream.Stream;
27+
28+
public class JavaGeometryTypeSuite {
29+
30+
/*
31+
* Test cases GeometryType construction based on SRID.
32+
*/
33+
34+
@Test
35+
public void geometryTypeWithSpecifiedValidSridTest() {
36+
// Valid SRID values for GEOMETRY.
37+
Stream.of(0, 3857, 4326).forEach(srid -> {
38+
DataType geometryType = DataTypes.createGeometryType(srid);
39+
Assertions.assertEquals("GEOMETRY(" + srid + ")", geometryType.sql());
40+
Assertions.assertEquals("geometry(" + srid + ")", geometryType.typeName());
41+
Assertions.assertEquals("geometry(" + srid + ")", geometryType.simpleString());
42+
});
43+
}
44+
45+
@Test
46+
public void geometryTypeWithSpecifiedInvalidSridTest() {
47+
// Invalid SRID values for GEOMETRY.
48+
Stream.of(-1, -2, 1, 2).forEach(srid -> {
49+
try {
50+
DataTypes.createGeometryType(srid);
51+
Assertions.fail("Expected SparkIllegalArgumentException for SRID: " + srid);
52+
} catch (SparkIllegalArgumentException e) {
53+
Assertions.assertEquals("ST_INVALID_SRID_VALUE", e.getCondition());
54+
Assertions.assertEquals(String.valueOf(srid), e.getMessageParameters().get("srid"));
55+
}
56+
});
57+
}
58+
59+
/*
60+
* Test cases GeometryType construction based on CRS.
61+
*/
62+
63+
@Test
64+
public void geometryTypeWithSpecifiedValidCrsTest() {
65+
// Valid CRS values for GEOMETRY.
66+
SpatialReferenceSystemMapper srsMapper = SpatialReferenceSystemMapper.get();
67+
Stream.of("SRID:0", "EPSG:3857", "OGC:CRS84").forEach(crs -> {
68+
int srid = srsMapper.getSrid(crs);
69+
DataType geometryType = DataTypes.createGeometryType(crs);
70+
Assertions.assertEquals("GEOMETRY(" + srid + ")", geometryType.sql());
71+
Assertions.assertEquals("geometry(" + srid + ")", geometryType.typeName());
72+
Assertions.assertEquals("geometry(" + srid + ")", geometryType.simpleString());
73+
});
74+
}
75+
76+
@Test
77+
public void geometryTypeWithSpecifiedInvalidCrsTest() {
78+
// Invalid CRS values for GEOMETRY.
79+
Stream.of("0", "SRID", "SRID:-1", "SRID:4326", "CRS84", "").forEach(crs -> {
80+
try {
81+
DataTypes.createGeometryType(crs);
82+
Assertions.fail("Expected SparkIllegalArgumentException for CRS: " + crs);
83+
} catch (SparkIllegalArgumentException e) {
84+
Assertions.assertEquals("ST_INVALID_CRS_VALUE", e.getCondition());
85+
Assertions.assertEquals(crs, e.getMessageParameters().get("crs"));
86+
}
87+
});
88+
}
89+
90+
@Test
91+
public void geometryTypeWithSpecifiedAnyTest() {
92+
// Special string value "ANY" in place of CRS is used to denote a mixed GEOMETRY type.
93+
DataType geometryType = DataTypes.createGeometryType("ANY");
94+
Assertions.assertEquals("GEOMETRY(ANY)", geometryType.sql());
95+
Assertions.assertEquals("geometry(any)", geometryType.typeName());
96+
Assertions.assertEquals("geometry(any)", geometryType.simpleString());
97+
}
98+
}

0 commit comments

Comments
 (0)