Поскольку я не нашел полного ответа по этой теме, я собрал класс, который должен правильно обрабатывать его, с поддержкой:
Использование довольно просто:
(Для этого примера я использую пользовательскую локаль)
public static final int DECIMAL_PLACES = 2;
NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);
String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"
double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345
Вот класс:
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
public class NumberFormatter {
private static final String SYMBOL_INFINITE = "\u221e";
private static final char SYMBOL_MINUS = '-';
private static final char SYMBOL_ZERO = '0';
private static final int DECIMAL_LEADING_GROUPS = 10;
private static final int EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation
private DecimalFormat decimalFormat;
private DecimalFormat decimalFormatLong;
private DecimalFormat exponentialFormat;
private char groupSeparator;
public NumberFormatter(int decimalPlaces) {
configureDecimalPlaces(decimalPlaces);
}
public void configureDecimalPlaces(int decimalPlaces) {
if (decimalPlaces <= 0) {
throw new IllegalArgumentException("Invalid decimal places");
}
DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
separators.setMinusSign(SYMBOL_MINUS);
separators.setZeroDigit(SYMBOL_ZERO);
groupSeparator = separators.getGroupingSeparator();
StringBuilder decimal = new StringBuilder();
StringBuilder exponential = new StringBuilder("0.");
for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
}
for (int i = 0; i < decimalPlaces; i++) {
decimal.append("#");
exponential.append("0");
}
exponential.append("E0");
decimalFormat = new DecimalFormat(decimal.toString(), separators);
decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
exponentialFormat = new DecimalFormat(exponential.toString(), separators);
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
}
public String format(double value) {
String result;
if (Double.isNaN(value)) {
result = "";
} else if (Double.isInfinite(value)) {
result = String.valueOf(SYMBOL_INFINITE);
} else {
double absValue = Math.abs(value);
if (absValue >= 1) {
if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
value = Math.floor(value);
result = exponentialFormat.format(value);
} else {
result = decimalFormat.format(value);
}
} else if (absValue < 1 && absValue > 0) {
if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
result = decimalFormat.format(value);
if (result.equalsIgnoreCase("0")) {
result = decimalFormatLong.format(value);
}
} else {
result = exponentialFormat.format(value);
}
} else {
result = "0";
}
}
return result;
}
public String formatWithoutGroupSeparators(double value) {
return removeGroupSeparators(format(value));
}
public double parse(String value, double defValue) {
try {
return decimalFormat.parse(value).doubleValue();
} catch (ParseException e) {
e.printStackTrace();
}
return defValue;
}
private String removeGroupSeparators(String number) {
return number.replace(String.valueOf(groupSeparator), "");
}
}
Существует несколько способов сделать это.
1) Включить вклады от широтных полос. Здесь площадь каждой полосы будет (Rcos (A) (B1-B0)) (RdA), где A - широта, B1 и B0 - начальная и конечная долготы, а все углы находятся в радианах.
2) Разломите поверхность на сферические треугольники и вычислите площадь с помощью теоремы Жирара и добавьте их вверх.
3) Как было предложено здесь Джеймс Шек, в работе ГИС они используют проекцию, сохраняющую область, на плоское пространство и вычисляют площадь там.
Из описания ваших данных в звуках, подобных первому методу, может быть проще всего. (Конечно, могут быть и другие более простые методы, о которых я не знаю.)
Редактировать - сравнение этих двух методов:
При первом осмотре может показаться, что сферическая треугольный подход проще всего, но, в общем, это не так. Проблема состоит в том, что нужно не только разбивать область на треугольники, но и на сферические треугольники , т. Е. Треугольники, стороны которых являются большими дугами окружности. Например, широтные границы не квалифицируются , поэтому эти границы необходимо разбить на края, которые лучше приближаются к большим окружностям. И это становится труднее делать для произвольных ребер, где большие круги требуют определенных комбинаций сферических углов. Рассмотрим, например, как разбить среднюю полосу вокруг сферы, скажем, всю площадь между лат 0 и 45deg в сферические треугольники.
В конце концов, если кто-то должен сделать это правильно с аналогичными ошибки для каждого метода, метод 2 даст меньше треугольников, но их будет сложнее определить. Метод 1 дает больше полос, но их тривиально определить. Поэтому я предлагаю метод 1 как лучший подход.
Вы упоминаете «географию» в одном из ваших тегов, поэтому я могу только предположить, что вы находитесь за областью многоугольника на поверхности геоида. Обычно это делается с использованием проецируемой системы координат, а не географической системы координат (т. Е. Lon / lat). Если бы вы сделали это в lon / lat, я бы предположил, что возвращаемая единица измерения будет составлять процент поверхности сферы.
Если вы хотите сделать это с помощью более «ГИС», то вам нужно выбрать единицу измерения для вашей области и найти соответствующую проекцию, которая сохраняет область (не все). Поскольку вы говорите о вычислении произвольного многоугольника, я бы использовал что-то вроде проекции Ламберта азимутальной равной области . Установите начало / центр проекции в центр вашего многоугольника, проецируйте многоугольник в новую систему координат, затем вычислите область с помощью стандартных планарных методов.
Если вам нужно было сделать много полигонов в есть другие прогнозы, которые будут работать (или будут достаточно близки). Например, UTM является отличным приближением, если все ваши полигоны группируются вокруг одного меридиана.
Я не уверен, что какое-либо из этих действий имеет какое-либо отношение к тому, как работает функция Matlab's areaint.
Я переписал функцию «isaint» MATLAB в java, которая имеет точно такой же результат. «isaint» вычисляет «suface на единицу», поэтому я умножил ответ на «Площадь поверхности Земли» (5.10072e14 кв. м.).
private double area(ArrayList<Double> lats,ArrayList<Double> lons)
{
double sum=0;
double prevcolat=0;
double prevaz=0;
double colat0=0;
double az0=0;
for (int i=0;i<lats.size();i++)
{
double colat=2*Math.atan2(Math.sqrt(Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)+ Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)),Math.sqrt(1- Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)- Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)));
double az=0;
if (lats.get(i)>=90)
{
az=0;
}
else if (lats.get(i)<=-90)
{
az=Math.PI;
}
else
{
az=Math.atan2(Math.cos(lats.get(i)*Math.PI/180) * Math.sin(lons.get(i)*Math.PI/180),Math.sin(lats.get(i)*Math.PI/180))% (2*Math.PI);
}
if(i==0)
{
colat0=colat;
az0=az;
}
if(i>0 && i<lats.size())
{
sum=sum+(1-Math.cos(prevcolat + (colat-prevcolat)/2))*Math.PI*((Math.abs(az-prevaz)/Math.PI)-2*Math.ceil(((Math.abs(az-prevaz)/Math.PI)-1)/2))* Math.signum(az-prevaz);
}
prevcolat=colat;
prevaz=az;
}
sum=sum+(1-Math.cos(prevcolat + (colat0-prevcolat)/2))*(az0-prevaz);
return 5.10072E14* Math.min(Math.abs(sum)/4/Math.PI,1-Math.abs(sum)/4/Math.PI);
}