Mapping

June 28, 2018 11:49 am Update

People ask why I haven’t been making many new state land maps lately. πŸ•πŸ—Ί I’ve run out of state land to make maps of, and there aren’t that many changes to state lands from a year to year.🌳

Arduino GPS Logger Code I Wrote

Below is theΒ current version of the code I use for my GPS logger,Β which is powered by a Neo 6M GPS unit, an Arduino SD Card Reader with an 8 GB MicroSD Card, and an Arduino Nano. It logs a wide variety of information about my current location, speed of travel, and other information so I can easily look back at my trip.

 GPS Logger

/*
   GPS Logger

      SD card attached to SPI bus as follows:
    *  ** CS - pin 10
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13

*/

#include <TinyGPS++.h>
char p_buffer[100];
#define P(str) (strcpy_P(p_buffer, PSTR(str)), p_buffer)

#define HOMELAT  42.614277
#define HOMELNG -73.851662

#include <SoftwareSerial.h>

// The serial connection to the GPS module
SoftwareSerial ss(4, 3);

// The TinyGPS++ object
TinyGPSPlus gps;

// Line Buffer to Save to File
char lnBuffer[115];

// Decimal to Number Char String Buffer
char numStr[30];

// delay
uint8_t prevSecond;

// data logger
#include <SPI.h>
#include <SD.h>

const uint8_t chipSelect = 10;

// location of previous location, used
// to calculate distance traveled
float prevLat = 0;
float prevLng = 0;
float prevAlt = 0; // stored in meters
float prevSpeed = 0; // stored in meters per second
float prevTime = 0;

// keep a running total of miles since last boot
float milesSinceBoot = 0;

// filename
char fileName[13];

// don't do anything if card is bad
bool cardWorks = false;

void setup() {
  Serial.begin(115200);
  ss.begin(9600);
  for (uint8_t i = 6; i < 9; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
  }


  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println(F("Card failed, or not present"));
    digitalWrite(8, HIGH); //red
    cardWorks = false;

    // don't do anything more:
    return;
  }

  cardWorks = true;
  Serial.println(F("card initialized."));
  digitalWrite(6, HIGH);  // green

  fileName[0] = '\0';
}

void loop() {
  if (!cardWorks) {

    return;
  }

  while (ss.available() > 0) {
    gps.encode(ss.read());
  }

  // if no GPS blink red LED
  if (millis() > 5000 && gps.charsProcessed() < 10 && millis() % 1000 == 0) {
    digitalWrite(8, HIGH); //red
  }
  else if (millis() > 5000 && gps.charsProcessed() < 10 && millis() % 500 == 0) {
    digitalWrite(8, LOW); // red
  }

  // check if we have a valid location and blink yellow if invalid
  if (gps.location.isValid()) {
    digitalWrite(7, LOW);  // yellow
  }
  // next two lines will blink yellow (eye catching!)
  else if (!gps.location.isValid() && millis() % 1000 == 0) {
    digitalWrite(7, HIGH);  // yellow
  }
  else if (!gps.location.isValid() && millis() % 500 == 0) {
    digitalWrite(7, LOW);  // yellow
  }

  if (gps.location.isValid() && gps.time.second() != prevSecond) {

    // before we forget, log previous second
    prevSecond = gps.time.second();

    // if no file name, create
    if (fileName[0] == '\0') {
      // create filename
      // Example Filename = 03140201 (year=17, month=03, date=14, hour=02, tripnumber = 0
      uint8_t trip = 1;
      sprintf(fileName, P("%02d%02d%02d%02d.CSV"), gps.date.year() - 2000, gps.date.month(), gps.date.day(), trip);

      // well, if we have made 99 trips today (usually due to bad power connection) only write final trip
      while (SD.exists(fileName) && trip < 99) {
        trip++;
        sprintf(fileName, P("%02d%02d%02d%02d.CSV"), gps.date.year() - 2000, gps.date.month(), gps.date.day(), trip);
      }

      // print to serial where this data is being saved
      Serial.print(P("Writing to ... "));
      Serial.print(fileName);
      Serial.print(P("\n"));

      // write header to file
      File dataFileHead = SD.open(fileName, FILE_WRITE);

      // if not able to write, then turn LED red
      if (!dataFileHead) {
        digitalWrite(6, LOW);  // green
        digitalWrite(8, HIGH); //red
      }

      if (dataFileHead && trip != 99) {
        dataFileHead.println(P("year,month,day,hour,minute,second,sat,latitude,longitude,elev,mph,dir,cdir,grade,accel,trip,mihm,dirhm"));
        Serial.println( P("year,month,day,hour,minute,second,sat,latitude,longitude,elev,mph,dir,cdir,grade,accel,trip,mihm,dirhm"));
        lnBuffer[0] = '\0';
        dataFileHead.close();

        digitalWrite(6, HIGH);  // green
        digitalWrite(8, LOW); //red
      }

    }

    // dont log if no satellites found
    if (!gps.satellites.value()) return;

    sprintf(numStr, P("%04d,%02d,%02d,%02d,%02d,%02d, "), gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second());
    strcat(lnBuffer, numStr);


    dtostrf(gps.satellites.value(), 3, 0, numStr);
    if (gps.satellites.isValid())
      strcat(lnBuffer, numStr);
    strcat(lnBuffer, P(", "));


    dtostrf(gps.location.lat(), 10, 6, numStr);
    if (gps.location.isValid())
      strcat(lnBuffer, numStr);

    strcat(lnBuffer, P(", "));


    dtostrf(gps.location.lng(), 10, 6, numStr);

    if (gps.location.isValid())
      strcat(lnBuffer, numStr);

    strcat(lnBuffer, P(", "));

    if (gps.altitude.isValid())
      dtostrf(gps.altitude.feet(), 5, 0, numStr);

    strcat(lnBuffer, numStr);

    // we only add the additional info if we have enough buffer space
    // as occassionally before I was accidentially causing the buffer to
    // overflow. In an idea world, I'd use a bigger buffer but RAM is limited
    // e.g. if (strlen(lnBuffer) < 100)

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));

    dtostrf(gps.speed.mph(), 0, 1, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));

    dtostrf(gps.course.deg(), 0, 1, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, TinyGPSPlus::cardinal(gps.course.deg()));

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));

    // calculate current grade -- if we have a change in altitude
    // and we've traveled some kind of distance

    if (gps.altitude.isValid() && (gps.altitude.meters() - prevAlt) != 0 && TinyGPSPlus::distanceBetween(
          gps.location.lat(),
          gps.location.lng(),
          prevLat,
          prevLng) != 0) {


      dtostrf(
        (gps.altitude.meters() - prevAlt) /
        TinyGPSPlus::distanceBetween(
          gps.location.lat(),
          gps.location.lng(),
          prevLat,
          prevLng)

        , 5, 2, numStr);

      if (strlen(lnBuffer) < 100)
        strcat(lnBuffer, numStr);
    }
    else {
      if (strlen(lnBuffer) < 100)
        strcat(lnBuffer, P("0.00"));
    }

    // acceleration
    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));

    dtostrf((gps.speed.mps() - prevSpeed) / (millis() - prevTime), 7, 4, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));



    // total milage of trip at this part of logging
    // only increment when moving to minimize
    // "idle" gps hop

    if (prevLat && gps.speed.mph() > 1) {
      milesSinceBoot +=  TinyGPSPlus::distanceBetween(
                           gps.location.lat(),
                           gps.location.lng(),
                           prevLat,
                           prevLng) * 0.00062137112; // meters to miles
    }

    dtostrf( milesSinceBoot, 5, 2, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, numStr);

    if (strlen(lnBuffer) < 100)
      strcat(lnBuffer, P(", "));

    // miles from home
    dtostrf( TinyGPSPlus::distanceBetween(
               gps.location.lat(),
               gps.location.lng(),
               HOMELAT, HOMELNG) * 0.00062137112, 5, 2, numStr);

    if (strlen(lnBuffer) < 105)
      strcat(lnBuffer, numStr);

    // direction from home (for shits and giggles)

    if (strlen(lnBuffer) < 105)
      strcat(lnBuffer, P(", "));

    if (strlen(lnBuffer) < 110)
      strcat(lnBuffer,     TinyGPSPlus::cardinal(TinyGPSPlus::courseTo(
               HOMELAT, HOMELNG,
               gps.location.lat(),
               gps.location.lng()
             )));


    // save previous lat/lng/alt
    prevLat = gps.location.lat();
    prevLng = gps.location.lng();
    prevAlt = gps.altitude.meters();
    prevSpeed = gps.speed.mps();
    prevTime = millis();

    File dataFile = SD.open(fileName, FILE_WRITE);

    if (dataFile) {
      dataFile.println(lnBuffer);
      dataFile.close();

      // print to the serial port too:
      Serial.println(lnBuffer);
      digitalWrite(6, HIGH);  // green
      digitalWrite(8, LOW); //red
    }
    // if the file isn't open, pop up an error:
    else {
      Serial.print(F("error opening "));
      Serial.print(fileName);
      Serial.print("\n");

      digitalWrite(6, LOW);  // green
      digitalWrite(8, HIGH); //red

    }

    // clear buffer
    lnBuffer[0] = '\0';

  }
}

This is the code current version of the code I use for my GPS logger,Β which is powered by a Neo 6M GPS unit, an Arduino SD Card Reader with an 8 GB MicroSD Card, and an Arduino Nano. It logs a wide variety of information about my current location, speed of travel, and other information so I can easily look back at my trip.

Metes and bounds – Wikipedia

Metes and bounds – Wikipedia

Metes and bounds is a system or method of describing land, real property (in contrast to personal property) or real estate. Newer systems include rectangular (government survey), lot and block (recorded plat) and Torrens (used in Hawaii, Colorado, Australia, New Zealand and Canada).

Metes. The term "metes" refers to a boundary defined by the measurement of each straight run, specified by a distance between the terminal points, and an orientation or direction. A direction may be a simple compass bearing, or a precise orientation determined by accurate survey methods.

Bounds. The term "bounds" refers to a more general boundary description, such as along a certain watercourse, a stone wall, an adjoining public road way, or an existing building. The system is often used to define larger pieces of property (e.g. farms), and political subdivisions (e.g. town boundaries) where precise definition is not required or would be far too expensive, or previously designated boundaries can be incorporated into the description.

April 6, 2018 11:54 pm Update

Quantum GIS 3.0 is a really awesome piece of software. QGIS in recent years has been nipping at the heals of ArcGIS but with the latest version its taken another step closer.

QGIS biggest former weakness – the inflexible labeling engine that used to be a pain to edit labels has taken a Quatum leap forward. Also it’s much faster at many operations due to much more threading and caching. It’s great software that is open source, free for anybody to use for any purpose.

You can Download QGIS 3.0 here.