All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.numenta.nupic.encoders.GeospatialCoordinateEncoder Maven / Gradle / Ivy

/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2014, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

package org.numenta.nupic.encoders;

import org.numenta.nupic.util.Tuple;

public class GeospatialCoordinateEncoder extends CoordinateEncoder {
	private static final long serialVersionUID = 1L;
    
	private int scale;
	private int timestep;
	
	
	public GeospatialCoordinateEncoder() {
        Tuple desc = new Tuple("longitude", 0);
        Tuple desc2 = new Tuple("lattitude", 1);
        Tuple desc3 = new Tuple("speed", 2);
        description.add(desc);
        description.add(desc2);
        description.add(desc3);
	}
	
	/**
	 * Returns a builder for building ScalarEncoders. 
	 * This builder may be reused to produce multiple builders
	 * 
	 * @return a {@code CoordinateEncoder.Builder}
	 */
	public static GeospatialCoordinateEncoder.Builder geobuilder() {
		return new GeospatialCoordinateEncoder.Builder();
	}

	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void encodeIntoArray(Tuple inputData, int[] output) {
		double longitude = (double)inputData.get(0);
		double lattitude = (double)inputData.get(1);
		double speed = (double)inputData.get(2);
		int[] coordinate = coordinateForPosition(longitude, lattitude);
		double radius = radiusForSpeed(speed);
		
		super.encodeIntoArray(new Tuple(coordinate, radius), output);
	}
	
	public int[] coordinateForPosition(double longitude, double lattitude) {
		double[] coordinate = toMercator(longitude, lattitude);
		coordinate[0] /= scale;
		coordinate[1] /= scale;
		return new int[] { (int)coordinate[0], (int)coordinate[1] };
	}
	
	/**
	 * Returns coordinates converted to Mercator Spherical projection
	 * 
	 * @param lon	the longitude
	 * @param lat	the lattitude
	 * @return
	 */
	protected double[] toMercator(double lon, double lat) {
		double x = lon * 20037508.34d / 180;
		double y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
		y = y * 20037508.34d / 180;
		
		return new double[] { x, y};
	}
	
	/**
	 * Returns coordinates converted to Long/Lat from Mercator Spherical projection
	 * 
	 * @param lon	the longitude
	 * @param lat	the lattitude
	 * @return
	 */
	protected double[] inverseMercator(double x, double y) {
		double lon = (x / 20037508.34d) * 180;
		double lat = (y / 20037508.34d) * 180;
		
		lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
		
		return new double[] { lon, lat };
	}
	
	/**
	 * Tries to get the encodings of consecutive readings to be
     * adjacent with some overlap.
     * 
	 * @param speed	Speed (in meters per second)
	 * @return	Radius for given speed
	 */
	public double radiusForSpeed(double speed) {
		double overlap = 1.5;
		double coordinatesPerTimestep = speed * timestep / scale;
		int radius = (int)Math.round(coordinatesPerTimestep / 2D * overlap); 
		int minRadius = (int)Math.ceil((Math.sqrt(w) - 1) / 2);
		return Math.max(radius, minRadius);
	}
	
	/**
	 * Returns a {@link EncoderBuilder} for constructing {@link GeospatialCoordinateEncoder}s
	 * 
	 * The base class architecture is put together in such a way where boilerplate
	 * initialization can be kept to a minimum for implementing subclasses, while avoiding
	 * the mistake-proneness of extremely long argument lists.
	 * 
	 * @see ScalarEncoder.Builder#setStuff(int)
	 */
	public static class Builder extends Encoder.Builder {
		private int scale;
		private int timestep;
		
		private Builder() {}

		@Override
		public GeospatialCoordinateEncoder build() {
			//Must be instantiated so that super class can initialize 
			//boilerplate variables.
			encoder = new GeospatialCoordinateEncoder();
			
			//Call super class here
			super.build();
			
			////////////////////////////////////////////////////////
			//  Implementing classes would do setting of specific //
			//  vars here together with any sanity checking       //
			////////////////////////////////////////////////////////
			if(scale == 0 || timestep == 0) {
				throw new IllegalStateException("Scale or Timestep not set");
			}
			
			((GeospatialCoordinateEncoder)encoder).scale = scale;
			((GeospatialCoordinateEncoder)encoder).timestep = timestep;
			
			if(w <= 0 || w % 2 == 0) {
				throw new IllegalArgumentException("w must be odd, and must be a positive integer");
			}
			
			if(n <= 6 * w) {
				throw new IllegalArgumentException(
					"n must be an int strictly greater than 6*w. For " +
                       "good results we recommend n be strictly greater than 11*w");
			}
			
			if(name == null || name.equals("None")) {
				name = new StringBuilder("[").append(n).append(":").append(w).append("]").toString();
			}
			
			return (GeospatialCoordinateEncoder)encoder;
		}
		
		/**
		 * Scale of the map, as measured by
         * distance between two coordinates
         * (in meters per dimensional unit)
		 * @param scale
		 * @return
		 */
		public Builder scale(int scale) {
			this.scale = scale;
			return this;
		}
		
		/**
		 * Time between readings
		 * @param timestep
		 * @return
		 */
		public Builder timestep(int timestep) {
			this.timestep = timestep;
			return this;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy