use of org.apache.solr.util.DistanceUnits in project lucene-solr by apache.
the class GeoDistValueSourceParser method parse.
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
// TODO: dispatch through SpatialQueryable in the future?
//note: parseValueSourceList can't handle a field reference to an AbstractSpatialFieldType,
// so those fields are expressly handled via sfield=
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 SyntaxError("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 SyntaxError("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 SyntaxError("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 SyntaxError("geodist - not enough parameters:" + sources);
}
// We have all the parameters at this point, now check if one of the points is constant
//latLon
double[] constants;
constants = getConstants(mv1);
MultiValueSource other = mv2;
if (constants == null) {
constants = getConstants(mv2);
other = mv1;
}
// sfield can only be in mv2, according to the logic above
if (mv2 instanceof SpatialStrategyMultiValueSource) {
if (constants == null)
throw new SyntaxError("When using AbstractSpatialFieldType (e.g. RPT not LatLonType)," + " the point must be supplied as constants");
// note: uses Haversine by default but can be changed via distCalc=...
SpatialStrategy strategy = ((SpatialStrategyMultiValueSource) mv2).strategy;
DistanceUnits distanceUnits = ((SpatialStrategyMultiValueSource) mv2).distanceUnits;
Point queryPoint = strategy.getSpatialContext().makePoint(constants[1], constants[0]);
return strategy.makeDistanceValueSource(queryPoint, distanceUnits.multiplierFromDegreesToThisUnit());
}
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);
}
use of org.apache.solr.util.DistanceUnits in project lucene-solr by apache.
the class SpatialHeatmapFacets method getHeatmapForField.
/** Called by {@link org.apache.solr.request.SimpleFacets} to compute heatmap facets. */
public static NamedList<Object> getHeatmapForField(String fieldKey, String fieldName, ResponseBuilder rb, SolrParams params, DocSet docSet) throws IOException {
//get the strategy from the field type
final SchemaField schemaField = rb.req.getSchema().getField(fieldName);
final FieldType type = schemaField.getType();
final PrefixTreeStrategy strategy;
final DistanceUnits distanceUnits;
// note: the two instanceof conditions is not ideal, versus one. If we start needing to add more then refactor.
if ((type instanceof AbstractSpatialPrefixTreeFieldType)) {
AbstractSpatialPrefixTreeFieldType rptType = (AbstractSpatialPrefixTreeFieldType) type;
strategy = (PrefixTreeStrategy) rptType.getStrategy(fieldName);
distanceUnits = rptType.getDistanceUnits();
} else if (type instanceof RptWithGeometrySpatialField) {
RptWithGeometrySpatialField rptSdvType = (RptWithGeometrySpatialField) type;
strategy = rptSdvType.getStrategy(fieldName).getIndexStrategy();
distanceUnits = rptSdvType.getDistanceUnits();
} else {
//FYI we support the term query one too but few people use that one
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "heatmap field needs to be of type " + SpatialRecursivePrefixTreeFieldType.class + " or " + RptWithGeometrySpatialField.class);
}
final SpatialContext ctx = strategy.getSpatialContext();
//get the bbox (query Rectangle)
String geomStr = params.getFieldParam(fieldKey, FacetParams.FACET_HEATMAP_GEOM);
final Shape boundsShape = geomStr == null ? ctx.getWorldBounds() : SpatialUtils.parseGeomSolrException(geomStr, ctx);
//get the grid level (possibly indirectly via distErr or distErrPct)
final int gridLevel;
Integer gridLevelObj = params.getFieldInt(fieldKey, FacetParams.FACET_HEATMAP_LEVEL);
final int maxGridLevel = strategy.getGrid().getMaxLevels();
if (gridLevelObj != null) {
gridLevel = gridLevelObj;
if (gridLevel <= 0 || gridLevel > maxGridLevel) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, FacetParams.FACET_HEATMAP_LEVEL + " should be > 0 and <= " + maxGridLevel);
}
} else {
//SpatialArgs has utility methods to resolve a 'distErr' from optionally set distErr & distErrPct. Arguably that
// should be refactored to feel less weird than using it like this.
SpatialArgs spatialArgs = new SpatialArgs(SpatialOperation.Intersects, /*ignored*/
boundsShape == null ? ctx.getWorldBounds() : boundsShape);
final Double distErrObj = params.getFieldDouble(fieldKey, FacetParams.FACET_HEATMAP_DIST_ERR);
if (distErrObj != null) {
// convert distErr units based on configured units
spatialArgs.setDistErr(distErrObj * distanceUnits.multiplierFromThisUnitToDegrees());
}
spatialArgs.setDistErrPct(params.getFieldDouble(fieldKey, FacetParams.FACET_HEATMAP_DIST_ERR_PCT));
double distErr = spatialArgs.resolveDistErr(ctx, DEFAULT_DIST_ERR_PCT);
if (distErr <= 0) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, FacetParams.FACET_HEATMAP_DIST_ERR_PCT + " or " + FacetParams.FACET_HEATMAP_DIST_ERR + " should be > 0 or instead provide " + FacetParams.FACET_HEATMAP_LEVEL + "=" + maxGridLevel + " if you insist on maximum detail");
}
//The SPT (grid) can lookup a grid level satisfying an error distance constraint
gridLevel = strategy.getGrid().getLevelForDistance(distErr);
}
//Compute!
final HeatmapFacetCounter.Heatmap heatmap;
try {
heatmap = HeatmapFacetCounter.calcFacets(strategy, rb.req.getSearcher().getTopReaderContext(), // turn DocSet into Bits
getTopAcceptDocs(docSet, rb.req.getSearcher()), boundsShape, gridLevel, // will throw if exceeded
params.getFieldInt(fieldKey, FacetParams.FACET_HEATMAP_MAX_CELLS, 100_000));
} catch (IllegalArgumentException e) {
//e.g. too many cells
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e.toString(), e);
}
//Populate response
NamedList<Object> result = new NamedList<>();
result.add("gridLevel", gridLevel);
result.add("columns", heatmap.columns);
result.add("rows", heatmap.rows);
result.add("minX", heatmap.region.getMinX());
result.add("maxX", heatmap.region.getMaxX());
result.add("minY", heatmap.region.getMinY());
result.add("maxY", heatmap.region.getMaxY());
boolean hasNonZero = false;
for (int count : heatmap.counts) {
if (count > 0) {
hasNonZero = true;
break;
}
}
formatCountsAndAddToNL(fieldKey, rb, params, heatmap.columns, heatmap.rows, hasNonZero ? heatmap.counts : null, result);
return result;
}
Aggregations