Getting Started

Accessing AccuTerra Maps

Welcome to AccuTerra Maps. Adding AccuTerra Maps to your mobile or web applications is a great way to empower your users with access to the most complete outdoor recreation maps avaliable.

AccuTerra provides multiple map styles for you to choose from, see the Map Styles section below for more information on each style. Each map style supports both raster and vector tiles.

Each map service requires a API key to work. You can manage your API keys within the API Keys section of your Account page.

Get started developing with AccuTerra today, create your free account.

Map Styles

AccuTerra Outdoors

AccuTerra Outdoors showcases the unique map content AccuTerra has to offer. Our public lands data, combined with . We show in bright and bold color America’s many public land types so you always know who manages the land you’re on. When combined with our extensive road and trail network and overlaid on our highly detailed terrain data, AccuTerra Outdoors is the perfect companion for your next outdoor adventure. This map is available in both metric and imperial elevation units.

AccuTerra Topo

AccuTerra Topo is another take on our unique mapping content. With a different styling for roads, trails, and terrain AccuTerra Topo gives you another option for presenting AccuTerra to your users. Topo is well suited for use outside of the United States. This map is available in both metric and imperial elevation units.

AccuTerra Terrain

AccuTerra Terrain is a clean light basemap depicting AccuTerra’s terrain layer . This map is available in both metric and imperial elevation units.

Code Samples

Web

<!DOCTYPE HTML>
<html lang="en">

<head>
  <title>Leaflet</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
    integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
    crossorigin="" />
  <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
    integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
    crossorigin=""></script>
  <style>
    html,
    body {
      height: 100%;
      padding: 0;
      margin: 0;
    }

    #map {
      /* configure the size of the map */
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="map"></div>
  <script>
    // initialize Leaflet
    var map = L.map('map').setView({
      lon: -105.00,
      lat: 39.65
    }, 10);

    var myKey = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc";
    L.tileLayer(
      `https://{s}.accuterra.com/v1/raster/accuterra-outdoors/{z}/{x}/{y}.png?key=${myKey}`, {
        maxZoom: 16,
        attribution: '&copy; AccuTerra Maps',
        subdomains: [
          "maps-d1",
          "maps-d2",
          "maps-d3",
          "maps-d4",
        ]
      }).addTo(map);
  </script>
</body>

</html>
<html>
<head>
 <title>Leaflet + Mapbox GL</title>
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.6.0/leaflet.js"></script>
 <script type="text/javascript" src="https://api.mapbox.com/mapbox-gl-js/v1.10.1/mapbox-gl.js"></script>
 <link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v1.10.1/mapbox-gl.css">
 <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.6.0/leaflet.css">
 <script type="text/javascript" src="https://rawgit.com/mapbox/mapbox-gl-leaflet/master/leaflet-mapbox-gl.js"></script>
 <style type="text/css">
     html, body, #my-map {
         width: 100%;
         height: 300px;
         margin: 0;
     }
</style>
</head>
<body>
 <div id="my-map"></div>
 <script type="text/javascript">
     var map = L.map('my-map').setView([39.6500327, -105.0053989], 11);
     // the attribution is required
     map.attributionControl.setPrefix('').addAttribution('Powered by <a href="https://www.accuterra.com/" target="_blank">Accuterra</a> | © OpenStreetMap <a href="https://www.openstreetmap.org/copyright" target="_blank">contributors</a>');
     var myKey = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc"; 
     var gl = L.mapboxGL({
         style: `https://maps.accuterra.com/v1/styles/accuterra-outdoors/style.json?key=${myKey}`,
         accessToken: 'no-token'
     }).addTo(map);
</script>
</body>
</html>
<html>
<head>
    <title>MapBox GL</title>
    <script type="text/javascript" src="https://api.mapbox.com/mapbox-gl-js/v1.10.1/mapbox-gl.js"></script>
<link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v1.10.1/mapbox-gl.css">
    <style type="text/css">
        body {
            margin: 0;
            padding: 0;
        }
        #my-map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
    </style>
</head>
<body>
    <div id="my-map"></div>
    <script type="text/javascript">
        var myKey = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc";
        var map = new mapboxgl.Map({
            container: 'my-map',
            style: `https://maps.accuterra.com/v1/styles/accuterra-outdoors/style.json?key=${myKey}`,
            center: [-105.00,39.65],
            zoom: 10,
            attributionControl: false
        }).addControl(new mapboxgl.AttributionControl({
            compact: false,
            customAttribution: '&copy; AccuTerra Maps'
        }));
        map.addControl(new mapboxgl.NavigationControl());
    </script>
</body>
</html>
<html>
<head>
 <title>OpenLayers</title>
 <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css">
 <style type="text/css">
    html, body, #my-map {
        width: 100%;
        height: 100%;
        margin: 0;
    }

 </style>
 <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
 <script type="text/javascript" src="https://unpkg.com/ol-mapbox-style@6.1.4/dist/olms.js"></script> 
</head>
<body>
 <div id="my-map"></div>
 <script type="text/javascript">
     var myKey = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc";

     olms('my-map', `https://maps.accuterra.com/v1/styles/accuterra-outdoors/style.json?key=${myKey}`).then((map) => {
        let view = map.getView();
        view.setCenter(ol.proj.fromLonLat([-105.0053989, 39.6500327]));
        view.setZoom(10);
        map.setView(view);
     });
 </script>
</body>
</html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="https://js.arcgis.com/4.17/esri/themes/light/main.css">
    <script src="https://js.arcgis.com/4.17/"></script>
    <title>ArcGIS JavaScript</title>
    <style>
      html, body, #viewDiv {
        padding: 0;
        margin: 0;
        height: 300px;
        width: 100%;
      }
    </style>
    <script>
  require([
      "esri/Map",
      "esri/views/MapView",
      "esri/Basemap",
      "esri/layers/VectorTileLayer",
      "esri/layers/WebTileLayer",
    ], function(Map, MapView, Basemap, VectorTileLayer, WebTileLayer) {

    const myAPIKey = 'eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc';

    //override all XHRs (used in 3.x) to append api key
    (function() {
      var proxyOpen = window.XMLHttpRequest.prototype.open;
      window.XMLHttpRequest.prototype.open = function() {
        var args = Array.prototype.slice.call(arguments);
        if (args[1].indexOf("accuterra.com")>0){
          args[1] += (args[1].indexOf("?") == -1 ? "?" : "&") + `key=${myAPIKey}`;
        }
        return proxyOpen.apply(this, [].slice.call(args));
      };})();

    //override all fetch() calls (used in 4.x) to append api key
    (function () {
      var originalFetch = fetch;
      fetch = function() {
        var args = Array.prototype.slice.call(arguments);
        if (args[0].indexOf("accuterra.com")>0){
          args[0] += (args[0].indexOf("?") == -1 ? "?" : "&") + `key=${myAPIKey}`;
        }
        return originalFetch.apply(this, args).then(function(data) {
          // someFunctionToDoSomething();
          return data;
        });
      };
    })();

    // base vector layer
    var basemap = new Basemap({
        baseLayers: [
          new VectorTileLayer({
            style: "esri-accuterra.json"
          })
        ]
      });
      
    var map = new Map({
      basemap: basemap
    });
    
    // contours vector layer
    var contourLayer = new VectorTileLayer({
            style: "esri-accuterra-contours.json"
    });    
    map.add(contourLayer);
    
    // hill shade raster layer
    var hillShadeLayer = new WebTileLayer({
      urlTemplate: `https://maps.accuterra.com/v1/data/hillshade/{level}/{col}/{row}.jpg?key=${myAPIKey}`,
          opacity: 0.2
    });
    map.add(hillShadeLayer);
    
    var view = new MapView({
      container: "viewDiv",
      map: map,
      center: [-105.0053989, 39.6500327], // longitude, latitude
      zoom: 10
    });
  });
  </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>    
    
<html>
  <head>
    <title>Google Maps API with AccuTerra raster as base layer</title>
    <style>
      html, body, #map {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false&key=AIzaSyBy9114j99nP1KuB4P_bAIrLhNR2JFiS8Y"></script>
    <script type="text/javascript">

        var myAPIKey = 'eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc';

        var element = document.getElementById("map");
        /*
        Build list of map types.
        You can also use var mapTypeIds = ["roadmap", "satellite", "hybrid", "terrain", "AccuTerra"]
        but static list is dangerous because Google might update the default list of map types.
        */
        var mapTypeIds = [];
        for(var type in google.maps.MapTypeId) {
            mapTypeIds.push(google.maps.MapTypeId[type]);
        }
        mapTypeIds.push("AccuTerra");

        var map = new google.maps.Map(element, {
            center: {lat: 39.6500327, lng: -105.0053989},
            zoom: 10,
            mapTypeId: "AccuTerra",
            mapTypeControlOptions: {
                mapTypeIds: mapTypeIds
            }
        });

        map.mapTypes.set("AccuTerra", new google.maps.ImageMapType({
            getTileUrl: function(coord, zoom) {
              return `https://maps.accuterra.com/v1/raster/accuterra-outdoors/${zoom}/${coord.x}/${coord.y}.png?key=${myAPIKey}`;
            },
            tileSize: new google.maps.Size(256, 256),
            name: "AccuTerra",
            maxZoom: 18
        }));

    </script>
  </body>
</html>

Mobile

package com.accuterra.accuterramapsdemo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.mapboxsdk.Mapbox
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Mapbox access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Mapbox.getInstance(this, "no-token")

        val accuterraMapsToken = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc"
        
        // This contains the MapView in XML and needs to be called after the access token is configured.
        setContentView(R.layout.activity_main)
        mapView?.onCreate(savedInstanceState)
        mapView?.getMapAsync { mapboxMap ->
            mapboxMap.setStyle("https://maps.accuterra.com/v1/styles/accuterra-outdoors/style.json?key=$accuterraMapsToken") {
                // Map is set up and the style has loaded. Now you can add data or make other map adjustments.
            }
        }
    }

    // Add the mapView lifecycle to the activity's lifecycle methods
    public override fun onResume() {
        super.onResume()
        mapView?.onResume()
    }

    override fun onStart() {
        super.onStart()
        mapView?.onStart()
    }

    override fun onStop() {
        super.onStop()
        mapView?.onStop()
    }

    public override fun onPause() {
        super.onPause()
        mapView?.onPause()
    }

    override fun onLowMemory() {
        super.onLowMemory()
        mapView?.onLowMemory()
    }

    override fun onDestroy() {
        super.onDestroy()
        mapView?.onDestroy()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        mapView?.onSaveInstanceState(outState)
    }
}
import UIKit
import Mapbox

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let accuterraMapsToken = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc"
        
        let url = URL(string: "https://maps.accuterra.com/v1/styles/accuterra-outdoors/style.json?key=\(accuterraMapsToken)")
        let mapView = MGLMapView(frame: view.bounds, styleURL: url)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.setCenter(CLLocationCoordinate2D(latitude: 39.375239, longitude: -104.861077), zoomLevel: 14, animated: false)
        view.addSubview(mapView)
    }

}
package com.accuterra.accuterramapsdemo

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng

class MainActivity : AppCompatActivity(), OnMapReadyCallback {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Retrieve the content view that renders the map.
        setContentView(R.layout.activity_maps)
        if (getString(R.string.maps_api_key).isEmpty()) {
            Toast.makeText(this, "Add your own API key in AccuTerraMapsDemo/app/secure.properties as MAPS_API_KEY=YOUR_API_KEY", Toast.LENGTH_LONG).show()
        }
    
        // Get the SupportMapFragment and request notification when the map is ready to be used.
        val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment
        mapFragment?.getMapAsync(this)
    }

    override fun onMapReady(map: GoogleMap?) {
        val accuterraMapsToken = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc"

        map?.mapType = GoogleMap.MAP_TYPE_NONE

        var tileProvider: TileProvider = object : UrlTileProvider(256, 256) {
            override fun getTileUrl(x: Int, y: Int, zoom: Int): URL? {
        
                /* Define the URL pattern for the tile images */                
                val url = "https://maps.accuterra.com/v1/raster/accuterra-outdoors/$zoom/$x/$y.png?key=$accuterraMapsToken"
                return if (!checkTileExists(x, y, zoom)) {
                    null
                } else try {
                    URL(url)
                } catch (e: MalformedURLException) {
                    throw AssertionError(e)
                }
            }
        
            /*
             * Check that the tile server supports the requested x, y and zoom.
             * Complete this stub according to the tile range you support.
             * If you support a limited range of tiles at different zoom levels, then you
             * need to define the supported x, y range at each zoom level.
             */
            private fun checkTileExists(x: Int, y: Int, zoom: Int): Boolean {
                val minZoom = 8
                val maxZoom = 18
                return zoom in minZoom..maxZoom
            }
        }
        
        val tileOverlay = map?.addTileOverlay(
            TileOverlayOptions()
                .tileProvider(tileProvider)
        )

        val centerPoint = LatLng(39.6500327, -105.0053989)
        map?.moveCamera(CameraUpdateFactory.newLatLngZoom(centerPoint, 10f))
    }
}
import UIKit
import GoogleMaps

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let accuterraMapsToken = "eXePb4yLgQNHuICMzEylJWfs1A74RiKqGKytyMi7W9IFin0dlc"
        
        let camera = GMSCameraPosition.camera(withLatitude: 39.375239, longitude: -104.861077, zoom: 14.0)
        let mapView = GMSMapView.map(withFrame: view.frame, camera: camera)
        
        let urls: GMSTileURLConstructor = { (x, y, zoom) in
          let url = "https://maps.accuterra.com/v1/raster/accuterra-outdoors/\(zoom)/\(x)/\(y).png?key=\(accuterraMapsToken)"
          return URL(string: url)
        }

        let layer = GMSURLTileLayer(urlConstructor: urls)
        layer.zIndex = 100
        layer.map = mapView
        
        view.addSubview(mapView)
    }

}