1dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond/* 2dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Licensed to the Apache Software Foundation (ASF) under one or more 3dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * contributor license agreements. See the NOTICE file distributed with 4dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * this work for additional information regarding copyright ownership. 5dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * The ASF licenses this file to You under the Apache License, Version 2.0 6dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * (the "License"); you may not use this file except in compliance with 7dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * the License. You may obtain a copy of the License at 8dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 9dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * http://www.apache.org/licenses/LICENSE-2.0 10dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 11dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Unless required by applicable law or agreed to in writing, software 12dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * distributed under the License is distributed on an "AS IS" BASIS, 13dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * See the License for the specific language governing permissions and 15dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * limitations under the License. 16dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 17dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 18dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondpackage org.apache.commons.math.optimization.fitting; 19dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 20dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport java.util.Arrays; 21dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport java.util.Comparator; 22dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 23dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.exception.util.LocalizedFormats; 24dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.exception.NumberIsTooSmallException; 25dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.exception.OutOfRangeException; 26dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.exception.ZeroException; 27dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.exception.NullArgumentException; 28dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 29dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond/** 30dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Guesses the parameters ({@code a}, {@code b}, {@code c}, and {@code d}) 31dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * of a {@link ParametricGaussianFunction} based on the specified observed 32dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * points. 33dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 34dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @since 2.2 35dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $ 36dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 37dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondpublic class GaussianParametersGuesser { 38dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 39dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** Observed points. */ 40dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private final WeightedObservedPoint[] observations; 41dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 42dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** Resulting guessed parameters. */ 43dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private double[] parameters; 44dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 45dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 46dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Constructs instance with the specified observed points. 47dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 48dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param observations observed points upon which should base guess 49dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 50dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond public GaussianParametersGuesser(WeightedObservedPoint[] observations) { 51dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (observations == null) { 52dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY); 53dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 54dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (observations.length < 3) { 55dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond throw new NumberIsTooSmallException(observations.length, 3, true); 56dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 57dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond this.observations = observations.clone(); 58dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 59dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 60dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 61dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Guesses the parameters based on the observed points. 62dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 63dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return guessed parameters array <code>{a, b, c, d}</code> 64dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 65dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond public double[] guess() { 66dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (parameters == null) { 67dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond parameters = basicGuess(observations); 68dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 69dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return parameters.clone(); 70dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 71dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 72dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 73dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Guesses the parameters based on the specified observed points. 74dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 75dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param points observed points upon which should base guess 76dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 77dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return guessed parameters array <code>{a, b, c, d}</code> 78dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 79dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private double[] basicGuess(WeightedObservedPoint[] points) { 80dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond Arrays.sort(points, createWeightedObservedPointComparator()); 81dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double[] params = new double[4]; 82dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 83dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond int minYIdx = findMinY(points); 84dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond params[0] = points[minYIdx].getY(); 85dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 86dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond int maxYIdx = findMaxY(points); 87dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond params[1] = points[maxYIdx].getY(); 88dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond params[2] = points[maxYIdx].getX(); 89dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 90dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double fwhmApprox; 91dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond try { 92dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double halfY = params[0] + ((params[1] - params[0]) / 2.0); 93dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double fwhmX1 = interpolateXAtY(points, maxYIdx, -1, halfY); 94dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double fwhmX2 = interpolateXAtY(points, maxYIdx, +1, halfY); 95dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond fwhmApprox = fwhmX2 - fwhmX1; 96dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } catch (OutOfRangeException e) { 97dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond fwhmApprox = points[points.length - 1].getX() - points[0].getX(); 98dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 99dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond params[3] = fwhmApprox / (2.0 * Math.sqrt(2.0 * Math.log(2.0))); 100dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 101dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return params; 102dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 103dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 104dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 105dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Finds index of point in specified points with the smallest Y. 106dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 107dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param points points to search 108dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 109dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return index in specified points array 110dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 111dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private int findMinY(WeightedObservedPoint[] points) { 112dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond int minYIdx = 0; 113dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond for (int i = 1; i < points.length; i++) { 114dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (points[i].getY() < points[minYIdx].getY()) { 115dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond minYIdx = i; 116dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 117dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 118dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return minYIdx; 119dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 120dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 121dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 122dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Finds index of point in specified points with the largest Y. 123dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 124dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param points points to search 125dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 126dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return index in specified points array 127dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 128dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private int findMaxY(WeightedObservedPoint[] points) { 129dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond int maxYIdx = 0; 130dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond for (int i = 1; i < points.length; i++) { 131dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (points[i].getY() > points[maxYIdx].getY()) { 132dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond maxYIdx = i; 133dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 134dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 135dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return maxYIdx; 136dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 137dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 138dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 139dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Interpolates using the specified points to determine X at the specified 140dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Y. 141dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 142dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param points points to use for interpolation 143dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param startIdx index within points from which to start search for 144dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * interpolation bounds points 145dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param idxStep index step for search for interpolation bounds points 146dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param y Y value for which X should be determined 147dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 148dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return value of X at the specified Y 149dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 150dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @throws IllegalArgumentException if idxStep is 0 151dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @throws OutOfRangeException if specified <code>y</code> is not within the 152dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * range of the specified <code>points</code> 153dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 154dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private double interpolateXAtY(WeightedObservedPoint[] points, 155dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond int startIdx, int idxStep, double y) throws OutOfRangeException { 156dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (idxStep == 0) { 157dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond throw new ZeroException(); 158dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 159dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond WeightedObservedPoint[] twoPoints = getInterpolationPointsForY(points, startIdx, idxStep, y); 160dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond WeightedObservedPoint pointA = twoPoints[0]; 161dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond WeightedObservedPoint pointB = twoPoints[1]; 162dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (pointA.getY() == y) { 163dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return pointA.getX(); 164dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 165dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (pointB.getY() == y) { 166dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return pointB.getX(); 167dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 168dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return pointA.getX() + 169dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond (((y - pointA.getY()) * (pointB.getX() - pointA.getX())) / (pointB.getY() - pointA.getY())); 170dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 171dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 172dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 173dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Gets the two bounding interpolation points from the specified points 174dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * suitable for determining X at the specified Y. 175dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 176dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param points points to use for interpolation 177dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param startIdx index within points from which to start search for 178dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * interpolation bounds points 179dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param idxStep index step for search for interpolation bounds points 180dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param y Y value for which X should be determined 181dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 182dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return array containing two points suitable for determining X at the 183dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * specified Y 184dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 185dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @throws IllegalArgumentException if idxStep is 0 186dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @throws OutOfRangeException if specified <code>y</code> is not within the 187dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * range of the specified <code>points</code> 188dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 189dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private WeightedObservedPoint[] getInterpolationPointsForY(WeightedObservedPoint[] points, 190dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond int startIdx, int idxStep, double y) 191dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond throws OutOfRangeException { 192dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (idxStep == 0) { 193dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond throw new ZeroException(); 194dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 195dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond for (int i = startIdx; 196dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond (idxStep < 0) ? (i + idxStep >= 0) : (i + idxStep < points.length); 197dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond i += idxStep) { 198dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (isBetween(y, points[i].getY(), points[i + idxStep].getY())) { 199dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return (idxStep < 0) ? 200dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond new WeightedObservedPoint[] { points[i + idxStep], points[i] } : 201dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond new WeightedObservedPoint[] { points[i], points[i + idxStep] }; 202dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 203dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 204dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 205dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double minY = Double.POSITIVE_INFINITY; 206dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond double maxY = Double.NEGATIVE_INFINITY; 207dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond for (final WeightedObservedPoint point : points) { 208dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond minY = Math.min(minY, point.getY()); 209dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond maxY = Math.max(maxY, point.getY()); 210dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 211dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond throw new OutOfRangeException(y, minY, maxY); 212dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 213dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 214dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 215dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 216dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Determines whether a value is between two other values. 217dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 218dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param value value to determine whether is between <code>boundary1</code> 219dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * and <code>boundary2</code> 220dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param boundary1 one end of the range 221dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @param boundary2 other end of the range 222dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 223dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return true if <code>value</code> is between <code>boundary1</code> and 224dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * <code>boundary2</code> (inclusive); false otherwise 225dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 226dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private boolean isBetween(double value, double boundary1, double boundary2) { 227dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return (value >= boundary1 && value <= boundary2) || 228dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond (value >= boundary2 && value <= boundary1); 229dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 230dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond 231dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond /** 232dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Factory method creating <code>Comparator</code> for comparing 233dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * <code>WeightedObservedPoint</code> instances. 234dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * 235dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @return new <code>Comparator</code> instance 236dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */ 237dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond private Comparator<WeightedObservedPoint> createWeightedObservedPointComparator() { 238dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return new Comparator<WeightedObservedPoint>() { 239dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond public int compare(WeightedObservedPoint p1, WeightedObservedPoint p2) { 240dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1 == null && p2 == null) { 241dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return 0; 242dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 243dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1 == null) { 244dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return -1; 245dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 246dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p2 == null) { 247dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return 1; 248dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 249dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1.getX() < p2.getX()) { 250dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return -1; 251dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 252dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1.getX() > p2.getX()) { 253dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return 1; 254dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 255dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1.getY() < p2.getY()) { 256dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return -1; 257dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 258dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1.getY() > p2.getY()) { 259dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return 1; 260dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 261dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1.getWeight() < p2.getWeight()) { 262dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return -1; 263dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 264dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond if (p1.getWeight() > p2.getWeight()) { 265dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return 1; 266dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 267dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond return 0; 268dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 269dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond }; 270dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond } 271dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond} 272