Hallo zusammen,
trotz mehrerer Stunden suchen und ausprobieren kann ich für mein Problem keine Lösung (hier und google) finden. Vielleicht kann mir ja jemand weiterhelfen:
In einer txt-Datei auf einer SD-Karte sind GPS-Koordinaten (mind. 3) abgelegt. Diese sollen in ein Array eingelesen werden und anschließend mittels „Point in Polygon“ weiterverarbeitet werden. Das Polygon soll hierbei aus den Koordinaten der txt-Datei bestehen.
Idee für Inhalt der txt-Datei (mit Texteditor erstellt) [longitude,latitude]:
52.518188,13.405258
50.935480,6.959870
48.137526,11.580147
Klar es gibt etliche Beispiele im Netz um Daten von SD auszulesen. Aber die Kombination float und sinnvoll in ein array schreiben finde ich nirgends, Anpassungsversuche meinerseits scheiterten bisher leider auch immer.
tm967:
In einer txt-Datei auf einer SD-Karte sind GPS-Koordinaten (mind. 3) abgelegt.
Mindestens 3?
Vielleicht aber auch 300?
Oder 3000?
Ich kann Dir nur sagen: RAM-Speicher steht in Mikrocontrollern nur sehr begrenzt zur Verfügung. Bei sehr vielen Punkten in der Datei dürftest Du einen Algorithmus benötigen, der die notwendigen Berechnungen ausführen kann, ohne dass zuerst die gesamte Datei in ein RAM-Array eingelesen werden muss.
Bei 3, 4, oder auch 10 Punkten dürfte ein Array im RAM kein Problem darstellen.
Aber "mindestens 3" kann ja im Endeffekt auch dreihundert oder dreitausend oder noch viel mehr Punkte bedeuten. Und bei einem 3000-Punkte-Polygon dürftest Du vom Algorithmus her anders an die Sache herangehen müssen als "erstmal alles aus der Datei im RAM einlesen".
@Serenifly
Super - vielen Dank für das Aufzeigen des Weges.
Ich habe es nun versucht einzubauen - leider funktioniert es noch nicht ganz. Ein Punkt der in dem Polygon liegt, wird nicht als solcher erkannt. Die Koordinaten der ersten Zeile werden jedoch angezeigt (nur bis zur 2. Nachkommastelle - aber das liegt ja nur an der Anzeige?)
Serenifly:
Dann kann man das nacheinander mehrmals aufrufen um die Zeilen auszulesen.
Blöde Frage, aber wie mach ich das? Sry, aber bin nicht so der große Programmierer ...
#include <SD.h>
const int STRING_BUFFER_SIZE = 30;
char stringBuffer[STRING_BUFFER_SIZE];
const uint8_t polySides = 3;
float polyX[polySides] = {};
float polyY[polySides] = {};
File dataFile;
void setup()
{
SD.begin();
Serial.begin(9600);
}
void loop()
{
dataFile = SD.open("data.txt");
readStream(dataFile);
float polyX = atof(strtok(stringBuffer, ","));
float polyY = atof(strtok(NULL, ","));
Serial.println("polyX:");
Serial.println(polyX);
Serial.println("polyY:");
Serial.println(polyY);
Serial.println("in Polygon?");
Serial.println(pointInPolygon(51.067083, 10.198048)); // Sollte drin sein
delay(10000);
}
bool readStream(Stream& stream)
{
static byte index;
while (stream.available())
{
char c = stream.read();
if (c >= 32 && index < STRING_BUFFER_SIZE - 1)
{
stringBuffer[index++] = c;
}
else if (c == '\n' && index > 0)
{
stringBuffer[index] = '\0';
index = 0;
return true;
}
}
return false;
}
bool pointInPolygon( float x, float y )
{
int i, j = polySides - 1;
bool oddNodes = false;
for ( i = 0; i < polySides; i++ )
{
if ( (polyY[i] < y && polyY[j] >= y || polyY[j] < y && polyY[i] >= y) && (polyX[i] <= x || polyX[j] <= x) )
{
oddNodes ^= ( polyX[i] + (y - polyY[i]) / (polyY[j] - polyY[i]) * (polyX[j] - polyX[i]) < x );
}
j = i;
}
return oddNodes;
}
@jurs
Mit mindestens 3 meinte ich eher, dass das Ganze mit 1-2 Koordinaten ja nur bedingt Sinn ergibt
Mehr als 6 sollten es nicht werden. Dafür wären 2 Polygone, auf welche getestet wird, schön.
Hat jemand eine Idee, wie man am einfachsten die Konstante POLY_SIDES (definiert mit const int) mit der Anzahl der Zeilen (also aus der if-Abfrage = numOfCoords) überschreibt, bzw. den Wert übergibt.
Ziel ist, auf die SD-Karte 3, 4, 5, ... 10 Zeilen mit Lat/Lon-Koordinaten zu schreiben und dann eine richtige Berechnung mittels "Point in Polygon" durchzuführen, ohne im Programmcode den POLY_SIDES Wert manuell verändern zu müssen.
Meine Idee wäre:
in IF-Abfrage prüfen, ob POLY_SIDES = numOfCoords, wenn nicht, Wert in EEPROM schreiben und neustart auslösen
bei jedem Start const int POLY_SIDES = Wert aus EEPROM
Scheint aber recht kompliziert ... gibt es einen einfacheren / schöneren Weg?
Du kannst die Zahl auch in die erste Zeile der Datei schreiben.
Man kann auch einmal über die Datei gehen und erst mal nur die Anzahl der Zeilen zählen. Und dann ein zweites mal um die Zeilen auszulesen.
Das ist dann keine Konstante mehr, sondern eine Variable
Mit dem Array bekommst du dann aber ein kleines Problem. Die Größe von globalen Arrays muss zur Compile-Zeit bekannt sein. Lokale Arrays können in avr-gcc (nicht in Standard C!) auch eine variable Länge haben.
Hier musst du dir überlegen ob du die Koordinaten wirklich dauerhaft speichern willst. Wenn du nur die Funktion füttern willst, müssen die eigentlich nicht global gespeichert werden und es reicht das Array lokal in der Funktion zu deklarieren. Wenn doch, wäre auch dynamischer Speicher noch eine Option, auch wenn es hier nicht gerne gesehen wird.
tm967:
Ziel ist, auf die SD-Karte 3, 4, 5, ... 10 Zeilen mit Lat/Lon-Koordinaten zu schreiben und dann eine richtige Berechnung mittels "Point in Polygon" durchzuführen, ohne im Programmcode den POLY_SIDES Wert manuell verändern zu müssen.
Meine Idee wäre:
in IF-Abfrage prüfen, ob POLY_SIDES = numOfCoords, wenn nicht, Wert in EEPROM schreiben und neustart auslösen
bei jedem Start const int POLY_SIDES = Wert aus EEPROM
Scheint aber recht kompliziert ... gibt es einen einfacheren / schöneren Weg?
Meine Idee wäre, den Wert als Maximalwert für die Anzal der Datenpaare zu definieren, also z.B. 10:
const int POLY_SIDES = 10; // so viele Werte dürfen maximal in der Datei stehen
Und in der Einleseroutine zählst Du mit, wie viele Datenpaare tatsächlich eingelesen werden:
int numPoints= 0; // die Anzahl der tatsächlich vorhandenen Werte
...
...
...
if (readStream(dataFile) == true)
{
polyCoords[numOfCoords].lat = atof(strtok(stringBuffer, ","));
polyCoords[numOfCoords].lon = atof(strtok(NULL, ","));
if (numPoint<POLY_SIDES) numPoints++; // ein weiteres Wertepaar wurde gelesen (bis zur Maximalanzahl)
}
Dann steht die tatsächliche Punktezahl nach dem Einlesen in der Variablen 'numPoints'.
Und innerhalb Deiner pointInPolygon() Funktion ersetzt Du alle Zugriffe auf die deklarierte Maximalanzahl in den Arrays ('polySides') durch Zugriffe auf die tatsächlich verwendete Anzahl ('numPoints').
Es werden maximal POLY_SIDES Paare eingelesen und man bekommt die tatsächliche Anzahl in numOfCoords. Die for-Schleife bricht mit break ab wenn das Ende der Datei kommt. Kann man natürlich auch anders zählen.
Ah ja, so einfach kann es sein ... Besten Dank! 8)
Ohne diesen "Zusatz" klappt der Point in Polygon-Code nicht. Verwendet man nur 3 Koordinatenpaare und lässt const int POLY_SIDES = 4, so bekommt man auch außerhalb des Polygons an manchen Stellen falsch-richtige Ergebnisse.