Parcel Boundary Implementation for Android Applications

Parcel Boundary Implementation for Android Applications

Parcel Boundary Implementation for Android Applications

Parcel boundaries are the precise GIS (Geographic Information System) coordinates of a piece of property.

Overlaid on top of a satellite or aerial image, they show a mapped image of the exact boundaries of a lot. They allow a potential real estate buyer to "walk" the boundaries of a piece of property from an online map, seeing its proximity to streets and highways, distance from neighboring homes, and access to green space, streams and wooded areas. Below diagram represents how parcel boundaries are plotted on the map.

boundaries

Parcel boundaries do more than just provide a "bird’s eye view" of a piece of property. Once the GIS coordinates of a property are loaded into a map-based platform, they can be combined with additional data sets like school districts, neighborhood demographics and other location information. Governments use parcel data to assess service areas, make predictions and respond to disasters.

What is GIS?

Geographic information system (GIS) is a system designed to capture, store, manipulate, analyze, manage, and present all types of geographical data.

Types of coordinate systems

The following are two common types of coordinate systems used in a geographic information system (GIS):

  1. Spherical coordinate system

  2. Projected coordinate system

Spherical coordinate system: A global or spherical coordinate system such as latitude-longitude. These are often referred to as geographic coordinate systems.

Projected coordinate system: A projected coordinate system such as universal transverse Mercator (UTM). Albers Equal Area, or Robinson, all of which (along with numerous other map projection models) provide various mechanisms to project maps of the earth’s spherical surface onto a two-dimensional Cartesian coordinate plane. Projected coordinate systems are referred to as map projections. Coordinate systems (both geographic and projected) provide a framework for defining real-world locations.

What is WMS?

A computer program that produces maps of spatially referenced data dynamically from geographic information. The OpenGIS® Web Map Service Interface Standard (WMS) provides a simple HTTP interface for requesting geo-registered map images from one or more distributed geospatial databases. A WMS request defines the geographic layer(s) and area of interest to be processed. The response to the request is one or more geo-registered map images (returned as JPEG, PNG, etc) that can be displayed in a browser application. The interface also supports the ability to specify whether the returned images should be transparent so that layers from multiple servers can be combined or not. WMS specifies a number of different request types, two of which are required by any WMS server:

  • GetCapabilities - returns parameters about the WMS (such as map image format and WMS version compatibility) and the available layers (map bounding box, coordinate reference systems, URI of the data and whether the layer is mostly opaque or not)

  • GetMap - returns a map image. Parameters include: width and height of the map, coordinate reference system, rendering style, image format

  • Request types that WMS providers may optionally support include:

  • GetFeatureInfo - if a layer is marked as 'queryable' then you can request data about a coordinate of the map image.

  • GetLegendGraphic - return an image of the map’s legend image, giving a visual guide to map elements.

Implementation in android devices Using Google Maps

  1. At each zoom level, the map is divided into tiles and only the tiles that overlap the screen are downloaded and rendered. Each tile is square and the map is divided into tiles as follows:

  2. At zoom level 0, one tile represents the entire world. The coordinates of that tile are (x, y) = (0, 0).

  3. At zoom level 1, the world is divided into 4 tiles arranged in a 2 x 2 grid.

  4. At zoom level N, the world is divided into 4N tiles arranged in a 2^N x 2^N grid."

We know the bounds of the entire map which is square. (Roughly -20037508m to 20037508m in both directions using Web Mercator)

The world is projected using the Mercator projection (see Wikipedia) with the left (west) side of the map corresponding to -180 degrees of longitude and the right (east) side of the map corresponding to 180 degrees of longitude. To make the map square, the top (north) side of the map corresponds to 85.0511 degrees of latitude and the bottom (south) side of the map corresponds to -85.0511 degrees of latitude. Areas outside this latitude range are not rendered.

Spherical Mercator EPSG:900913 (EPSG:3857) and WGS84 Datum

The coordinates you use in the Google Maps API and which are presented to the users is Latitude/Longitude in WGS84 Datum (when directly projected by Platte Carre then it is referenced as EPSG:4326). But for map publishing in the form compatible with all the popular interactive maps and especially for ground tile overlays you need to use Mercator map projection. Interactive web maps are using "Spherical Mercator" system which uses Mercator projection on the sphere instead of WGS84 ellipsoid. It is defined as EPSG:900913 or EPSG:3857 (deprecated EPSG:3785).

What is spatial reference system (SRS)?

A map projection is any method of representing the surface of a sphere or other shape on a plane. A coordinate system (also called a spatial reference system) is a means of assigning coordinates to a location and establishing relationships between sets of such coordinates. It enables the interpretation of a set of coordinates as a representation of a position in a real world space. European Petroleum Survey Group (EPSG) specifications and documentation typically use the term coordinate reference system.

Steps to implement Parcel Boundaries

  1. get the longitude and latitude values in degrees

  2. Calculate the x, y values for the Tile

  3. Calculate Top Left and Bottom Right Lat/Lang for that Tile for which we need to draw Boundaries

  4. Calculate Bounding Box Values (Map Area on which that Tile is covering)

  5. Pass the required values to your service

url
"http://yourApp.org/geoserver/wms?" +"service=WMS" + "&version=1.1.1&request=GetMap" +"&layers=yourLayer" +
"&bbox=%f,%f,%f,%f" +"&width=256" +"&height=256" +"&srs=EPSG:3857" +"&format=image/png&transparent=true";
WebMapService.java
package com.trvajjala.webmapservice;

import java.text.DecimalFormat;

import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.Projection;
/**
 *
 *
 * @author ThirupathiReddy Vajjala
 * @version <b>1.0</b>
 */
public class WebMapService {

            static final String tag = "WebMapService";

            private static final double MAP_SIZE = 20037508.34789244 * 2;
            private static final double[] TILE_ORIGIN = { -20037508.34789244,
                                    20037508.34789244 };
            private static final int ORIG_X = 0;
            private static final int ORIG_Y = 1;
            static final short TILE_SIZE = 256;

            private long tileX;
            private long tileY;
            private int zoom;

            double lat;
            double lon;

            public WebMapService(GeoPoint geoPoint, int zoom) {
                        this.lat = geoPoint.getLatitudeE6() / 1E6;
                        this.lon = geoPoint.getLongitudeE6() / 1E6;
                        this.zoom = zoom;
                        tileX = (int) Math.floor((lon + 180) / 360 * (1 << zoom));
                        tileY = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat))
                                                + 1 / Math.cos(Math.toRadians(lat)))
                                                / Math.PI)
                                                / 2 * (1 << zoom));
            }

            public void refreshTile(GeoPoint geoPoint) {
                        tileX = (int) Math.floor((lon + 180) / 360 * (1 << zoom));
                        tileY = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat))
                                                + 1 / Math.cos(Math.toRadians(lat)))
                                                / Math.PI)
                                                / 2 * (1 << zoom));
            }

            public RectF getBoundingBox() {

                        RectF rect = new RectF();

                        double tileSize = MAP_SIZE / Math.pow(2, zoom);
                        double minx = TILE_ORIGIN[ORIG_X] + tileX * tileSize;
                        double maxx = TILE_ORIGIN[ORIG_X] + (tileX + 1) * tileSize;
                        double miny = TILE_ORIGIN[ORIG_Y] - (tileY + 1) * tileSize;
                        double maxy = TILE_ORIGIN[ORIG_Y] - tileY * tileSize;
                        DecimalFormat f = new DecimalFormat("0.00000000");

                        rect.left = Float.parseFloat(f.format(minx));
                        rect.top = Float.parseFloat(f.format(miny));
                        rect.right = Float.parseFloat(f.format(maxx));
                        rect.bottom = Float.parseFloat(f.format(maxy));

                        return rect;
            }



            public Rect getBoundaryArea(Projection projection) {

                        Rect rect = new Rect();
                        double longitudeMin = tileXToLongitude(tileX);
                        double latitudeMin = tileYToLatitude(tileY);
                        double longitudeMax = tileXToLongitude(tileX + 1);
                        double latitudeMax = tileYToLatitude(tileY + 1);

                        Point topLeft = new Point();

                        Point bottomRight = new Point();

                        GeoPoint minGeo = new GeoPoint((int) (latitudeMin * 1E6),
                                                (int) (longitudeMin * 1E6));
                        GeoPoint maxGeo = new GeoPoint((int) (latitudeMax * 1E6),
                                                (int) (longitudeMax * 1E6));

                        projection.toPixels(minGeo, topLeft);
                        projection.toPixels(maxGeo, bottomRight);

                        rect.top = topLeft.y;
                        rect.left = topLeft.x;
                        rect.bottom = bottomRight.y;
                        rect.right = bottomRight.x;

                        return rect;
            }

            private double tileXToLongitude(long tileX) {
                        return pixelXToLongitude(tileX * TILE_SIZE);
            }

            private double pixelXToLongitude(double pixelX) {
                        return 360 * ((pixelX / ((long) TILE_SIZE << zoom)) - 0.5);
            }

            private double tileYToLatitude(long tileY) {
                        return pixelYToLatitude(tileY * TILE_SIZE);
            }

            private double pixelYToLatitude(double pixelY) {
                        double y = 0.5 - (pixelY / ((long) TILE_SIZE << zoom));
                        return 90 - 360 * Math.atan(Math.exp(-y * 2 * Math.PI)) / Math.PI;
            }

}

Call this above WebMapService from asyn Task as shown below

MapTask.java
class MapTask extends AsyncTask<String, Void, List<Overlay>> {

              @Override
              protected void onPreExecute() {
                     super.onPreExecute();
                     mapView.getOverlays().clear();
              }

              @Override
              protected List<Overlay> doInBackground(String... arg0) {
                     return plotPropertyBoundaries();

              }

              @Override
              protected void onPostExecute(List<Overlay> result) {
                     super.onPostExecute(result);

                     mapView.getOverlays().addAll(result);
                     mapView.invalidate();

              }

       }





public List<Overlay> plotPropertyBoundaries() {

              List<Overlay> overlayList = new ArrayList<Overlay>();

              try {
                     WebMapService bBox = new WebMapService(mapView.getMapCenter(),
                                  mapView.getZoomLevel() - 2);
                     RectF bbox = bBox.getBoundingBox();
                     Rect mapArea = bBox.getBoundaryArea(mapView.getProjection());
                     mapView.getOverlays().clear();
                     Bitmap bitmap = ApptUtil.processParcelBoundaryMap(bbox,
                                  getApplicationContext(), 512, 512);

                     ParcelOverlay parcelOverlay = new ParcelOverlay(mapArea, bitmap);

                     overlayList.add(parcelOverlay);

              } catch (Exception e) {
                     // e.printStackTrace();
              }

              return overlayList;
       }
 }

Draw returned Bitmap Image using overlay as show below

ParcelOverlay.java
public class ParcelOverlay extends Overlay {
              Rect mapArea;
              Bitmap bitmap;

              public ParcelOverlay(Rect mapArea, Bitmap bitmap) {

                     this.mapArea = mapArea;
                     this.bitmap = bitmap;

              }

              @Override
              public void draw(Canvas canvas, MapView mapView, boolean shadow) {
                     super.draw(canvas, mapView, shadow);
                     try {
                           canvas.drawBitmap(bitmap, null, mapArea, null);
                     } catch (Exception e) {

                     }
              }

       }
Note
if you want retrieve an image which covers more area on the device you need to reduce zoomLevel and increase width & height of the image size.

Comments

Popular posts from this blog

IBM Datapower GatewayScript

Spring boot Kafka Integration

Spring boot SOAP Web Service Performance