/* * Copyright 2018 data Artisans GmbH * * Licensed 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 com.dataartisans.examples.infoworld.utils; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.typeutils.TupleTypeInfo; import org.apache.flink.table.functions.ScalarFunction; /** * GeoUtils provides utility methods to deal with locations in New York City. */ public class GeoUtils { // geo boundaries of the area of NYC private static double LON_EAST = -73.7; private static double LON_WEST = -74.05; private static double LAT_NORTH = 41.0; private static double LAT_SOUTH = 40.5; // delta step to create artificial grid overlay of NYC private static double DELTA_LON = 0.0014; private static double DELTA_LAT = 0.00125; // ( |LON_WEST| - |LON_EAST| ) / DELTA_LAT private static int NUMBER_OF_GRID_X = 250; // ( LAT_NORTH - LAT_SOUTH ) / DELTA_LON private static int NUMBER_OF_GRID_Y = 400; /** * Checks if a location specified by longitude and latitude values is * within the geo boundaries of New York City. * * @param lon longitude of the location to check * @param lat latitude of the location to check * * @return true if the location is within NYC boundaries, otherwise false. */ public static boolean isInNYC(float lon, float lat) { return !(lon > LON_EAST || lon < LON_WEST) && !(lat > LAT_NORTH || lat < LAT_SOUTH); } /** * Maps a location specified by latitude and longitude values to a cell of a * grid covering the area of NYC. * The grid cells are roughly 100 x 100 m and sequentially number from north-west * to south-east starting by zero. * * @param lon longitude of the location to map * @param lat latitude of the location to map * * @return id of mapped grid cell. */ public static int mapToGridCell(float lon, float lat) { int xIndex = (int) Math.floor((Math.abs(LON_WEST) - Math.abs(lon)) / DELTA_LON); int yIndex = (int) Math.floor((LAT_NORTH - lat) / DELTA_LAT); return xIndex + (yIndex * NUMBER_OF_GRID_X); } /** * Returns the longitude of the center of a grid cell. * * @param gridCellId The grid cell. * * @return The longitude value of the cell's center. */ public static float getGridCellCenterLon(int gridCellId) { int xIndex = gridCellId % NUMBER_OF_GRID_X; return (float) (Math.abs(LON_WEST) - (xIndex * DELTA_LON) - (DELTA_LON / 2)) * -1.0f; } /** * Returns the latitude of the center of a grid cell. * * @param gridCellId The grid cell. * * @return The latitude value of the cell's center. */ public static float getGridCellCenterLat(int gridCellId) { int xIndex = gridCellId % NUMBER_OF_GRID_X; int yIndex = (gridCellId - xIndex) / NUMBER_OF_GRID_X; return (float) (LAT_NORTH - (yIndex * DELTA_LAT) - (DELTA_LAT / 2)); } /** * Table API / SQL Scalar UDF to check if a coordinate is in NYC. */ public static class IsInNYC extends ScalarFunction { public boolean eval(float lon, float lat) { return isInNYC(lon, lat); } } /** * Table API / SQL Scalar UDF to convert a lon/lat pair into a cell ID. */ public static class ToCellId extends ScalarFunction { public int eval(float lon, float lat) { return GeoUtils.mapToGridCell(lon, lat); } } /** * Table API / SQL Scalar UDF to convert a cell ID into a lon/lat pair. */ public static class ToCoords extends ScalarFunction { public Tuple2<Float, Float> eval(int cellId) { return Tuple2.of( GeoUtils.getGridCellCenterLon(cellId), GeoUtils.getGridCellCenterLat(cellId) ); } @Override public TypeInformation getResultType(Class[] signature) { return new TupleTypeInfo<>(Types.FLOAT, Types.FLOAT); } } }