Создание класса для доступа и назначения векторных типов и создание класса, который получает позиции и назначает ему регион - PullRequest
0 голосов
/ 29 августа 2018

Я нахожусь в процессе анализа набора данных, который выглядит следующим образом

#Latitude   Longitude   Depth [m]   Bathy depth [m] CaCO3 [%] ...
-78 -177    0   693 1
-78 -173    0   573 2
.
.

План состоит в том, чтобы иметь возможность считывать данные в вектор, а затем разбивать их на различные группы «глубины Бати» (батиметрической глубины). Это также должно быть одновременно сгруппировано в различные океанские бассейны. Например, все точки данных в Северной Атлантике, которые также находятся между BathyDepth 500-1500 м, 1000-2000 м, 1500-2500 м ... должны быть в своей собственной группе (возможно, вектор или другой объект). Идея состоит в том, чтобы иметь возможность выводить их в разные текстовые файлы.

Я пытался сделать это в несколько запутанной манере. Вы можете увидеть это ниже

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <string>
#include <GeographicLib/Geodesic.hpp> //Library which allows for distance calculations

using namespace std;
using namespace GeographicLib;

//Define each basin spatially
//North Atlantic
double NAtlat1 = 1,  NAtlong1 = 1, NAtlat2 = 2, NAtlong2=2; //Incorrect               values, to be set later
//South Atlantic
double SAtlat1 = 1,  SAtlong1 = 1, SAtlat2 = 2, SAtlong2=2;
//North Pacific and the all others...

struct Point
{
        //structure Sample code/label--Lat--Long--SedimentDepth[m]--BathymetricDepth[m]--CaCO3[%]...
        string dummy;
        double latitude, longitude, rockDepth, bathyDepth, CaCO3, fCaCO3, bSilica, Quartz, CO3i, CO3c, DCO3;
        string dummy2;
        //Use Overload>> operator
        friend istream& operator>>(istream& inputFile, Point& p);
};

//Overload operator
istream& operator>>(istream& inputFile, Point& p)
{
        string row_text;
        //Read line from input, store in row_text
        getline(inputFile, row_text);
        //Extract line, store in ss row_stream
        istringstream row_stream(row_text);
        //Read-in data into each variable 
        row_stream >> p.dummy >> p.latitude >> p.longitude >> p.rockDepth >> p.bathyDepth >> p.CaCO3 >> p.fCaCO3 >> p.bSilica >> p.Quartz >> p.CO3i >> p.CO3c >> p.DCO3 >> p.dummy2;
        return inputFile;
}

int main ()
{
        //Geodesic class.
        const Geodesic& geod = Geodesic::WGS84();

        //Input file
    ifstream inputFile("Data.csv");
    //Point-type vector to store all data
    vector<Point> database;

    //bathDepth515 = depths between 500m and 1500m
    vector<Point> bathyDepth515;
    vector<Point> bathyDepth1020; //Create the rest too
    Point p;
    if (inputFile)
    {
            while(inputFile >> p)
            {
                    database.push_back(p);
            }

            inputFile.close();
    }
    else
    {
            cout <<"Unable to open file";
    }

    for(int i = 0; i < database.size(); ++i)
    {
            //Group data in database in sets of bathyDepth
            if(database[i].bathyDepth >= 500 && database[i].bathyDepth < 1500)
            {
                    //Find and fill particular bathDepths
                    bathyDepth515.push_back(database[i]);
            }
            if(database[i].bathyDepth >= 1000 && database[i].bathyDepth < 2000)
            {
                    bathyDepth1020.push_back(database[i]);
            }
            //...Further conditional statements based on Bathymetric depth, could easily include a spatial condition too...

    //Calculate distance between point i and all other points within 500-1500m depth window. Do the same with all other windows.
    for(int i = 0; i < bathyDepth515.size(); ++i)
    {
            for(int j = 0; j < bathyDepth515.size(); ++j)
            {
                    double s12;
                    if(i != j)
                    {
                            geod.Inverse(bathyDepth515[i].latitude, bathyDepth515[i].longitude, bathyDepth515[j].latitude, bathyDepth515[j].longitude, s12);
                    }
            }
    }
    return 0;
}

Задача 1: Я думаю, что ясно, что некоторые методологии не являются объектно-ориентированными. Например, может быть лучший способ назначить все данные Point определенному океанскому бассейну, вместо того, чтобы вручную вводить их в начале моей программы, как я делал это при группировании данных по глубине. Я начал создавать класс бассейна с методами для определения местоположения и определениями длины / долготы каждого бассейна, но не очень далеко нашел интуитивный способ сделать это. Я хотел бы, чтобы кто-то дал мне представление о том, как лучше это построить. Моя попытка построить (очень хрупкий) класс выглядит следующим образом

class Basin
{
    public:
            Basin();
            Basin(double latit1, double longit1, double latit2, double longit2);
            double getLatitude();
            ...
    private:
            double NAt, SAt, NPac, SPac; //All basins
            double latitude1, longitude1, latitude2, longitude2; //         Boundaries defined by two latitude markers, and two longitude markers.
};

class NAt: public Basin{...}
//...Build class definitions...

Задача 2: Моя вторая проблема - это метод, в котором я создал векторы для окон различной глубины. Это может стать очень громоздким, если мне придется изменить способ разделения глубин или добавить больше глубин. Мне бы не хотелось менять почти все части моей программы, просто чтобы приспособиться к тому, как я решил сдвинуть окна глубины. Я был бы признателен, если бы кто-нибудь дал мне несколько идей о том, как это сделать. Единственное решение, которое я мог придумать для проблемы 2, заключается в следующем

//Vector of vectors to store each rows' entries separately. Not sure if this is needed as my `Point` `Struct` already allows for each of the entries in a row to be accessed. 
    vector<vector<Point> > database;
    //Create database vectors for different bathDepth windows, eventhough their names will be the same, once push_back, it doesn't matter
    for(int i = 1*500; i<=8*500; i+=500)
    {
        vector<Point> bathDepthi;
        //Possibly push these back into database. These can then be accessed and populated using if statements.
    }
    //Create vectors individually, creating vector of vectors would be more elegant as I won't have to create more if one were to decide to change the range, I'd just have to change the for loop.

Я не знаю, насколько сильно мои попытки помочь мне, но я подумал, что мог бы дать лучшее представление о том, что я пытаюсь достичь. Извиняюсь за столь длинный пост.

ПЕРЕКЛЮЧАТЕЛЬ РЕДАКЦИИ-ДИЗАЙНА ОТ std::vector ДО std::map

Основываясь на информации, полученной от размещенного ответа Хеке, я попробовал следующее, но я не уверен, что именно это имел в виду указанный пользователь. Я решил использовать условные операторы, чтобы определить, находится ли точка в конкретном бассейне. Если моя попытка верна, я все еще не уверен, как поступить, если это правильно. В частности, я не уверен, как хранить для доступа к разделенным векторам, скажем, записывать их в отдельные текстовые файлы (т.е. файлы .txt для другой батиметрической глубины). Должен ли я хранить итератор разбиения в векторе, если так, какого типа? Итератор объявлен с auto, что смущает меня относительно того, как объявить тип вектора для хранения этого итератора.

Моя попытка:

std::map<std::string, std::vector<Point> > seamap;
seamap.insert( std::pair<std::string, std::vector<Point> > ("Nat", vector<Point>{}) );
seamap.insert( std::pair<std::string, std::vector<Point> > ("Sat", vector<Point>{}) ); //Repeat for all ocean basins

Point p;

while (inputFile >> p && !inputFile.eof() )
{
    //Check if Southern Ocean
    if (p.latitude > Slat2)
    {
        //Check if Atlantic, Pacific, Indian...
        if (p.longitude >= NAtlong1 && p.longitude < SAtlong2 && p.latitude > SPLIT)
        {
            seamap["Nat"].push_back(p);
        } // Repeat for different basins
    }
    else
    {
        seamap["South"].push_back(p);
    }
}

//Partition basins by depth
for ( std::map<std::string, std::vector<Point> >::iterator it2 = seamap.begin(); it2 != seamap.end(); it2++ )
{
    for ( int i = 500; i<=4500; i+=500 )
            {
                auto itp = std::partition( it2->second.begin(), it2->second.end(), [&i](const auto &a) {return a.bathyDepth < i;} );
            }
}

1 Ответ

0 голосов
/ 29 августа 2018

Задача 1

Проверьте это: Определение, находится ли набор точек внутри или снаружи квадрата

Вы можете расположить точку по их бассейну, читая файл данных. Вам просто нужна структура данных, где их. std :: map может быть хорошим кандидатом. (используйте имя океана в качестве ключа и вектор точек в качестве значения)

Задача 2

Ваши пределы окна глубины, кажется, растут с шагом 500 м, и диапазоны, кажется, перекрываются. Может быть, использование std :: partition для этих векторов внутри этой карты будет работать?

некоторые псевдо:

  1. разделить вектор с <500 в качестве предиката, используя целое диапазон. </li>
  2. сохранить итератор, возвращенный на шаге 1, и использовать его как нижний, а конечный итератор - как верхний.
  3. теперь раздел с <1000 в качестве предиката. </li>
  4. так же, как 2-3. но с <1500 в качестве предиката и итератором, возвращаемым <1000 в качестве нижнего. </li>
  5. повторять, пока все глубины не будут покрыты.
  6. сделать то же самое для всех морских векторов, хранящихся на этой карте.

Итераторы, которые вы сохранили, могут быть использованы для получения определенного диапазона из этого конкретного вектора.

Т.е. используйте итератор, возвращаемый разделом <500, в качестве нижнего, и итератор, возвращаемый разделом <1500, и вы получаете диапазон с глубинами от 500 до 1500 в этом конкретном векторе. </p>

Теперь у вас должна быть карта, полная векторов, к которым вы можете получить доступ по названию океана и диапазону глубины с интервалами 500 м.

редактировать: некоторые уточнения

std::map<std::string, std::vector<point>> seamap;
seamap.insert("Nat", std::vector<point>{}); // do this for every sea.
//read point data from file

while(FILESTREAM_NOT_EMPTY)
{
    //construct a point object here.

    // add points to correct vector

    if (THIS_PARTICULAR_POINT_IS_IN_NAT)
           seamap["Nat"].push_back(point);
    else if (THIS_PARTICULAR_POINT_IS_IN_SAT)
           seamap["Sat"].push_back(point);
    //and so on...
}

после этого выполните разбиение каждого вектора в этой карте моря. (помощник: Как использовать основанный на диапазоне цикл for () с std :: map? )

for (int i =500; i<4500;i+500)
{
      auto iter_for_next = std::partition(LOWER_ITERATOR,
                                          UPPER_ITERATOR, 
                                          [&i]( const auto &a){return a < i;});

//here save the iterator for later use in some kind of structure.
}

Надеюсь, это поможет!

...