Package org.apache.solr.search.function.distance

Source Code of org.apache.solr.search.function.distance.HaversineConstFunction

package org.apache.solr.search.function.distance;
/**
* 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.
*/

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.spatial.DistanceUtils;
import org.apache.lucene.spatial.tier.InvalidGeoException;
import org.apache.solr.common.params.SpatialParams;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.FunctionQParser;
import org.apache.solr.search.ValueSourceParser;
import org.apache.solr.search.function.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


/**
* Haversine function with one point constant
*/
public class HaversineConstFunction extends ValueSource {

  public static ValueSourceParser parser = new ValueSourceParser() {
    @Override
    public ValueSource parse(FunctionQParser fp) throws ParseException
    {
      // TODO: dispatch through SpatialQueriable in the future?
      List<ValueSource> sources = fp.parseValueSourceList();

      // "m" is a multi-value source, "x" is a single-value source
      // allow (m,m) (m,x,x) (x,x,m) (x,x,x,x)
      // if not enough points are present, "pt" will be checked first, followed by "sfield".     

      MultiValueSource mv1 = null;
      MultiValueSource mv2 = null;

      if (sources.size() == 0) {
        // nothing to do now
      } else if (sources.size() == 1) {
        ValueSource vs = sources.get(0);
        if (!(vs instanceof MultiValueSource)) {
          throw new ParseException("geodist - invalid parameters:" + sources);
        }
        mv1 = (MultiValueSource)vs;
      } else if (sources.size() == 2) {
        ValueSource vs1 = sources.get(0);
        ValueSource vs2 = sources.get(1);

        if (vs1 instanceof MultiValueSource && vs2 instanceof MultiValueSource) {
          mv1 = (MultiValueSource)vs1;
          mv2 = (MultiValueSource)vs2;
        } else {
          mv1 = makeMV(sources, sources);
        }
      } else if (sources.size()==3) {
        ValueSource vs1 = sources.get(0);
        ValueSource vs2 = sources.get(1);
        if (vs1 instanceof MultiValueSource) {     // (m,x,x)
          mv1 = (MultiValueSource)vs1;
          mv2 = makeMV(sources.subList(1,3), sources);
        } else {                                   // (x,x,m)
          mv1 = makeMV(sources.subList(0,2), sources);
          vs1 = sources.get(2);
          if (!(vs1 instanceof MultiValueSource)) {
            throw new ParseException("geodist - invalid parameters:" + sources);
          }
          mv2 = (MultiValueSource)vs1;
        }
      } else if (sources.size()==4) {
        mv1 = makeMV(sources.subList(0,2), sources);
        mv2 = makeMV(sources.subList(2,4), sources);
      } else if (sources.size() > 4) {
        throw new ParseException("geodist - invalid parameters:" + sources);
      }

      if (mv1 == null) {
        mv1 = parsePoint(fp);
        mv2 = parseSfield(fp);
      } else if (mv2 == null) {
        mv2 = parsePoint(fp);
        if (mv2 == null)
          mv2 = parseSfield(fp);
      }

      if (mv1 == null || mv2 == null) {
        throw new ParseException("geodist - not enough parameters:" + sources);
      }

      // We have all the parameters at this point, now check if one of the points is constant
      double[] constants;
      constants = getConstants(mv1);
      MultiValueSource other = mv2;
      if (constants == null) {
        constants = getConstants(mv2);
        other = mv1;
      }

      if (constants != null && other instanceof VectorValueSource) {
        return new HaversineConstFunction(constants[0], constants[1], (VectorValueSource)other);
      }     

      return new HaversineFunction(mv1, mv2, DistanceUtils.EARTH_MEAN_RADIUS_KM, true);
    }
  };

  /** make a MultiValueSource from two non MultiValueSources */
  private static VectorValueSource makeMV(List<ValueSource> sources, List<ValueSource> orig) throws ParseException {
    ValueSource vs1 = sources.get(0);
    ValueSource vs2 = sources.get(1);

    if (vs1 instanceof MultiValueSource || vs2 instanceof MultiValueSource) {
      throw new ParseException("geodist - invalid parameters:" + orig);
    }
    return  new VectorValueSource(sources);
  }

  private static MultiValueSource parsePoint(FunctionQParser fp) throws ParseException {
    String pt = fp.getParam(SpatialParams.POINT);
    if (pt == null) return null;
    double[] point = null;
    try {
      point = DistanceUtils.parseLatitudeLongitude(pt);
    } catch (InvalidGeoException e) {
      throw new ParseException("Bad spatial pt:" + pt);
    }
    return new VectorValueSource(Arrays.asList(new ValueSource[] {new DoubleConstValueSource(point[0]),new DoubleConstValueSource(point[1])}));
  }

  private static double[] getConstants(MultiValueSource vs) {
    if (!(vs instanceof VectorValueSource)) return null;
    List<ValueSource> sources = ((VectorValueSource)vs).getSources();
    if (sources.get(0) instanceof ConstNumberSource && sources.get(1) instanceof ConstNumberSource) {
      return new double[] { ((ConstNumberSource) sources.get(0)).getDouble(), ((ConstNumberSource) sources.get(1)).getDouble()};
    }
    return null;
  }

  private static MultiValueSource parseSfield(FunctionQParser fp) throws ParseException {
    String sfield = fp.getParam(SpatialParams.FIELD);
    if (sfield == null) return null;
    SchemaField sf = fp.getReq().getSchema().getField(sfield);
    ValueSource vs = sf.getType().getValueSource(sf, fp);
    if (!(vs instanceof MultiValueSource)) {
      throw new ParseException("Spatial field must implement MultiValueSource:" + sf);
    }
    return (MultiValueSource)vs;
  }


  //////////////////////////////////////////////////////////////////////////////////////

  private final double latCenter;
  private final double lonCenter;
  private final VectorValueSource p2;  // lat+lon, just saved for display/debugging
  private final ValueSource latSource;
  private final ValueSource lonSource;

  private final double latCenterRad_cos; // cos(latCenter)
  private static final double EARTH_MEAN_DIAMETER = DistanceUtils.EARTH_MEAN_RADIUS_KM * 2;


  public HaversineConstFunction(double latCenter, double lonCenter, VectorValueSource vs) {
    this.latCenter = latCenter;
    this.lonCenter = lonCenter;
    this.p2 = vs;
    this.latSource = p2.getSources().get(0);
    this.lonSource = p2.getSources().get(1);
    this.latCenterRad_cos = Math.cos(latCenter * DistanceUtils.DEGREES_TO_RADIANS);
  }

  protected String name() {
    return "geodist";
  }

  @Override
  public DocValues getValues(Map context, IndexReader reader) throws IOException {
    final DocValues latVals = latSource.getValues(context, reader);
    final DocValues lonVals = lonSource.getValues(context, reader);
    final double latCenterRad = this.latCenter * DistanceUtils.DEGREES_TO_RADIANS;
    final double lonCenterRad = this.lonCenter * DistanceUtils.DEGREES_TO_RADIANS;
    final double latCenterRad_cos = this.latCenterRad_cos;

    return new DocValues() {
      @Override
      public float floatVal(int doc) {
        return (float) doubleVal(doc);
      }

      @Override
      public int intVal(int doc) {
        return (int) doubleVal(doc);
      }

      @Override
      public long longVal(int doc) {
        return (long) doubleVal(doc);
      }

      @Override
      public double doubleVal(int doc) {
        double latRad = latVals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
        double lonRad = lonVals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
        double diffX = latCenterRad - latRad;
        double diffY = lonCenterRad - lonRad;
        double hsinX = Math.sin(diffX * 0.5);
        double hsinY = Math.sin(diffY * 0.5);
        double h = hsinX * hsinX +
                (latCenterRad_cos * Math.cos(latRad) * hsinY * hsinY);
        return (EARTH_MEAN_DIAMETER * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)));
      }

      @Override
      public String strVal(int doc) {
        return Double.toString(doubleVal(doc));
      }

      @Override
      public String toString(int doc) {
        return name() + '(' + latVals.toString(doc) + ',' + lonVals.toString(doc) + ',' + latCenter + ',' + lonCenter + ')';
      }
    };
  }

  @Override
  public void createWeight(Map context, Searcher searcher) throws IOException {
    latSource.createWeight(context, searcher);
    lonSource.createWeight(context, searcher);
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof HaversineConstFunction)) return false;
    HaversineConstFunction other = (HaversineConstFunction) o;
    return this.latCenter == other.latCenter
        && this.lonCenter == other.lonCenter
        && this.p2.equals(other.p2);

  }

  @Override
  public int hashCode() {
    int result = p2.hashCode();
    long temp;
    temp = Double.doubleToRawLongBits(latCenter);
    result = 31 * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToRawLongBits(lonCenter);
    result = 31 * result + (int) (temp ^ (temp >>> 32));
    return result;
  }

  @Override
  public String description() {
    return name() + '(' + p2 + ',' + latCenter + ',' + lonCenter + ')';
  }
}
TOP

Related Classes of org.apache.solr.search.function.distance.HaversineConstFunction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.