You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hivemall.apache.org by my...@apache.org on 2017/04/24 10:18:08 UTC
incubator-hivemall git commit: Close #73: [HIVEMALL-96] Add GEO
Spatial UDFs (tile, map_url)
Repository: incubator-hivemall
Updated Branches:
refs/heads/master 6e24d3a95 -> f538c891f
Close #73: [HIVEMALL-96] Add GEO Spatial UDFs (tile, map_url)
Project: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/commit/f538c891
Tree: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/tree/f538c891
Diff: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/diff/f538c891
Branch: refs/heads/master
Commit: f538c891fdbae83ec48768bcbf2ad9223a90e9ba
Parents: 6e24d3a
Author: myui <yu...@gmail.com>
Authored: Mon Apr 24 19:17:43 2017 +0900
Committer: myui <yu...@gmail.com>
Committed: Mon Apr 24 19:17:43 2017 +0900
----------------------------------------------------------------------
.../java/hivemall/geospatial/MapURLUDF.java | 160 +++++++++++++++++++
.../main/java/hivemall/geospatial/TileUDF.java | 102 ++++++++++++
.../utils/geospatial/GeoSpatialUtils.java | 67 ++++++++
.../java/hivemall/utils/math/MathUtils.java | 7 +
.../utils/geospatial/GeoSpatialUtilsTest.java | 50 ++++++
docs/gitbook/SUMMARY.md | 4 +
docs/gitbook/geospatial/latlon.md | 113 +++++++++++++
resources/ddl/define-all-as-permanent.hive | 10 ++
resources/ddl/define-all.hive | 10 ++
resources/ddl/define-all.spark | 10 ++
resources/ddl/define-udfs.td.hql | 2 +
11 files changed, 535 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/core/src/main/java/hivemall/geospatial/MapURLUDF.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/geospatial/MapURLUDF.java b/core/src/main/java/hivemall/geospatial/MapURLUDF.java
new file mode 100644
index 0000000..c2423e8
--- /dev/null
+++ b/core/src/main/java/hivemall/geospatial/MapURLUDF.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.geospatial;
+
+import hivemall.UDFWithOptions;
+import hivemall.utils.geospatial.GeoSpatialUtils;
+import hivemall.utils.hadoop.HiveUtils;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Options;
+import org.apache.hadoop.hive.ql.exec.Description;
+import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
+import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.udf.UDFType;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;
+import org.apache.hadoop.io.Text;
+
+@Description(
+ name = "map_url",
+ value = "_FUNC_(double lat, double lon, int zoom [, const string option]) - Returns a URL string",
+ extended = "OpenStreetMap: http://tile.openstreetmap.org/${zoom}/${xtile}/${ytile}.png\n"
+ + "Google Maps: https://www.google.com/maps/@${lat},${lon},${zoom}z")
+@UDFType(deterministic = true, stateful = false)
+public final class MapURLUDF extends UDFWithOptions {
+
+ private PrimitiveObjectInspector latOI;
+ private PrimitiveObjectInspector lonOI;
+ private PrimitiveObjectInspector zoomOI;
+
+ private MapType type = MapType.openstreetmap;
+ private Text result;
+
+ @Override
+ protected Options getOptions() {
+ Options opts = new Options();
+ opts.addOption("t", "type", true,
+ "Map type [default: openstreetmap|osm, googlemaps|google]");
+ return opts;
+ }
+
+ @Override
+ protected CommandLine processOptions(String optionValue) throws UDFArgumentException {
+ CommandLine cl = parseOptions(optionValue);
+
+ this.type = MapType.resolve(cl.getOptionValue("type", "openstreetmap"));
+ return cl;
+ }
+
+ public enum MapType {
+ openstreetmap, googlemaps;
+
+ @Nonnull
+ public static final MapType resolve(@Nonnull String type) throws UDFArgumentException {
+ if ("openstreetmap".equalsIgnoreCase(type) || "osm".equalsIgnoreCase(type)) {
+ return openstreetmap;
+ } else if ("googlemaps".equalsIgnoreCase(type) || "google".equalsIgnoreCase(type)) {
+ return googlemaps;
+ } else {
+ throw new UDFArgumentException("Illegal map type: " + type);
+ }
+ }
+ }
+
+ @Override
+ public ObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
+ if (argOIs.length != 3 && argOIs.length != 4) {
+ throw new UDFArgumentException("_FUNC_ takes 3 or 4 arguments: " + argOIs.length);
+ }
+ if (argOIs.length == 4) {
+ String opts = HiveUtils.getConstString(argOIs[3]);
+ processOptions(opts);
+ }
+
+ this.latOI = HiveUtils.asDoubleCompatibleOI(argOIs[0]);
+ this.lonOI = HiveUtils.asDoubleCompatibleOI(argOIs[1]);
+ this.zoomOI = HiveUtils.asIntegerOI(argOIs[2]);
+
+ this.result = new Text();
+ return PrimitiveObjectInspectorFactory.writableStringObjectInspector;
+ }
+
+ @Override
+ public Text evaluate(DeferredObject[] arguments) throws HiveException {
+ Object arg0 = arguments[0].get();
+ Object arg1 = arguments[1].get();
+ Object arg2 = arguments[2].get();
+
+ if (arg0 == null || arg1 == null) {
+ return null;
+ }
+ if (arg2 == null) {
+ throw new UDFArgumentException("zoom level is null");
+ }
+
+ double lat = PrimitiveObjectInspectorUtils.getDouble(arg0, latOI);
+ double lon = PrimitiveObjectInspectorUtils.getDouble(arg1, lonOI);
+ int zoom = PrimitiveObjectInspectorUtils.getInt(arg2, zoomOI);
+
+ result.set(toMapURL(lat, lon, zoom, type));
+ return result;
+ }
+
+ @Nonnull
+ private static String toMapURL(double lat, double lon, int zoom, @Nonnull MapType type)
+ throws UDFArgumentException {
+ if (type == MapType.openstreetmap) {// http://tile.openstreetmap.org/${zoom}/${xtile}/${ytile}.png
+ if (zoom < 0 || zoom > 19) {
+ throw new UDFArgumentException(
+ "Illegal zoom level. Supported zoom level for openstreetmap is [0,19]: " + zoom);
+ }
+ final int xtile, ytile;
+ try {
+ xtile = GeoSpatialUtils.lon2tile(lon, zoom);
+ ytile = GeoSpatialUtils.lat2tile(lat, zoom);
+ } catch (IllegalArgumentException ex) {
+ throw new UDFArgumentException(ex);
+ }
+ return "http://tile.openstreetmap.org/" + Integer.toString(zoom) + '/'
+ + Integer.toString(xtile) + '/' + Integer.toString(ytile) + ".png";
+ } else if (type == MapType.googlemaps) {// https://www.google.com/maps/@${lat},${lon},${zoom}z
+ if (zoom < 0 || zoom > 21) {
+ throw new UDFArgumentException(
+ "Illegal zoom level. Supported zoom level for Google Maps is [0,21]: " + zoom);
+ }
+ return "https://www.google.com/maps/@" + Double.toString(lat) + ','
+ + Double.toString(lon) + ',' + Integer.toString(zoom) + 'z';
+ } else {
+ throw new UDFArgumentException("Unexpected map type: " + type);
+ }
+ }
+
+ @Override
+ public String getDisplayString(String[] children) {
+ return "map_url(" + Arrays.toString(children) + ")";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/core/src/main/java/hivemall/geospatial/TileUDF.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/geospatial/TileUDF.java b/core/src/main/java/hivemall/geospatial/TileUDF.java
new file mode 100644
index 0000000..074b6a4
--- /dev/null
+++ b/core/src/main/java/hivemall/geospatial/TileUDF.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.geospatial;
+
+import hivemall.utils.geospatial.GeoSpatialUtils;
+import hivemall.utils.hadoop.HiveUtils;
+import hivemall.utils.lang.Preconditions;
+
+import java.util.Arrays;
+
+import org.apache.hadoop.hive.ql.exec.Description;
+import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
+import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.udf.UDFType;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;
+import org.apache.hadoop.io.IntWritable;
+
+/**
+ * @link http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
+ */
+@Description(
+ name = "tile",
+ value = "_FUNC_(double lat, double lon, int zoom)::INT - Returns a tile number 2^2n where n is zoom level.\n"
+ + "_FUNC_(lat,lon,zoom) = xtile(lon,zoom) + ytile(lat,zoom) * 2^n",
+ extended = "refer http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames for detail")
+@UDFType(deterministic = true, stateful = false)
+public final class TileUDF extends GenericUDF {
+
+ private PrimitiveObjectInspector latOI;
+ private PrimitiveObjectInspector lonOI;
+ private PrimitiveObjectInspector zoomOI;
+
+ private IntWritable result;
+
+ @Override
+ public ObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
+ if (argOIs.length != 3) {
+ throw new UDFArgumentException("_FUNC_ takes exactly 3 arguments: " + argOIs.length);
+ }
+ this.latOI = HiveUtils.asDoubleCompatibleOI(argOIs[0]);
+ this.lonOI = HiveUtils.asDoubleCompatibleOI(argOIs[1]);
+ this.zoomOI = HiveUtils.asIntegerOI(argOIs[2]);
+
+ this.result = new IntWritable();
+ return PrimitiveObjectInspectorFactory.writableIntObjectInspector;
+ }
+
+ @Override
+ public IntWritable evaluate(DeferredObject[] arguments) throws HiveException {
+ Object arg0 = arguments[0].get();
+ Object arg1 = arguments[1].get();
+ Object arg2 = arguments[2].get();
+
+ if (arg0 == null || arg1 == null) {
+ return null;
+ }
+ if (arg2 == null) {
+ throw new UDFArgumentException("zoom level is null");
+ }
+
+ double lat = PrimitiveObjectInspectorUtils.getDouble(arg0, latOI);
+ double lon = PrimitiveObjectInspectorUtils.getDouble(arg1, lonOI);
+ int zoom = PrimitiveObjectInspectorUtils.getInt(arg2, zoomOI);
+ Preconditions.checkArgument(zoom >= 0, "Invalid zoom level", UDFArgumentException.class);
+
+ final int tile;
+ try {
+ tile = GeoSpatialUtils.tile(lat, lon, zoom);
+ } catch (IllegalArgumentException ex) {
+ throw new UDFArgumentException(ex);
+ }
+
+ result.set(tile);
+ return result;
+ }
+
+ @Override
+ public String getDisplayString(String[] children) {
+ return "tile(" + Arrays.toString(children) + ")";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/core/src/main/java/hivemall/utils/geospatial/GeoSpatialUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/geospatial/GeoSpatialUtils.java b/core/src/main/java/hivemall/utils/geospatial/GeoSpatialUtils.java
new file mode 100644
index 0000000..1cc254e
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/geospatial/GeoSpatialUtils.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.geospatial;
+
+import static hivemall.utils.math.MathUtils.sec;
+import static java.lang.Math.PI;
+import static java.lang.Math.floor;
+import static java.lang.Math.log;
+import static java.lang.Math.tan;
+
+import javax.annotation.Nonnegative;
+
+public final class GeoSpatialUtils {
+
+ public static final double MAX_LATITUDE = 85.0511d;
+ public static final double MIN_LATITUDE = -85.0511d;
+
+ private GeoSpatialUtils() {}
+
+ public static int lon2tile(final double lon, @Nonnegative final int zoom) {
+ if (lon < -180.d || lon > 180.d) {
+ throw new IllegalArgumentException("Longitude must be in range [-180,+180]: " + lon);
+ }
+ return (int) floor((lon + 180.d) / 360.d * (1 << zoom));
+ }
+
+ public static int lat2tile(final double lat, @Nonnegative final int zoom) {
+ if (lat < MIN_LATITUDE || lat > MAX_LATITUDE) {
+ throw new IllegalArgumentException("Latitude must be in range [-85.0511,+85.0511]: "
+ + lat + "\nSee http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames");
+ }
+ double lat_rad = Math.toRadians(lat);
+ int n = 1 << zoom;
+ return (int) floor((1.d - log(tan(lat_rad) + sec(lat_rad)) / PI) / 2.d * n);
+ }
+
+ /**
+ * @link https://en.wikipedia.org/wiki/Tiled_web_map#Tile_numbering_schemes
+ */
+ public static int tile(final double lat, final double lon, @Nonnegative final int zoom) {
+ int xtile = lon2tile(lon, zoom);
+ int ytile = lat2tile(lat, zoom);
+ int n = 1 << zoom; // 2^z
+ return xtile + (n * ytile);
+ }
+
+ public static int tiles(final int zoom) {
+ return 1 << (zoom * 2); // 2^2z
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/core/src/main/java/hivemall/utils/math/MathUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/math/MathUtils.java b/core/src/main/java/hivemall/utils/math/MathUtils.java
index 9b46527..061b75d 100644
--- a/core/src/main/java/hivemall/utils/math/MathUtils.java
+++ b/core/src/main/java/hivemall/utils/math/MathUtils.java
@@ -46,6 +46,13 @@ public final class MathUtils {
private MathUtils() {}
+ /**
+ * @return secant 1 / cos(d)
+ */
+ public static double sec(final double d) {
+ return 1.d / Math.cos(d);
+ }
+
/**
* Returns a bit mask for the specified number of bits.
*/
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/core/src/test/java/hivemall/utils/geospatial/GeoSpatialUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/hivemall/utils/geospatial/GeoSpatialUtilsTest.java b/core/src/test/java/hivemall/utils/geospatial/GeoSpatialUtilsTest.java
new file mode 100644
index 0000000..6f551be
--- /dev/null
+++ b/core/src/test/java/hivemall/utils/geospatial/GeoSpatialUtilsTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.geospatial;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class GeoSpatialUtilsTest {
+
+ @Test
+ public void testTile() {
+ double[] lat_array = new double[] {GeoSpatialUtils.MIN_LATITUDE, 0.d,
+ GeoSpatialUtils.MAX_LATITUDE};
+ double[] lon_array = new double[] {-180.d, 0.d, 180.d};
+ for (double lat : lat_array) {
+ for (double lon : lon_array) {
+ Assert.assertTrue(
+ String.format("lat=%s, lon=%s, tile=%s", lat, lon,
+ GeoSpatialUtils.tile(lat, lon, 4)), GeoSpatialUtils.tile(lat, lon, 4) >= 0);
+ }
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTileInvalidLat1() {
+ GeoSpatialUtils.tile(GeoSpatialUtils.MIN_LATITUDE - 0.1, 0, 4);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTileInvalidLat2() {
+ GeoSpatialUtils.tile(GeoSpatialUtils.MAX_LATITUDE + 0.1, 0, 4);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/docs/gitbook/SUMMARY.md
----------------------------------------------------------------------
diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md
index 78b1faa..fe109ec 100644
--- a/docs/gitbook/SUMMARY.md
+++ b/docs/gitbook/SUMMARY.md
@@ -154,6 +154,10 @@
* [Latent Dirichlet Allocation](clustering/lda.md)
+## Part XI - GeoSpatial functions
+
+* [Lat/Lon functions](geospatial/latlon.md)
+
## Part XI - Hivemall on Spark
* [Getting Started](spark/getting_started/README.md)
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/docs/gitbook/geospatial/latlon.md
----------------------------------------------------------------------
diff --git a/docs/gitbook/geospatial/latlon.md b/docs/gitbook/geospatial/latlon.md
new file mode 100644
index 0000000..96ca11b
--- /dev/null
+++ b/docs/gitbook/geospatial/latlon.md
@@ -0,0 +1,113 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+This page introduces Geo-spatial functions that treats latitude and longitude.
+
+<!-- toc -->
+
+> #### Note
+> This feature is supported from Hivemall v0.5-rc.1 or later.
+
+# Tile number function
+
+`tile(double lat, double lon, int zoom)` returns a tile number in `xtile(lon,zoom) + ytile(lat,zoom) * 2^n`. The tile number is in range `[0,2^2z]`.
+
+Formulas to convert latitude and longitude into tile x,y coordinates are as follows:
+
+{% math %}
+\begin{aligned}
+x &= \left\lfloor \frac{lon + 180}{360} \cdot 2^z \right\rfloor \\ \\
+y &=
+ \left\lfloor
+ \left(
+ 1 - \frac{
+ \ln \left(
+ \tan \left(
+ lat \cdot \frac{\pi}{180}
+ \right) + \frac{1}{\cos \left( lat \cdot \frac{\pi}{180} \right)}
+ \right)
+ }{\pi}
+ \right) \cdot 2^{z - 1}
+ \right\rfloor
+\end{aligned}
+{% endmath %}
+
+Refer [this page](http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) for detail. Zoom level is well described in [this page](http://wiki.openstreetmap.org/wiki/Zoom_levels).
+
+### Usage
+
+```sql
+WITH data as (
+ select 51.51202 as lat, 0.02435 as lon, 17 as zoom
+ union all
+ select 51.51202 as lat, 0.02435 as lon, 4 as zoom
+ union all
+ select null as lat, 0.02435 as lon, 17 as zoom
+)
+select
+ tile(lat, lon, zoom) as tile
+from
+ data;
+```
+
+| tile |
+|:--:|
+|1417478152|
+|88|
+|NULL|
+
+# Map URL function
+
+`map_url(double lat, double lon, int zoom [, const string option])` function returns a tile URL of openstreetmap.com or maps.google.com.
+
+The 4th argument takes the following optional arguments:
+```sql
+hive> select map_url(1,1,1,'-help');
+
+usage: map_url(double lat, double lon, int zoom [, const string option]) -
+ Returns a URL string [-help] [-t <arg>]
+ -help Show function help
+ -t,--type <arg> Map type [default: openstreetmap|osm,
+ googlemaps|google]
+```
+
+### Usage
+
+```sql
+WITH data as (
+ select 51.51202 as lat, 0.02435 as lon, 17 as zoom
+ union all
+ select 51.51202 as lat, 0.02435 as lon, 4 as zoom
+ union all
+ select null, 0.02435, 17
+)
+select
+ map_url(lat,lon,zoom) as osm_url,
+ map_url(lat,lon,zoom,'-type googlemaps') as gmap_url
+from
+ data;
+```
+
+|osm_url|gmap_url|
+|:------:|:--------:|
+|http://tile.openstreetmap.org/17/65544/43582.png | https://www.google.com/maps/@51.51202,0.02435,17z|
+|http://tile.openstreetmap.org/4/8/5.png|https://www.google.com/maps/@51.51202,0.02435,4z|
+|NULL|NULL|
+
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/resources/ddl/define-all-as-permanent.hive
----------------------------------------------------------------------
diff --git a/resources/ddl/define-all-as-permanent.hive b/resources/ddl/define-all-as-permanent.hive
index 1eb9c82..435466d 100644
--- a/resources/ddl/define-all-as-permanent.hive
+++ b/resources/ddl/define-all-as-permanent.hive
@@ -626,6 +626,16 @@ CREATE FUNCTION train_lda as 'hivemall.topicmodel.LDAUDTF' USING JAR '${hivemall
DROP FUNCTION IF EXISTS lda_predict;
CREATE FUNCTION lda_predict as 'hivemall.topicmodel.LDAPredictUDAF' USING JAR '${hivemall_jar}';
+---------------------------
+-- Geo-Spatial functions --
+---------------------------
+
+DROP FUNCTION IF EXISTS tile;
+CREATE FUNCTION tile as 'hivemall.geospatial.TileUDF' USING JAR '${hivemall_jar}';
+
+DROP FUNCTION IF EXISTS map_url;
+CREATE FUNCTION map_url as 'hivemall.geospatial.MapURLUDF' USING JAR '${hivemall_jar}';
+
----------------------------
-- Smile related features --
----------------------------
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/resources/ddl/define-all.hive
----------------------------------------------------------------------
diff --git a/resources/ddl/define-all.hive b/resources/ddl/define-all.hive
index b503546..8982ef4 100644
--- a/resources/ddl/define-all.hive
+++ b/resources/ddl/define-all.hive
@@ -622,6 +622,16 @@ create temporary function train_lda as 'hivemall.topicmodel.LDAUDTF';
drop temporary function if exists lda_predict;
create temporary function lda_predict as 'hivemall.topicmodel.LDAPredictUDAF';
+---------------------------
+-- Geo-Spatial functions --
+---------------------------
+
+drop temporary function if exists tile;
+create temporary function tile as 'hivemall.geospatial.TileUDF';
+
+drop temporary function if exists map_url;
+create temporary function map_url as 'hivemall.geospatial.MapURLUDF';
+
----------------------------
-- Smile related features --
----------------------------
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/resources/ddl/define-all.spark
----------------------------------------------------------------------
diff --git a/resources/ddl/define-all.spark b/resources/ddl/define-all.spark
index b5239cf..a6473db 100644
--- a/resources/ddl/define-all.spark
+++ b/resources/ddl/define-all.spark
@@ -607,6 +607,16 @@ sqlContext.sql("DROP TEMPORARY FUNCTION IF EXISTS lda_predict")
sqlContext.sql("CREATE TEMPORARY FUNCTION lda_predict AS 'hivemall.topicmodel.LDAPredictUDAF'")
/**
+ * Geo Spatial Functions
+ */
+
+sqlContext.sql("DROP TEMPORARY FUNCTION IF EXISTS tile")
+sqlContext.sql("CREATE TEMPORARY FUNCTION tile AS 'hivemall.geospatial.TileUDF'")
+
+sqlContext.sql("DROP TEMPORARY FUNCTION IF EXISTS map_url")
+sqlContext.sql("CREATE TEMPORARY FUNCTION map_url AS 'hivemall.geospatial.MapURLUDF'")
+
+/**
* Smile related features
*/
http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/f538c891/resources/ddl/define-udfs.td.hql
----------------------------------------------------------------------
diff --git a/resources/ddl/define-udfs.td.hql b/resources/ddl/define-udfs.td.hql
index 28d17ff..a2e5838 100644
--- a/resources/ddl/define-udfs.td.hql
+++ b/resources/ddl/define-udfs.td.hql
@@ -160,6 +160,8 @@ create temporary function changefinder as 'hivemall.anomaly.ChangeFinderUDF';
create temporary function sst as 'hivemall.anomaly.SingularSpectrumTransformUDF';
create temporary function train_lda as 'hivemall.topicmodel.LDAUDTF';
create temporary function lda_predict as 'hivemall.topicmodel.LDAPredictUDAF';
+create temporary function tile as 'hivemall.geospatial.TileUDF';
+create temporary function map_url as 'hivemall.geospatial.MapURLUDF';
-- NLP features
create temporary function tokenize_ja as 'hivemall.nlp.tokenizer.KuromojiUDF';