UK Map App

How To Set Up A Local Map Cache

This page describes how you can set up your local network to make downloading maps in UK Map faster - if you download the same maps on multiple devices - by caching the map data files. This will help if you're an organisation with multiple devices running UK Map, or if you're an individual or household with multiple iPhones and iPads. It works by caching a copy of the map data on a local server the first time it is downloaded, so that subsequent downloads will be much faster.

This is quite a complex process; the target audience for this page is network administrators and individuals with similar technical experience.

I'll describe the specific steps that I have used to make this work in my own home-office network; you may choose to copy my setup, or to achieve the same results in another way to suit your environment.


A Caching HTTP Proxy

You need to set up a caching HTTP proxy on your network.

In larger organisations you may already have such a proxy, in which case you just need to check that its configuration is suitable. If you don't have one, there are several options. Perhaps the best-known is Squid. I have chosen to use NGINX, which is a light-weight web server with some proxy and caching support; it is not really suitable to use as a general purpose caching proxy, but it works well for this specific case and is easy to set up.

I am running my cache on an ODROID-HC1 with a 1 TB SSD; this is a small Linux network attached storage device on which I run Armbian.

Here is a fragment of my NGINX configuration, which provides the caching proxy on port 82:

/etc/nginx/sites-available/proxy_cache

proxy_cache_path /var/cache/nginx/proxy_cache
                 use_temp_path=off inactive=18M levels=1:2 keys_zone=cachekeys:10M max_size=150G;
# inactive=18M (18 months) means that cached files that have not been used will be deleted
# after 18 months (M=Month, m=minute). This is considerably greater than the default. 18 months
# suits how often the map files are updated, typically approximately annually.
# (Note that at the time of writing I have not confirmed for how long the files actually are kept!)
# The max_size should be sufficient for all the UK Map maps files; the sizes are currently:
# Paid 10k:               11 GB
# Paid 25k:               18 GB
# Paid 50k:                5 GB
# Terrain data:                 72 MB
# Open Map Local:         48 GB
# Street View:            46 GB
# Vector Map District:    15 GB
# Total:               < 150 GB
# keys_zone specifies the name of a shared memory region in which the cache keys are
# stored and its size. Apparently 1 MB can store about 8,000 keys. The potential total
# number of files is 11,000 x 6 map types, which is < 80,000 files, so 10 MB should be
# sufficient.

server {
        # Provide the proxy on port 82:  (Make sure you're not using port 82 for anything else.)
        listen 82 default_server;
        listen [::]:82 default_server;

        resolver 8.8.8.8;  # Set this to the address of a DNS server, either local or global e.g. 8.8.8.8.

        proxy_cache cachekeys;  # Match the keys_zone in the proxy_cache_path above.

        # By default, HEAD requests are converted to GETs; disable that.
        proxy_cache_convert_head off;

        # By default the cache key doesn't include the method; it must when we are not
        # converting HEADs to GETs.
        proxy_cache_key "$request_method$scheme$host$request_uri";

        # I'm not convinced that cache key is correct and/or optimal; isn't the scheme and host
        # included in the URI? Aren't the args part of the URI? Should this use $host or
        # $proxy_host? What about $uri vs $request_uri? Examples use $uri$is_args$args;
        # does request_uri include the ?args ?

        # Revalidate expired cached items (with If-None-Match hopefully), rather than
        # discarding and refetching; default is off surprisingly.
        proxy_cache_revalidate on;

        # Cache successful downloads for 18 months.
        # (I'm not sure how this interacts with proxy_cache_path inactive=18M above.)
        # Don't cache other response codes, e.g. 3xx, at least not for long; since those
        # codes aren't accompanied by large amounts of data there would be little performance
        # benefit to caching them, even if doing so were correct.
        proxy_cache_valid 200 18M;  # Beware M = Month, m = minute.

        # The default is 1.0, for some reason:
        proxy_http_version 1.1;

        # proxy_redirect is needed for reverse proxies to update the Location: header
        # in redirection responses. We don't need it for a forward proxy. It's not clear
        # that the default does the right thing, so turn it off:
        proxy_redirect off;

        location / {
                proxy_pass $scheme://$host;
        }
}

If you use a different caching proxy than NGINX, note the comments in there about how long to cache for and the required size of the cache. Note that these downloads do not use https, nor do they require authentication, nor cookies. However there may be additional non-standard headers in the requests which you should pass on to the server with the initial request.


iOS Proxy Discovery and Configuration

In order for your iOS devices to actually make use of the cache, you need to provide and advertise the proxy configuration. This requires a proxy auto-config file.

Note that the caching proxy configuration above will cache content for anywhere; it's the proxy auto-config file that instructs the device to use it only for the UK Map data, and to get everything else direct from the origin. Here's an example file:

/var/www/wpad/wpad.dat

function FindProxyForURL(url, host)
{
  if (   shExpMatch(host,"*.maps.ukmapapp.com")
      || shExpMatch(host,"*.*.maps.ukmapapp.com")
      || host == "dtm.ukmapapp.com"
     ) {
    return "PROXY my_proxy_hostname:82; DIRECT";
  } else {
    return "DIRECT";
  }
}

Replace my_proxy_hostname with the hostname of your local caching proxy.

Note the domains that you should try to cache. The map data is served from subdomains (1 or 2 levels deep) of maps.ukmapapp.com; dtm.ukmapapp.com serves the terrain data. It is probably best not to try to cache the root ukmapapp.com as that contains the app's website, which has rather different characteristics than the maps e.g. it uses https.

Now you need to arrange for your server to serve that file. Here's an NGINX configuration fragment showing how I do that:

/etc/nginx/sites-available/wpad

server {
	listen 80;
	listen [::]:80;
	server_name wpad.my_domain;
	root /var/www/wpad;

	types {
		application/x-ns-proxy-autoconfig dat;
	}

	location / {
		try_files $uri =404;
	}
}

Replace my_domain with something suitable, which may be "local".

You need to arrange for this server to be reachable with the name "wpad" (for "web proxy auto discovery". In my network I run Dnsmasq as a local DNS server, and adding "wpad.my_domain" to the server's entry in /etc/hosts is sufficient for Dnsmasq to advertise this address. If you're not running a local DNS server, then you can probably configure Avahi to advertise the name using mDNS - though I understand that there are issues with advertising multiple names for the same server with mDNS.

A final, and perhaps optional, step is to also advertise the location of the proxy auto-configuration file using DHCP. Again I use Dnsmasq for DHCP, and I just need to add this to /etc/dnsmasq.conf:

dhcp-option=252,"http://wpad.mydomain/wpad.dat"

Now, ideally, your iOS devices would automatically detect all of this. Unfortunately it seems that iOS devices have proxy auto-configuration disabled by default. If you're a large organisation with managed devices, you can probably enable this proxy via your iOS management system. For the rest of us, it's necessary to enable it manually. In the Settings app, go to "Wi-Fi", and select your network. Scroll down to "HTTP PROXY", select "Automatic", and touch "Save". (You don't need to enter anything in the URL box. I often forget to touch "Save"!) You need to do this on all of your devices, and if you have multiple Wi-Fi SSIDs you'll need to do it separately for each of them.

What should now happen is:

  1. The iOS device should ask the DHCP server for the "option 252" WPAD URL and get back "http://wpad.mydomain/wpad.dat".
  2. If that doesn't work, the iOS device will try to construct a URL based on its own hostname. So if the iPhone is iphone.mydomain, then it should try http://wpad.mydomain/wpad.dat . If the device doesn't have a domain supplied by the DHCP server, it may consider its domain to be .local and look up wpad.local, or it may look up the unqualified hostname wpad (I've not tried to make that work).
  3. Note that at this point iOS may present a warning saying "UK Map would like to find and connect to devices on your local network". I don't know what happens if you press "Don't Allow".
  4. The iOS device now resolves the hostname wpad.mydomain using DNS or mDNS, connects to the web server on port 80, and retrieves the proxy auto-configuration file.
  5. All web accesses will now evaluate the proxy configuration function. For most accesses this returns "DIRECT" and the access completes as usual.
  6. Accesses to the UK Map map data will be sent to the caching proxy on port 82. On the first request for a file, it will fetch it from the normal server over the internet. It will then be saved. Subsequent requests will be sent from the cache.

Testing It

To test it out:


I hope that the above is useful. Please let me know if you try to make it work. The support I can offer is limitted but I will try to help if I can.