~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to solr/core/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package org.apache.solr.search.function.distance;
2
 
/**
3
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
4
 
 * contributor license agreements.  See the NOTICE file distributed with
5
 
 * this work for additional information regarding copyright ownership.
6
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
7
 
 * (the "License"); you may not use this file except in compliance with
8
 
 * the License.  You may obtain a copy of the License at
9
 
 *
10
 
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 
 *
12
 
 * Unless required by applicable law or agreed to in writing, software
13
 
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 
 * See the License for the specific language governing permissions and
16
 
 * limitations under the License.
17
 
 */
18
 
 
19
 
import org.apache.lucene.index.IndexReader;
20
 
import org.apache.lucene.queryParser.ParseException;
21
 
import org.apache.lucene.search.Searcher;
22
 
import org.apache.lucene.spatial.DistanceUtils;
23
 
import org.apache.lucene.spatial.tier.InvalidGeoException;
24
 
import org.apache.solr.common.params.SpatialParams;
25
 
import org.apache.solr.schema.SchemaField;
26
 
import org.apache.solr.search.FunctionQParser;
27
 
import org.apache.solr.search.ValueSourceParser;
28
 
import org.apache.solr.search.function.*;
29
 
 
30
 
import java.io.IOException;
31
 
import java.util.Arrays;
32
 
import java.util.List;
33
 
import java.util.Map;
34
 
 
35
 
 
36
 
/**
37
 
 * Haversine function with one point constant
38
 
 */
39
 
public class HaversineConstFunction extends ValueSource {
40
 
 
41
 
  public static ValueSourceParser parser = new ValueSourceParser() {
42
 
    @Override
43
 
    public ValueSource parse(FunctionQParser fp) throws ParseException
44
 
    {
45
 
      // TODO: dispatch through SpatialQueriable in the future?
46
 
      List<ValueSource> sources = fp.parseValueSourceList();
47
 
 
48
 
      // "m" is a multi-value source, "x" is a single-value source
49
 
      // allow (m,m) (m,x,x) (x,x,m) (x,x,x,x)
50
 
      // if not enough points are present, "pt" will be checked first, followed by "sfield".      
51
 
 
52
 
      MultiValueSource mv1 = null;
53
 
      MultiValueSource mv2 = null;
54
 
 
55
 
      if (sources.size() == 0) {
56
 
        // nothing to do now
57
 
      } else if (sources.size() == 1) {
58
 
        ValueSource vs = sources.get(0);
59
 
        if (!(vs instanceof MultiValueSource)) {
60
 
          throw new ParseException("geodist - invalid parameters:" + sources);
61
 
        }
62
 
        mv1 = (MultiValueSource)vs;
63
 
      } else if (sources.size() == 2) {
64
 
        ValueSource vs1 = sources.get(0);
65
 
        ValueSource vs2 = sources.get(1);
66
 
 
67
 
        if (vs1 instanceof MultiValueSource && vs2 instanceof MultiValueSource) {
68
 
          mv1 = (MultiValueSource)vs1;
69
 
          mv2 = (MultiValueSource)vs2;
70
 
        } else {
71
 
          mv1 = makeMV(sources, sources);
72
 
        }
73
 
      } else if (sources.size()==3) {
74
 
        ValueSource vs1 = sources.get(0);
75
 
        ValueSource vs2 = sources.get(1);
76
 
        if (vs1 instanceof MultiValueSource) {     // (m,x,x)
77
 
          mv1 = (MultiValueSource)vs1;
78
 
          mv2 = makeMV(sources.subList(1,3), sources);
79
 
        } else {                                   // (x,x,m)
80
 
          mv1 = makeMV(sources.subList(0,2), sources);
81
 
          vs1 = sources.get(2);
82
 
          if (!(vs1 instanceof MultiValueSource)) {
83
 
            throw new ParseException("geodist - invalid parameters:" + sources);
84
 
          }
85
 
          mv2 = (MultiValueSource)vs1;
86
 
        }
87
 
      } else if (sources.size()==4) {
88
 
        mv1 = makeMV(sources.subList(0,2), sources);
89
 
        mv2 = makeMV(sources.subList(2,4), sources);
90
 
      } else if (sources.size() > 4) {
91
 
        throw new ParseException("geodist - invalid parameters:" + sources);
92
 
      }
93
 
 
94
 
      if (mv1 == null) {
95
 
        mv1 = parsePoint(fp);
96
 
        mv2 = parseSfield(fp);
97
 
      } else if (mv2 == null) {
98
 
        mv2 = parsePoint(fp);
99
 
        if (mv2 == null)
100
 
          mv2 = parseSfield(fp);
101
 
      }
102
 
 
103
 
      if (mv1 == null || mv2 == null) {
104
 
        throw new ParseException("geodist - not enough parameters:" + sources);
105
 
      }
106
 
 
107
 
      // We have all the parameters at this point, now check if one of the points is constant
108
 
      double[] constants;
109
 
      constants = getConstants(mv1);
110
 
      MultiValueSource other = mv2;
111
 
      if (constants == null) {
112
 
        constants = getConstants(mv2);
113
 
        other = mv1;
114
 
      }
115
 
 
116
 
      if (constants != null && other instanceof VectorValueSource) {
117
 
        return new HaversineConstFunction(constants[0], constants[1], (VectorValueSource)other);
118
 
      }      
119
 
 
120
 
      return new HaversineFunction(mv1, mv2, DistanceUtils.EARTH_MEAN_RADIUS_KM, true);
121
 
    }
122
 
  };
123
 
 
124
 
  /** make a MultiValueSource from two non MultiValueSources */
125
 
  private static VectorValueSource makeMV(List<ValueSource> sources, List<ValueSource> orig) throws ParseException {
126
 
    ValueSource vs1 = sources.get(0);
127
 
    ValueSource vs2 = sources.get(1);
128
 
 
129
 
    if (vs1 instanceof MultiValueSource || vs2 instanceof MultiValueSource) {
130
 
      throw new ParseException("geodist - invalid parameters:" + orig);
131
 
    }
132
 
    return  new VectorValueSource(sources);
133
 
  }
134
 
 
135
 
  private static MultiValueSource parsePoint(FunctionQParser fp) throws ParseException {
136
 
    String pt = fp.getParam(SpatialParams.POINT);
137
 
    if (pt == null) return null;
138
 
    double[] point = null;
139
 
    try {
140
 
      point = DistanceUtils.parseLatitudeLongitude(pt);
141
 
    } catch (InvalidGeoException e) {
142
 
      throw new ParseException("Bad spatial pt:" + pt);
143
 
    }
144
 
    return new VectorValueSource(Arrays.asList(new ValueSource[] {new DoubleConstValueSource(point[0]),new DoubleConstValueSource(point[1])}));
145
 
  }
146
 
 
147
 
  private static double[] getConstants(MultiValueSource vs) {
148
 
    if (!(vs instanceof VectorValueSource)) return null;
149
 
    List<ValueSource> sources = ((VectorValueSource)vs).getSources();
150
 
    if (sources.get(0) instanceof ConstNumberSource && sources.get(1) instanceof ConstNumberSource) {
151
 
      return new double[] { ((ConstNumberSource) sources.get(0)).getDouble(), ((ConstNumberSource) sources.get(1)).getDouble()};
152
 
    }
153
 
    return null;
154
 
  }
155
 
 
156
 
  private static MultiValueSource parseSfield(FunctionQParser fp) throws ParseException {
157
 
    String sfield = fp.getParam(SpatialParams.FIELD);
158
 
    if (sfield == null) return null;
159
 
    SchemaField sf = fp.getReq().getSchema().getField(sfield);
160
 
    ValueSource vs = sf.getType().getValueSource(sf, fp);
161
 
    if (!(vs instanceof MultiValueSource)) {
162
 
      throw new ParseException("Spatial field must implement MultiValueSource:" + sf);
163
 
    }
164
 
    return (MultiValueSource)vs;
165
 
  }
166
 
 
167
 
 
168
 
  //////////////////////////////////////////////////////////////////////////////////////
169
 
 
170
 
  private final double latCenter;
171
 
  private final double lonCenter;
172
 
  private final VectorValueSource p2;  // lat+lon, just saved for display/debugging
173
 
  private final ValueSource latSource;
174
 
  private final ValueSource lonSource;
175
 
 
176
 
  private final double latCenterRad_cos; // cos(latCenter)
177
 
  private static final double EARTH_MEAN_DIAMETER = DistanceUtils.EARTH_MEAN_RADIUS_KM * 2;
178
 
 
179
 
 
180
 
  public HaversineConstFunction(double latCenter, double lonCenter, VectorValueSource vs) {
181
 
    this.latCenter = latCenter;
182
 
    this.lonCenter = lonCenter;
183
 
    this.p2 = vs;
184
 
    this.latSource = p2.getSources().get(0);
185
 
    this.lonSource = p2.getSources().get(1);
186
 
    this.latCenterRad_cos = Math.cos(latCenter * DistanceUtils.DEGREES_TO_RADIANS);
187
 
  }
188
 
 
189
 
  protected String name() {
190
 
    return "geodist";
191
 
  }
192
 
 
193
 
  @Override
194
 
  public DocValues getValues(Map context, IndexReader reader) throws IOException {
195
 
    final DocValues latVals = latSource.getValues(context, reader);
196
 
    final DocValues lonVals = lonSource.getValues(context, reader);
197
 
    final double latCenterRad = this.latCenter * DistanceUtils.DEGREES_TO_RADIANS;
198
 
    final double lonCenterRad = this.lonCenter * DistanceUtils.DEGREES_TO_RADIANS;
199
 
    final double latCenterRad_cos = this.latCenterRad_cos;
200
 
 
201
 
    return new DocValues() {
202
 
      @Override
203
 
      public float floatVal(int doc) {
204
 
        return (float) doubleVal(doc);
205
 
      }
206
 
 
207
 
      @Override
208
 
      public int intVal(int doc) {
209
 
        return (int) doubleVal(doc);
210
 
      }
211
 
 
212
 
      @Override
213
 
      public long longVal(int doc) {
214
 
        return (long) doubleVal(doc);
215
 
      }
216
 
 
217
 
      @Override
218
 
      public double doubleVal(int doc) {
219
 
        double latRad = latVals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
220
 
        double lonRad = lonVals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
221
 
        double diffX = latCenterRad - latRad;
222
 
        double diffY = lonCenterRad - lonRad;
223
 
        double hsinX = Math.sin(diffX * 0.5);
224
 
        double hsinY = Math.sin(diffY * 0.5);
225
 
        double h = hsinX * hsinX +
226
 
                (latCenterRad_cos * Math.cos(latRad) * hsinY * hsinY);
227
 
        return (EARTH_MEAN_DIAMETER * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)));
228
 
      }
229
 
 
230
 
      @Override
231
 
      public String strVal(int doc) {
232
 
        return Double.toString(doubleVal(doc));
233
 
      }
234
 
 
235
 
      @Override
236
 
      public String toString(int doc) {
237
 
        return name() + '(' + latVals.toString(doc) + ',' + lonVals.toString(doc) + ',' + latCenter + ',' + lonCenter + ')';
238
 
      }
239
 
    };
240
 
  }
241
 
 
242
 
  @Override
243
 
  public void createWeight(Map context, Searcher searcher) throws IOException {
244
 
    latSource.createWeight(context, searcher);
245
 
    lonSource.createWeight(context, searcher);
246
 
  }
247
 
 
248
 
  @Override
249
 
  public boolean equals(Object o) {
250
 
    if (!(o instanceof HaversineConstFunction)) return false;
251
 
    HaversineConstFunction other = (HaversineConstFunction) o;
252
 
    return this.latCenter == other.latCenter
253
 
        && this.lonCenter == other.lonCenter
254
 
        && this.p2.equals(other.p2);
255
 
 
256
 
  }
257
 
 
258
 
  @Override
259
 
  public int hashCode() {
260
 
    int result = p2.hashCode();
261
 
    long temp;
262
 
    temp = Double.doubleToRawLongBits(latCenter);
263
 
    result = 31 * result + (int) (temp ^ (temp >>> 32));
264
 
    temp = Double.doubleToRawLongBits(lonCenter);
265
 
    result = 31 * result + (int) (temp ^ (temp >>> 32));
266
 
    return result;
267
 
  }
268
 
 
269
 
  @Override
270
 
  public String description() {
271
 
    return name() + '(' + p2 + ',' + latCenter + ',' + lonCenter + ')';
272
 
  }
273
 
}