21

I am working on an iOS application. Where I show the Elevation and Topography map of a certain area. I have managed to download the .hgt file within app from here.

So far I am able to extract the Elevation from the hgt file. Now I have to also show the Terrain Map for that area. I have been searching about it and I think I can't create terrain map directly with hgt file within iOS application. I have to use GRASS GIS, SRTM2OSM or TileMill to create terrain map and then use it in application.

Can please anyone direct me what I can do here and how to proceed.

EDIT:

I have asked to not to use any kind of map for this. So basically I have to create the map by using core drawing, and I have no idea about it.

Something Like this without the text:

enter image description here

superGokuN
  • 1,399
  • 13
  • 28
  • 1
    Please add an example of how you want it to look. – Upholder Of Truth Dec 28 '17 at 08:09
  • So if I understand it this is what you need to do: you have hgt file, you need to create a bitmap (UIImage) from it, and place it on real map in some area. Correct? Conversion of height map to bitmap is simple and fun. You need to have an idea how final result should look like. Please update you question with some sample image. – Juraj Antas Dec 29 '17 at 19:46
  • info about hgt format: https://dds.cr.usgs.gov/srtm/version2_1/Documentation/Quickstart.pdf – Juraj Antas Dec 29 '17 at 19:48
  • @JurajAntas something like that only. A bitmap or a graph. I want something like this https://www.trails.com/images/topo/topo_sample2.jpg without the text, just the lines showing the elevation. The main issue is we can't use any kind of map to show this. So we have to draw all this with core drawing and I don't know how to do this. – superGokuN Jan 03 '18 at 11:07

2 Answers2

12

With iOS you have access to Maps through MapKit framework to display map or satellite imagery directly from your app's interface, you can use also Google Maps through Google Maps SDK for iOS but both (iOS Maps and Google Maps) don't have a terrain level.

So to avoid to re-create something that already exist you can take a look to the OpenStreetMaps frameworks, here you can find many available frameworks, one of them is called MapBox and you can download the latest sources and example here

As you can read from wiki pages we have also the terrain level: enter image description here

I think it's a really useful library, updated and working with swift 4 , here you can find an easy tutorial to start:

import Mapbox
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let mapView = MGLMapView(frame: view.bounds)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.setCenter(CLLocationCoordinate2D(latitude: 40.74699, longitude: -73.98742), zoomLevel: 9, animated: false)
        view.addSubview(mapView)
        // to show the terrain level 
        mapView.styleURL = MGLStyle.outdoorsStyleURL()
    }
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • Hi, I know I can use Mapkit and other applications like TileMill or MapBox Studio to create maps. But I have asked to not to use any kind of map. – superGokuN Jan 03 '18 at 11:11
  • 1
    Hi man, in this case you can find a good tutorial to better understand how to start with hand-writing maps (tiles). Take a look to [this guide](https://www.raywenderlich.com/158175/advanced-mapkit-tutorial-custom-tiles). Ask me, if you need other help. – Alessandro Ornano Jan 03 '18 at 11:16
  • Thanks for the reference. I am working with very confined options here. Sorry for being so clumsy in explaining the situation. I can't disclose the Idea behind the App. This app will work in an area with no Internet access, and client has strictly asked not to use any kind of Map. So basically I am left with only core drawing to draw the Image and apply shades and shadows to create something similar. Which is way beyond my tiny brain :( – superGokuN Jan 03 '18 at 11:34
  • You're welcome. Take a look also to [this](http://www.viggiosoft.com/blog/blog/2014/01/21/custom-and-offline-maps-using-overlay-tiles/) article, maybe could be interesting to solve your problems around offline maps. This tutorial have also it's github page [here](https://github.com/viggiosoft/MapTileOverlayTutorial) – Alessandro Ornano Jan 03 '18 at 11:56
0

I have to create the map by using core drawing

  • You can draw an Map object from SRTM HGT file [1]

  • For reading interpolated height from SRTM HGT files [2]

  • Below is code working with .cpp file you can get some idea for how to draw it from SRTM [3]

#include "generator/srtm_parser.hpp"

#include "coding/endianness.hpp"
#include "coding/zip_reader.hpp"

#include "base/logging.hpp"

#include <iomanip>
#include <sstream>

namespace generator
{
namespace
{
size_t constexpr kArcSecondsInDegree = 60 * 60;
size_t constexpr kSrtmTileSize = (kArcSecondsInDegree + 1) * (kArcSecondsInDegree + 1) * 2;

struct UnzipMemDelegate : public ZipFileReader::Delegate
{
  UnzipMemDelegate(std::string & buffer) : m_buffer(buffer), m_completed(false) {}

  // ZipFileReader::Delegate overrides:
  void OnBlockUnzipped(size_t size, char const * data) override { m_buffer.append(data, size); }

  void OnStarted() override
  {
    m_buffer.clear();
    m_completed = false;
  }

  void OnCompleted() override { m_completed = true; }

  std::string & m_buffer;
  bool m_completed;
};
}  // namespace

// SrtmTile ----------------------------------------------------------------------------------------
SrtmTile::SrtmTile()
{
  Invalidate();
}

SrtmTile::SrtmTile(SrtmTile && rhs) : m_data(move(rhs.m_data)), m_valid(rhs.m_valid)
{
  rhs.Invalidate();
}

void SrtmTile::Init(std::string const & dir, ms::LatLon const & coord)
{
  Invalidate();

  std::string const base = GetBase(coord);
  std::string const cont = dir + base + ".SRTMGL1.hgt.zip";
  std::string file = base + ".hgt";

  UnzipMemDelegate delegate(m_data);
  try
  {
    ZipFileReader::UnzipFile(cont, file, delegate);
  }
  catch (ZipFileReader::LocateZipException const & e)
  {
    // Sometimes packed file has different name. See N39E051 measure.
    file = base + ".SRTMGL1.hgt";

    ZipFileReader::UnzipFile(cont, file, delegate);
  }

  if (!delegate.m_completed)
  {
    LOG(LWARNING, ("Can't decompress SRTM file:", cont));
    Invalidate();
    return;
  }

  if (m_data.size() != kSrtmTileSize)
  {
    LOG(LWARNING, ("Bad decompressed SRTM file size:", cont, m_data.size()));
    Invalidate();
    return;
  }

  m_valid = true;
}

feature::TAltitude SrtmTile::GetHeight(ms::LatLon const & coord)
{
  if (!IsValid())
    return feature::kInvalidAltitude;

  double ln = coord.lon - static_cast<int>(coord.lon);
  if (ln < 0)
    ln += 1;
  double lt = coord.lat - static_cast<int>(coord.lat);
  if (lt < 0)
    lt += 1;
  lt = 1 - lt;  // from North to South

  size_t const row = kArcSecondsInDegree * lt;
  size_t const col = kArcSecondsInDegree * ln;

  size_t const ix = row * (kArcSecondsInDegree + 1) + col;

  if (ix >= Size())
    return feature::kInvalidAltitude;
  return ReverseByteOrder(Data()[ix]);
}

std::string SrtmTile::GetBase(ms::LatLon coord)
{
  std::ostringstream ss;
  if (coord.lat < 0)
  {
    ss << "S";
    coord.lat *= -1;
    coord.lat += 1;
  }
  else
  {
    ss << "N";
  }
  ss << std::setw(2) << std::setfill('0') << static_cast<int>(coord.lat);

  if (coord.lon < 0)
  {
    ss << "W";
    coord.lon *= -1;
    coord.lon += 1;
  }
  else
  {
    ss << "E";
  }
  ss << std::setw(3) << static_cast<int>(coord.lon);
  return ss.str();
}

void SrtmTile::Invalidate()
{
  m_data.clear();
  m_data.shrink_to_fit();
  m_valid = false;
}

// SrtmTileManager ---------------------------------------------------------------------------------
SrtmTileManager::SrtmTileManager(std::string const & dir) : m_dir(dir) {}
feature::TAltitude SrtmTileManager::GetHeight(ms::LatLon const & coord)
{
  std::string const base = SrtmTile::GetBase(coord);
  auto it = m_tiles.find(base);
  if (it == m_tiles.end())
  {
    SrtmTile tile;
    try
    {
      tile.Init(m_dir, coord);
    }
    catch (RootException const & e)
    {
      LOG(LINFO, ("Can't init SRTM tile:", base, "reason:", e.Msg()));
    }

    // It's OK to store even invalid tiles and return invalid height
    // for them later.
    it = m_tiles.emplace(base, std::move(tile)).first;
  }

  return it->second.GetHeight(coord);
}
}  // namespace generator