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

edu.ucr.cs.bdlab.beast.generator.SpatialGenerator.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 University of California, Riverside
 *
 * 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 edu.ucr.cs.bdlab.beast.generator

import edu.ucr.cs.bdlab.beast.common.BeastOptions

import java.awt.geom.AffineTransform
import edu.ucr.cs.bdlab.beast.geolite.{EnvelopeND, Feature, IFeature, PointND}
import edu.ucr.cs.bdlab.beast.io.FeatureReader
import org.locationtech.jts.geom.{CoordinateSequence, Geometry, GeometryFactory, Polygon}

import scala.util.Random

abstract class SpatialGenerator(partition: RandomSpatialPartition)
  extends Iterator[IFeature] {

  /**The random number generator to use with this partition*/
  val random: Random = new Random(partition.seed)

  /**Generate a random value {0, 1} from a bernoulli distribution with parameter p*/
  def bernoulli(p: Double): Int = if (random.nextDouble() < p) 1 else 0

  /**Generate a random value in the range [a, b) from a uniform distribution*/
  def uniform(a: Double, b: Double): Double = (b - a) * random.nextDouble() + a

  /**Generate a random number in the range (-inf, +inf) from a normal distribution*/
  def normal(mu: Double, sigma: Double): Double =
    mu + sigma * Math.sqrt(-2 * Math.log(random.nextDouble())) * Math.sin(2 * Math.PI * random.nextDouble())

  /**Generate a random integer in the range [1, n]*/
  def dice(n: Int): Int = uniform(0, n).toInt + 1

  /**Generate a random integer in the given range*/
  def dice(range: Range): Int = random.nextInt(range.size) + range.min

  /**
   * The geometry factory used to generate the geometries
   */
  val geometryFactory: GeometryFactory = FeatureReader.DefaultGeometryFactory

  val affineMatrix: Array[Double] = {
    val matrixStr: String = partition.opts.getString(SpatialGenerator.AffineMatrix)
    if (matrixStr == null)
      null
    else {
      val matrixComponents = matrixStr.split(",").map(_.toDouble)
      assert(matrixComponents.length == partition.dimensions * (partition.dimensions + 1))
      matrixComponents
    }
  }

  /**
   * Generates and returns the next geometry without applying the affine transformation
   * @return the generated geometry
   */
  def nextGeometry: Geometry

  /**
   * Generates the next feature and applies the affine transformation to it
   * @return a feature that wraps the generated geometry
   */
  def next: IFeature = {
    var geometry: Geometry = nextGeometry
    assert(geometry != null)
    if (affineMatrix != null)
      geometry = affineTransform(geometry)
    Feature.create(null, geometry)
  }

  /**
   * Transform the given geometry using the affine transformation of this generator
   * and returns the transformed geometry
   * @param geometry the geometry to transform
   * @return the transformed geometry
   */
  def affineTransform(geometry: Geometry): Geometry = geometry match {
    case p: PointND =>
      val tp: PointND = new PointND(p.getFactory, partition.dimensions)
      val tindex: Int = partition.dimensions  * partition.dimensions
      for (d <- 0 until partition.dimensions) {
        var v = affineMatrix(tindex + d)
        for (k <- 0 until partition.dimensions) {
          v += affineMatrix((k * partition.dimensions) + d) * p.getCoordinate(k)
        }
        tp.setCoordinate(d, v)
      }
      tp
    case e: EnvelopeND =>
      val te: EnvelopeND = new EnvelopeND(e.getFactory, partition.dimensions)
      val tindex: Int = partition.dimensions * partition.dimensions
      for (d <- 0 until partition.dimensions) {
        var v1 = affineMatrix(tindex + d)
        var v2 = v1
        for (k <- 0 until partition.dimensions) {
          v1 += affineMatrix((k * partition.dimensions) + d) * e.getMinCoord(k)
          v2 += affineMatrix((k * partition.dimensions) + d) * e.getMaxCoord(k)
        }
        te.setMinCoord(d, v1 min v2)
        te.setMaxCoord(d, v1 max v2)
      }
      te
    case p: Polygon =>
      val cs: CoordinateSequence = p.getExteriorRing.getCoordinateSequence
      for (i <- 0 until cs.size()) {
        val transformedX: Double = affineMatrix(0) * cs.getX(i) + affineMatrix(2) * cs.getY(i) + affineMatrix(4)
        val transformedY: Double = affineMatrix(1) * cs.getX(i) + affineMatrix(3) * cs.getY(i) + affineMatrix(5)
        cs.setOrdinate(i, 0, transformedX)
        cs.setOrdinate(i, 1, transformedY)
      }
      p.getFactory.createPolygon(cs)
  }
}

object SpatialGenerator {
  /**Number of records per partition*/
  val RecordsPerPartition = "recordsPerPartition"

  /**The seed for the random number generator. If left blank, current time is used*/
  val Seed = "seed"

  /**The number of dimensions of generated geometries*/
  val Dimensions = "dimensions"

  /**
   * The affine matrix represented as comma-separated floating-point values.
   * The order of the matrix components is [m00, m10, m01, m11, m02, m12] where:
   *  - m00 (sx) the X coordinate scaling element of the 3x3 matrix
   *  - m10 (ry) the Y coordinate shearing element of the 3x3 matrix
   *  - m01 (rx) the X coordinate shearing element of the 3x3 matrix
   *  - m11 (sy) the Y coordinate scaling element of the 3x3 matrix
   *  - m02 (tx) the X coordinate translation element of the 3x3 matrix
   *  - m12 (ty) the Y coordinate translation element of the 3x3 matrix
   * See [[AffineTransform]]
   */
  val AffineMatrix = "affineMatrix"

  def setAffineTransform(opts: BeastOptions, at: AffineTransform): BeastOptions = {
    opts.set(AffineMatrix, Seq(at.getScaleX, at.getShearY,
      at.getShearX, at.getScaleY,
      at.getTranslateX, at.getTranslateY).mkString(","))
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy