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