Точка в сферическом полигоне с использованием Python [duplicate]

Поскольку я не нашел полного ответа по этой теме, я собрал класс, который должен правильно обрабатывать его, с поддержкой:

  • Форматирование: легко форматировать двойную строку с определенным количество знаков после запятой
  • Анализ: проанализируйте отформатированное значение обратно в double
  • Локаль: форматируйте и проанализируйте, используя стандартную локаль
  • Экспоненциальная нотация: начните с использования экспоненциальной нотации после некоторого порога

Использование довольно просто:

(Для этого примера я использую пользовательскую локаль)

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), "");
    }

}
18
задан Paul A. Hoadley 27 August 2009 в 11:46
поделиться

4 ответа

Существует несколько способов сделать это.

1) Включить вклады от широтных полос. Здесь площадь каждой полосы будет (Rcos (A) (B1-B0)) (RdA), где A - широта, B1 и B0 - начальная и конечная долготы, а все углы находятся в радианах.

2) Разломите поверхность на сферические треугольники и вычислите площадь с помощью теоремы Жирара и добавьте их вверх.

3) Как было предложено здесь Джеймс Шек, в работе ГИС они используют проекцию, сохраняющую область, на плоское пространство и вычисляют площадь там.

Из описания ваших данных в звуках, подобных первому методу, может быть проще всего. (Конечно, могут быть и другие более простые методы, о которых я не знаю.)

На этом имеется документ, если у вас есть доступ, но я натолкнулся на него googling для «продольных полос», поэтому я предполагаю, что они используют метод 1. выше.

Редактировать - сравнение этих двух методов:

При первом осмотре может показаться, что сферическая треугольный подход проще всего, но, в общем, это не так. Проблема состоит в том, что нужно не только разбивать область на треугольники, но и на сферические треугольники , т. Е. Треугольники, стороны которых являются большими дугами окружности. Например, широтные границы не квалифицируются , поэтому эти границы необходимо разбить на края, которые лучше приближаются к большим окружностям. И это становится труднее делать для произвольных ребер, где большие круги требуют определенных комбинаций сферических углов. Рассмотрим, например, как разбить среднюю полосу вокруг сферы, скажем, всю площадь между лат 0 и 45deg в сферические треугольники.

В конце концов, если кто-то должен сделать это правильно с аналогичными ошибки для каждого метода, метод 2 даст меньше треугольников, но их будет сложнее определить. Метод 1 дает больше полос, но их тривиально определить. Поэтому я предлагаю метод 1 как лучший подход.

9
ответ дан tom10 22 August 2018 в 21:38
поделиться
  • 1
    Мой ответ - это разработка вашего (2). Вычислительно, векторная математика будет намного дешевле, чем интеграция, и, вполне возможно, проще кодировать. Заметим, что все векторные операции могут выполняться со сферическими координатными векторами, которые по большей части широты / долготы. – Cascabel 27 August 2009 в 18:59
  • 2
    @Jefromi: Я думаю, что ваш комментарий неверен, и я отредактировал свой ответ, чтобы ответить на этот вопрос. – tom10 27 August 2009 в 22:40
  • 3
    Спасибо, Том. I предположим, что функция Matlab выполняет что-то вроде вашего (1). Я посмотрю, смогу ли я достать эту бумагу. Что касается вашего возражения против сферических треугольников, мой вопрос, возможно, не был полностью ясен по этому вопросу, но все, что у меня есть, - это вершины - упорядоченное множество пар широты / долготы. Края просто подразумеваются, поэтому мы можем также предположить, что они являются большими кругами для любых вычислений. – Paul A. Hoadley 28 August 2009 в 00:04
  • 4
    Пол ... это имеет смысл, особенно если вы указываете близко друг к другу. – tom10 28 August 2009 в 00:13
  • 5
    Мне удалось отследить эту бумагу. И, что удивительно, поскольку FTP-сервер, упомянутый в этой статье, ушел, связанный код. Поэтому я раскрою свои навыки Fortran и проверю это. – Paul A. Hoadley 29 August 2009 в 01:20
4
ответ дан Cascabel 22 August 2018 в 21:38
поделиться

Вы упоминаете «географию» в одном из ваших тегов, поэтому я могу только предположить, что вы находитесь за областью многоугольника на поверхности геоида. Обычно это делается с использованием проецируемой системы координат, а не географической системы координат (т. Е. Lon / lat). Если бы вы сделали это в lon / lat, я бы предположил, что возвращаемая единица измерения будет составлять процент поверхности сферы.

Если вы хотите сделать это с помощью более «ГИС», то вам нужно выбрать единицу измерения для вашей области и найти соответствующую проекцию, которая сохраняет область (не все). Поскольку вы говорите о вычислении произвольного многоугольника, я бы использовал что-то вроде проекции Ламберта азимутальной равной области . Установите начало / центр проекции в центр вашего многоугольника, проецируйте многоугольник в новую систему координат, затем вычислите область с помощью стандартных планарных методов.

Если вам нужно было сделать много полигонов в есть другие прогнозы, которые будут работать (или будут достаточно близки). Например, UTM является отличным приближением, если все ваши полигоны группируются вокруг одного меридиана.

Я не уверен, что какое-либо из этих действий имеет какое-либо отношение к тому, как работает функция Matlab's areaint.

6
ответ дан James Schek 22 August 2018 в 21:38
поделиться
  • 1
    Спасибо Джеймсу. Я задавался вопросом, возможно ли проецирование многоугольника в плоскость. Я вижу, что проекция сохраняет область, так что, возможно, это было бы идеально. – Paul A. Hoadley 29 August 2009 в 01:17
  • 2
    +1 ... право, беседуя с другом, который также выполняет много работы по ГИС, она сказала мне, что так они это делают. Есть ли причина для такого подхода? – tom10 30 August 2009 в 02:16
  • 3
    @ Пол - вы уже знаете это, но будьте осторожны, какую проекцию вы выберете. Некоторые проекции сохраняют область, другие - нет. Общий веб-Меркатор, используемый на большинстве карт, сохраняет только форму. – James Schek 31 August 2009 в 16:01
  • 4
    @tom Не знаю, почему ... Мое предположение заключается в том, что работать с декартовыми / планарными системами проще. Если вам нужно сделать больше, чем рассчитать площадь многоугольника, то наличие планарного представления облегчит жизнь. Плюс - USGS, среди прочего, обеспечивает "ссылку" реализации большинства основных методов проектирования. – James Schek 31 August 2009 в 16:04
  • 5
    @James: с вычислительной точки зрения: какой из прогнозов с равной площадью будет самым дешевым для расчета площади? Я имею в виду, какая проекция имеет простейшую формулу преобразования? – Igor Brejc 29 January 2010 в 06:35

Я переписал функцию «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);
}
8
ответ дан user2548538 22 August 2018 в 21:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: