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

org.apache.baremaps.dem.ChaikinSmoother Maven / Gradle / Ivy

The newest version!
/*
 * 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 org.apache.baremaps.dem;

import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.geom.util.GeometryTransformer;

/**
 * A geometry transformer that applies the Chaikin smoothing algorithm to a geometry's coordinates.
 * This class extends the {@link GeometryTransformer} to provide a smoother version of the input
 * geometry by iteratively applying the Chaikin algorithm, which reduces sharp corners and refines
 * the shape of geometrical features.
 */
public class ChaikinSmoother extends GeometryTransformer {

  private final int iterations;

  private final double factor;

  /**
   * Constructs a {@code ChaikinSmoother} with the specified number of iterations and factor.
   *
   * @param iterations the number of iterations
   * @param factor the smoothing factor
   */
  public ChaikinSmoother(int iterations, double factor) {
    this.iterations = iterations;
    this.factor = factor;
  }

  /**
   * Transforms the coordinates of a geometry using the Chaikin smoothing algorithm.
   *
   * @param coordinateSequence the coordinates to transform
   * @param parent the parent geometry
   * @return the transformed coordinates
   */
  @Override
  protected CoordinateSequence transformCoordinates(
      CoordinateSequence coordinateSequence,
      Geometry parent) {
    return smooth(coordinateSequence, iterations, factor);
  }

  /**
   * Smooths the coordinates of a coordinate sequence using the Chaikin algorithm. This method
   * distinguishes between rings and lines and applies the algorithm accordingly.
   *
   * @param coordinateSequence the coordinates to smooth
   * @param iterations the number of iterations
   * @param factor the smoothing factor
   * @return the smoothed coordinates
   */
  public static CoordinateSequence smooth(
      CoordinateSequence coordinateSequence,
      int iterations,
      double factor) {
    if (CoordinateSequences.isRing(coordinateSequence)) {
      return new CoordinateArraySequence(chaikin(coordinateSequence.toCoordinateArray(), 2, 0.25));
    } else {
      Coordinate[] original = coordinateSequence.toCoordinateArray();
      Coordinate[] smoothed = chaikin(original, iterations, factor);
      int sumOfSquares = (iterations * (iterations + 1) * (2 * iterations + 1)) / 6;
      int trimmedLength = smoothed.length - sumOfSquares;
      Coordinate[] result = new Coordinate[trimmedLength + 2];
      result[0] = original[0];
      System.arraycopy(smoothed, 0, result, 1, trimmedLength);
      result[trimmedLength + 1] = original[original.length - 1];
      return new CoordinateArraySequence(result);
    }
  }

  /**
   * Applies the Chaikin smoothing algorithm to the specified coordinates.
   *
   * @param coordinates the coordinates to smooth
   * @param iterations the number of iterations
   * @param factor the smoothing factor
   * @return the smoothed coordinates
   */
  private static Coordinate[] chaikin(
      Coordinate[] coordinates,
      int iterations,
      double factor) {
    if (iterations <= 0) {
      return coordinates;
    }

    for (int i = 0; i < iterations; i++) {
      int l = coordinates.length;
      double f1 = 1 - factor;
      double f2 = factor;

      Coordinate[] smoothed = new Coordinate[l * 2];
      for (int j = 0; j < l; j++) {
        Coordinate c1 = coordinates[j];
        Coordinate c2 = coordinates[(j + 1) % l];
        smoothed[j * 2] = new Coordinate(
            f1 * c1.getX() + f2 * c2.getX(),
            f1 * c1.getY() + f2 * c2.getY());
        smoothed[j * 2 + 1] = new Coordinate(
            f2 * c1.getX() + f1 * c2.getX(),
            f2 * c1.getY() + f1 * c2.getY());
      }

      coordinates = smoothed;
    }

    return coordinates;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy