blob: 5f05cf17973c71faccd2d1dc779bb499c0d5427e [file] [log] [blame]
package org.apache.solr.util;
/*
* 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 com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.exception.InvalidShapeException;
import com.spatial4j.core.shape.Point;
import org.apache.solr.common.SolrException;
/** Utility methods pertaining to spatial. */
public class SpatialUtils {
private SpatialUtils() {}
/** Parses either "lat, lon" (spaces optional on either comma side) or "x y" style formats. Spaces can be basically
* anywhere. And not any whitespace, just the space char.
*
* @param str Non-null; may have leading or trailing spaces
* @param ctx Non-null
* @return Non-null
* @throws InvalidShapeException If for any reason there was a problem parsing the string or creating the point.
*/
public static Point parsePoint(String str, SpatialContext ctx) throws InvalidShapeException {
//note we don't do generic whitespace, just a literal space char detection
//TODO: decide on if we should pick one format decided by ctx.isGeo()
// Perhaps 5x use isGeo; 4x use either?
try {
double x, y;
str = str.trim();//TODO use findIndexNotSpace instead?
int commaIdx = str.indexOf(',');
if (commaIdx == -1) {
// "x y" format
int spaceIdx = str.indexOf(' ');
if (spaceIdx == -1)
throw new InvalidShapeException("Point must be in 'lat, lon' or 'x y' format: " + str);
int middleEndIdx = findIndexNotSpace(str, spaceIdx + 1, +1);
x = Double.parseDouble(str.substring(0, spaceIdx));
y = Double.parseDouble(str.substring(middleEndIdx));
} else {
// "lat, lon" format
int middleStartIdx = findIndexNotSpace(str, commaIdx - 1, -1);
int middleEndIdx = findIndexNotSpace(str, commaIdx + 1, +1);
y = Double.parseDouble(str.substring(0, middleStartIdx + 1));
x = Double.parseDouble(str.substring(middleEndIdx));
}
x = ctx.normX(x);//by default norm* methods do nothing but perhaps it's been customized
y = ctx.normY(y);
return ctx.makePoint(x, y);//will verify x & y fit in boundary
} catch (InvalidShapeException e) {
throw e;
} catch (Exception e) {
throw new InvalidShapeException(e.toString(), e);
}
}
private static int findIndexNotSpace(String str, int startIdx, int inc) {
assert inc == +1 || inc == -1;
int idx = startIdx;
while (idx >= 0 && idx < str.length() && str.charAt(idx) == ' ')
idx += inc;
return idx;
}
/** Calls {@link #parsePoint(String, com.spatial4j.core.context.SpatialContext)} and wraps
* the exception with {@link org.apache.solr.common.SolrException} with a helpful message. */
public static Point parsePointSolrException(String externalVal, SpatialContext ctx) throws SolrException {
try {
return parsePoint(externalVal, ctx);
} catch (InvalidShapeException e) {
String message = e.getMessage();
if (!message.contains(externalVal))
message = "Can't parse point '" + externalVal + "' because: " + message;
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message, e);
}
}
}