Commit 55035812 authored by Béla Wiegel's avatar Béla Wiegel Committed by Gerrit Erichsen
Browse files

Performance improvement, bug fix

PlantDataReaderDialog: add ability to assign Location by centroid-nearest Location if no municipality is given
AreaPotentialCalculations, WindowShapeFileLoading: added possibility to subtract forbidden area by POL files from area potential
PolygonObjectManager: significant performance improvement by using tree structures for containers
MarginCalculationManager: performance improvement by using STL iterators
LocationsWidget: bug fix due to wrong object generation on heap
POLReadWrite: added function to get polygons by regional code
parent 893ea95c
......@@ -180,6 +180,24 @@ POLMunicipal * POLReadWrite::getMunicipal(quint8 SN_L, QString regionalCode)
return nullptr;
}
QList<QPolygonF> POLReadWrite::getPolygons(QString regionalCode)
{
for (POLDataSet * dataSet : m_dataSets)
{
QList<POLMunicipal*> municipals = dataSet->municipals();
for (POLMunicipal * municipal : municipals)
{
if (municipal->regionalCode() == regionalCode)
{
return municipal->polygons();
}
}
}
return QList<QPolygonF>();
}
void POLReadWrite::clear()
{
for (POLDataSet * dataSet : m_dataSets)
......
......@@ -22,6 +22,7 @@ public:
QList<POLDataSet*> getDataSets() const;
POLMunicipal * getMunicipal(quint8 SN_L, QString regionalCode);
QList<QPolygonF> getPolygons(QString regionalCode);
void clear();
......
......@@ -212,55 +212,26 @@ void PlantDataReaderDialog::processFile()
}
}
float minDistance = static_cast<float>(1e30);
int minDistanceIndex = -1;
float minDistance = -1;
// check if lat and lon fields are available
if (!found && csvIndexLat != -1 && csvIndexLon != -1)
{
// check if both lat and lon are set
if (csvData[csvIndexLat] != "" && csvData[csvIndexLon] != "")
if (csvData[csvIndexLat] != "" && csvData[csvIndexLon] != "" && csvData[csvIndexLat] != "0" && csvData[csvIndexLon] != "0")
{
QString strLat = csvData[csvIndexLat];
QString strLon = csvData[csvIndexLon];
QPointF plantCoordinates(csvData[csvIndexLon].toDouble(), csvData[csvIndexLat].toDouble());
if (locationDataString != "")
locationDataString += ", ";
locationDataString += "lat: " + strLat + ", lon: " + strLon;
locationDataString += "lat: " + csvData[csvIndexLat] + ", lon: " + csvData[csvIndexLon];
// iterate locations to match plant to location based on map polygon
QList<int> indices;
for (int i = 0; i < m_locationsTableModel->rowCount(); i++)
indices << i;
float distance = static_cast<float>(1e30);
for (int index : indices)
{
const LocationsTableItem * locationItem_temp = static_cast<const LocationsTableItem*>(m_locationsTableModel->getItem(index));
// calculate distance
QPointF diff = VectorCalculations::differenceVectorENU(plantCoordinates, locationItem_temp->getLonLat(), plantCoordinates);
distance = VectorCalculations::norm(diff);
if (distance < minDistance)
{
minDistance = distance;
minDistanceIndex = index;
}
if (locationItem_temp->getMapPolygon().containsPoint(plantCoordinates, Qt::WindingFill))
{
// match
locationID = index;
locationItem = locationItem_temp;
nodeItem = static_cast<const NodesTableItem*>(m_nodesTableModel->getItem(locationItem->getNodeID()));
found = true;
qDebug() << " found corresponding location" << locationItem->getName() << "and node" << nodeItem->getName() << "by coordinates";
break;
}
}
struct NearestLocationIndex nearestIndex = findNearestLocationIndex(csvData[csvIndexLat], csvData[csvIndexLon]);
minDistanceIndex = nearestIndex.minDistanceIndex;
minDistance = nearestIndex.minDistance;
found = nearestIndex.found;
locationID = nearestIndex.locationID;
locationItem = nearestIndex.locationItem;
nodeItem = nearestIndex.nodeItem;
}
}
......@@ -269,7 +240,8 @@ void PlantDataReaderDialog::processFile()
found = true;
locationID = minDistanceIndex;
locationItem = static_cast<const LocationsTableItem*>(m_locationsTableModel->getItem(minDistanceIndex));
nodeItem = static_cast<const NodesTableItem*>(m_nodesTableModel->getItem(locationItem->getNodeID()));
if (locationItem != nullptr)
nodeItem = static_cast<const NodesTableItem*>(m_nodesTableModel->getItem(locationItem->getNodeID()));
}
// continue only if location (and corresponding node) are found
......@@ -340,7 +312,7 @@ void PlantDataReaderDialog::processFile()
{
if (locationDataString == "")
{
qDebug() << " no location data available";
qDebug() << " no location data available. Added plant without creating ConfigTableItem.";
}
else
{
......@@ -366,8 +338,12 @@ bool secondIsLower(const pair<double,double> & a, const pair<double,double> & b)
void PlantDataReaderDialog::processOPSDWindData()
{
// debug
float processedPower = 0;
float assignedPower = 0;
float debug_processedPower = 0;
float debug_assignedPower = 0;
float debug_powerAGS = 0;
float debug_powerNaN = 0;
float debug_powerMissing = 0;
float debug_powerFound = 0;
QList<float> turbinePower;
for (int i = 0; i < m_windTableModel->rowCount(); i++)
......@@ -377,7 +353,6 @@ void PlantDataReaderDialog::processOPSDWindData()
qSort(curve.begin(), curve.end(), secondIsLower);
turbinePower.append(static_cast<float>(curve.last().second));
qDebug() << curve.last().second;
}
QList<float> turbinePowerSorted = QList<float>(turbinePower);
......@@ -399,6 +374,8 @@ void PlantDataReaderDialog::processOPSDWindData()
// get csv columns
int indexAGS = csvHeader.indexOf("municipality_code");
int indexPower = csvHeader.indexOf("electrical_capacity");
int indexLat = csvHeader.indexOf("lat");
int indexLon = csvHeader.indexOf("lon");
QMap<QString, QList<float>> powerPerClassinAGS;
while (!file.atEnd())
......@@ -409,14 +386,44 @@ void PlantDataReaderDialog::processOPSDWindData()
QString AGS = csvData[indexAGS];
// fill up to length of 8
while (AGS.size() < 8)
AGS = "0" + AGS;
float power = csvData[indexPower].toFloat(); // [MW]
processedPower += power;
debug_processedPower += power;
power *= 1000; // [kW]
if (AGS == "nan")
{
if (csvData[indexLat] == "" || csvData[indexLon] == "")
{
debug_powerMissing += power;
}
else
{
// find LocationItem by Polygon
struct NearestLocationIndex nearestIndex = findNearestLocationIndex(csvData[indexLat], csvData[indexLon]);
if (nearestIndex.found)
{
AGS = nearestIndex.locationItem->getAGS();
debug_powerFound += power;
}
else
{
AGS = static_cast<const LocationsTableItem*>(m_locationsTableModel->getItem(nearestIndex.minDistanceIndex))->getAGS();
debug_powerNaN += power;
}
}
}
else
{
// fill up to length of 8
while (AGS.size() < 8)
AGS = "0" + AGS;
debug_powerAGS += power;
}
if (power == 10)
power = 10;
int powerClass = 0;
while (powerClass < powerClasses.size() - 1 &&
!(power >= powerClasses[powerClass] &&
......@@ -430,7 +437,7 @@ void PlantDataReaderDialog::processOPSDWindData()
powerPerClassinAGS[AGS].append(0);
}
powerPerClassinAGS[AGS][powerClass] += power;
powerPerClassinAGS[AGS][powerClass] += (power);
}
file.close();
......@@ -450,7 +457,8 @@ void PlantDataReaderDialog::processOPSDWindData()
for (int turbineIndex = 0; turbineIndex < powerPerClassinAGS[locationItem->getAGS()].size(); turbineIndex++)
{
int turbineID = turbinePower.indexOf(turbinePowerSorted[turbineIndex]);
if (powerPerClassinAGS[locationItem->getAGS()][turbineID] == 0)
if (powerPerClassinAGS[locationItem->getAGS()][turbineIndex] == 0)
continue;
ConfigTableItem configTableItem;
......@@ -461,18 +469,23 @@ void PlantDataReaderDialog::processOPSDWindData()
configTableItem.setStepSizePower(0);
// set max_power, min_power and step_power
configTableItem.setMaxPower(static_cast<double>(powerPerClassinAGS[locationItem->getAGS()][turbineID]));
configTableItem.setMinPower(configTableItem.getMaxPower());
assignedPower += configTableItem.getMaxPower();
double power = static_cast<double>(powerPerClassinAGS[locationItem->getAGS()][turbineIndex]);
power /= 1000; // from [kW] to [MW]
configTableItem.setMaxPower(power);
configTableItem.setMinPower(power);
debug_assignedPower += configTableItem.getMaxPower();
// set area proportional to installed power
float powerSum = 0;
for (float p : powerPerClassinAGS[locationItem->getAGS()])
powerSum += p;
configTableItem.setAreaOfPark(configTableItem.getMaxPower() / powerSum * locationItem->getAreaWind() * 1e6);
configModel->addItem(configTableItem);
}
}
}
qDebug() << processedPower;
qDebug() << assignedPower;
// skip csv writing
return;
......@@ -505,6 +518,46 @@ void PlantDataReaderDialog::processOPSDWindData()
result.close();
}
PlantDataReaderDialog::NearestLocationIndex PlantDataReaderDialog::findNearestLocationIndex(QString lat, QString lon)
{
QPointF plantCoordinates(lon.toDouble(), lat.toDouble());
// iterate locations to match plant to location based on map polygon
QList<int> indices;
for (int i = 0; i < m_locationsTableModel->rowCount(); i++)
indices << i;
struct NearestLocationIndex result;
for (int index : indices)
{
const LocationsTableItem * locationItem_temp = static_cast<const LocationsTableItem*>(m_locationsTableModel->getItem(index));
// calculate distance
QPointF diff = VectorCalculations::differenceVectorENU(plantCoordinates, locationItem_temp->getLonLat(), plantCoordinates);
float distance = VectorCalculations::norm(diff);
if (distance < result.minDistance)
{
result.minDistance = distance;
result.minDistanceIndex = index;
}
if (locationItem_temp->getMapPolygon().containsPoint(plantCoordinates, Qt::WindingFill))
{
// match
result.locationID = index;
result.locationItem = locationItem_temp;
result.nodeItem = static_cast<const NodesTableItem*>(m_nodesTableModel->getItem(result.locationItem->getNodeID()));
result.found = true;
qDebug() << " found corresponding location" << result.locationItem->getName() << "and node" << result.nodeItem->getName() << "by coordinates";
break;
}
}
return result;
}
void PlantDataReaderDialog::LonLatToAGS()
{
QFile file("D:/Locations_Nodes_Plants/wind_remaining.csv");
......
......@@ -53,6 +53,19 @@ private:
QString m_filePath;
QPushButton * m_btnOPSDData;
// variables
struct NearestLocationIndex {
bool found = false;
float minDistance = static_cast<float>(1e30);
int minDistanceIndex = -1;
int locationID;
const LocationsTableItem * locationItem;
const NodesTableItem * nodeItem;
};
// functions
NearestLocationIndex findNearestLocationIndex(QString lat, QString lon);
// temporary
void LonLatToAGS();
};
......
......@@ -338,6 +338,8 @@ void LocationsWidget::initWidgets(SelectorListModel * selectNodeModel,
SelectorListModel * temperatureDataSelect,
SelectorListModel * powerDemandDataSelect)
{
m_polygonMananger = new PolygonManager();
// HTTPGet: OSM interaction
m_http = new HTTPGet();
connect(m_http, &HTTPGet::signal_allFinished,
......@@ -362,7 +364,6 @@ void LocationsWidget::initWidgets(SelectorListModel * selectNodeModel,
m_widgetPolygonPoints = new WidgetPolygonPoints(m_polygonObjectMananger, this);
// Polygon Manager Widget: User interactions with PolygonObjects
m_polygonMananger = new PolygonManager();
m_polygonMananger->setPolygonObjectManager(m_polygonObjectMananger);
// Shape file reader
......
......@@ -462,6 +462,8 @@ void WindowLocationsCreator::createItemsBySF()
QString uniqueName = Descr + AreaType + "_" + ID;
LocationsTableItem locationItem(Name, uniqueName, "No_AGS", lat, lon, static_cast<double>(areaSolar), static_cast<double>(areaWind), polygonID);
locationItem.setAGS(ID);
locationItem.setAreaPolygonsWind(polygonsAreaWind);
locationItem.setAreaPolygonsSolar(polygonsAreaSolar);
......
......@@ -2,16 +2,17 @@
AreaPotentialCalculations::AreaPotentialCalculations(WindowShapeFileLoading * windowShapeFileLoading) :
m_windowShapeFileLoading(windowShapeFileLoading),
m_last_SN_L(0)
m_last_SN_L(15)
{
connect(m_windowShapeFileLoading, &WindowShapeFileLoading::calculationSequenceFinished, this, &AreaPotentialCalculations::handleCalculationSequenceFinished);
// set shape file to be loaded
m_windowShapeFileLoading->selectShapeFile(
"C:/Users/wkax39/Gemeindedaten/Shape-Files/VG250_GEM",
"D:/Gemeindedaten/Shape-Files/VG250_GEM",
ShapeFileRW::UTM_ETRS89_GRS80_32N);
m_windowShapeFileLoading->selectPOLFile("D:/Calculation_Results/Area_Potential/CalculatedAreaPotential_Germany_Wind_20191014115238_bugfix.pol");
// startCalculations();
startCalculations();
// unifyPOLFiles();
}
......@@ -30,7 +31,8 @@ void AreaPotentialCalculations::setCalculationValues(SN_L stateNumber)
<< getKeyValueString(Landuse_Residential)
<< getKeyValueString(Natural_Wood)
<< getKeyValueString(Leisure_NatureReserve)
<< getKeyValueString(Natural_Water);
<< getKeyValueString(Natural_Water)
<< getKeyValueString(Landuse_Farmland);
QList<int> margins;
......@@ -39,9 +41,10 @@ void AreaPotentialCalculations::setCalculationValues(SN_L stateNumber)
margins.clear();
margins
<< 0
<< 1000
<< 100 // Wind: 1000, Solar: 100
<< 0
<< 100 // Wind: 500, Solar: 100
<< 0
<< 500
<< 0;
m_windowShapeFileLoading->setOSMKeyValues(OSMKeyValueStrings, margins);
......@@ -62,6 +65,8 @@ QString AreaPotentialCalculations::getKeyValueString(AreaPotentialCalculations::
return "'leisure' = 'nature_reserve'";
case Natural_Water:
return "'natural' = 'water'";
case Landuse_Farmland:
return "'landuse' = 'farmland'";
}
return "";
......
......@@ -45,7 +45,8 @@ private:
Landuse_Forest,
Natural_Wood,
Leisure_NatureReserve,
Natural_Water
Natural_Water,
Landuse_Farmland
};
void setCalculationValues(SN_L stateNumber);
......
......@@ -26,6 +26,7 @@ void PolygonObjectManager::add(PolygonObject * p, QString name, QPolygonF polygo
m_QueryTypeList.append(queryType);
m_PolygonNames.append(name);
m_PolygonNamesMap.insert(name, m_numOfPolygons);
m_polygonDistances.append(-1);
++m_numOfPolygons;
......@@ -35,7 +36,7 @@ void PolygonObjectManager::add(PolygonObject * p, QString name, QPolygonF polygo
int PolygonObjectManager::getIndex(QString PolygonName, bool noLocking)
{
if (noLocking)
return m_PolygonNames.indexOf(PolygonName);
return m_PolygonNamesMap[PolygonName];
else
return getIndex(PolygonName);
}
......@@ -46,6 +47,7 @@ PolygonObject * PolygonObjectManager::remove(int index) {
PolygonObject * p = getPolygonObject(index);
m_objectList.removeAt(index);
m_polygonList.removeAt(index);
m_PolygonNamesMap.remove(m_PolygonNames[index]);
m_PolygonNames.removeAt(index);
m_polygonDistances.removeAt(index);
m_QueryTypeList.removeAt(index);
......@@ -226,7 +228,7 @@ void PolygonObjectManager::appendQueryType(int index, QString queryType)
int PolygonObjectManager::getIndex(QString PolygonName)
{
readWriteLock.lockForRead();
int index = m_PolygonNames.indexOf(PolygonName);
int index = m_PolygonNamesMap[PolygonName];
readWriteLock.unlock();
return index;
}
......@@ -235,7 +237,7 @@ bool PolygonObjectManager::hasPolygonObject(QString polygonName)
{
readWriteLock.lockForRead();
bool found = m_PolygonNames.contains(polygonName);
bool found = m_PolygonNamesMap.contains(polygonName);
readWriteLock.unlock();
return found;
......@@ -283,9 +285,9 @@ PolygonObject * PolygonObjectManager::createPolygonObject(QPolygonF poly, QStrin
readWriteLock.lockForRead();
// check if polygon already added
if (m_PolygonNames.contains(name))
if (m_PolygonNamesMap.contains(name))
{
int indexOfPolygon = m_PolygonNames.indexOf(name);
int indexOfPolygon = m_PolygonNamesMap[name];
PolygonObject * p = getPolygonObject(indexOfPolygon);
readWriteLock.unlock();
if (p == nullptr)
......@@ -388,6 +390,7 @@ void PolygonObjectManager::clearAll()
m_QueryDescription.clear();
m_QueryTypeList.clear();
m_PolygonNames.clear();
m_PolygonNamesMap.clear();
m_polygonDistances.clear();
m_numOfPolygons = 0;
......
......@@ -69,6 +69,7 @@ private:
QList<QString> m_QueryDescription;
QList<QList<QString>> m_QueryTypeList; // Due to kind of use, multiple query types per polygon are possible
QList<QString> m_PolygonNames;
QMap<QString, int> m_PolygonNamesMap;
QList<int> m_polygonDistances;
int m_numOfPolygons;
......
......@@ -20,6 +20,7 @@ WindowShapeFileLoading::WindowShapeFileLoading(
m_calculationSequence(false),
m_settings(settings),
m_SF(SF),
m_POL(nullptr),
m_polygonObjectManager(POM),
m_http(http),
m_totalSFPolygons(0),
......@@ -30,7 +31,8 @@ WindowShapeFileLoading::WindowShapeFileLoading(
<< QString("'landuse' = 'forest'")
<< QString("'natural' = 'wood'")
<< QString("'leisure' = 'nature_reserve'")
<< QString("'natural' = 'water'");
<< QString("'natural' = 'water'")
<< QString("'landuse' = 'farmland'");
initWindow();
......@@ -105,8 +107,8 @@ void WindowShapeFileLoading::update_prbCalcMargins()
void WindowShapeFileLoading::finishedMarginCalculationStep(QPolygonF marginPolygon, MarginCalculationManager::PolygonData polygonData)
{
Q_UNUSED(marginPolygon);
Q_UNUSED(polygonData);
Q_UNUSED(marginPolygon)
Q_UNUSED(polygonData)
update_prbCalcMargins();
}
......@@ -137,6 +139,13 @@ void WindowShapeFileLoading::loadSFFile()
m_btnAddPolygons->setEnabled(true);
}
void WindowShapeFileLoading::loadPOLFile()
{
m_POL = new POLReadWrite();
if (!m_POL->openFile(m_POLPath))
QMessageBox::warning(this, "POL File", "Cannot open POL file.");
}
void WindowShapeFileLoading::handleChbVisualiseOSMPolygonsChanged(bool checked)
{
m_settings->setValue("VisualiseOSMPolygons", checked);
......@@ -304,7 +313,7 @@ void WindowShapeFileLoading::OSMCalcMargins()
disconnect(m_http, &HTTPGet::LocalFileProcessingFinished, this, &WindowShapeFileLoading::handleHTTPGetFinished);
vector<MarginCalculationManager::PolygonData> marginCalculationData;
QList<QString> alreadyAddedPolygonNames;
QMap<QString, quint8> alreadyAddedPolygonNames;
qDebug() << "Distributing combs for parallel calculation...";
......@@ -341,9 +350,22 @@ void WindowShapeFileLoading::OSMCalcMargins()
// check if polygon is already added
if (!alreadyAddedPolygonNames.contains(data.polygonName))
marginCalculationData.push_back(data);
alreadyAddedPolygonNames << data.polygonName;
alreadyAddedPolygonNames.insert(data.polygonName, 0);
}
}
// treat POL polygons as margin polygon, not very nice, but gives same result
QList<QPolygonF> areaPotential = m_POL->getPolygons(sf->objectName());
for (int i = 0; i < areaPotential.size(); i++)
{
m_polygonObjectManager->createPolygonObject(
areaPotential[i],
sf->objectName() + "_pol_" + QString::number(i),
"POL area potential",
m_polygonObjectManager->getQueryType(sf).first() + "_POL",
QColor(22, 116, 33, 80),
m_settings->value("MapGraphics/VisualiseOSMPolygons", true).toBool());
}
}
m_marginCalculationManager = new MarginCalculationManager(m_polygonObjectManager, marginCalculationData);
......@@ -389,6 +411,7 @@ void WindowShapeFileLoading::CalcCombStructure()
/// get comb structure from CombManager and prepare margin polygon lists
QVector<pair<int, int>> combStructure = QVector<pair<int, int>>::fromStdVector(m_combManager->getCombStructure(sf->geoPoly()));
qDebug() << " time for structure generation:" << timer.restart();
QVector<QPolygonF> polygonsPerSFPolygon;
// loop through key/value pairs from OSM query
......@@ -406,13 +429,25 @@ void WindowShapeFileLoading::CalcCombStructure()
}
}
qDebug() << "Elapsed time for finding margin polygons:" << timer.restart();
// get POL Polygons
if (m_POL != nullptr)
{
QList<pair<QString, QPolygonF>> pList = m_polygonObjectManager->getPListByQueryType(
m_polygonObjectManager->getQueryType(sf).first() + "_POL");
for (pair<QString, QPolygonF> p : pList)
{
polygonsPerSFPolygon << p.second;
}
}
qDebug() << " time for finding margin polygons:" << timer.restart();
// sort polygon list by left point in x-direction (in LatLon coordinates)
QVector<QPolygonF> sorted_x_left = QVector<QPolygonF>(polygonsPerSFPolygon);
qSort(sorted_x_left.begin(), sorted_x_left.end(), PolygonObjectManager::xLeftLowerP);
qDebug() << "Elapsed time for sorting:" << timer.restart();
qDebug() << " time for sorting:" << timer.restart();
int numberOfThreads = QThreadPool::globalInstance()->maxThreadCount();
QList<QVector<pair<int, int>>> combList;
......@@ -434,10 +469,10 @@ void WindowShapeFileLoading::CalcCombStructure()
}
QThreadPool::globalInstance()->waitForDone();
qDebug() << "Elapsed time for comb calculations (parallel):" << timer.restart();
qDebug() << " time for comb calculations (parallel):" << timer.restart();
m_listPolygonsLastQuery_PermittedCombs << m_combManager->numberOfPermittedCombs(sf->boundingRectLL()); // not usable!!!
qDebug() << "Elapsed time for comb counting:" << timer.restart();
qDebug() << " time for comb counting:" << timer.restart();
}
m_btnUniteCombs->setEnabled(true);
......@@ -587,6 +622,37 @@ void WindowShapeFileLoading::fileDialogSFFile()
loadSFFile();
}