Skip to content

Commit

Permalink
feat(find): rework caching
Browse files Browse the repository at this point in the history
refs #97
  • Loading branch information
evansiroky authored Nov 23, 2019
2 parents e0a0117 + bc80513 commit b727fc6
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 16 deletions.
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ The most up-to-date and accurate node.js geographical timezone lookup package.

## Usage

```javascript
var geoTz = require('geo-tz')
```js
const geoTz = require('geo-tz')

geoTz.preCache() // optionally load all features into memory
geoTz(47.650499, -122.350070) // ['America/Los_Angeles']
geoTz(43.839319, 87.526148) // ['Asia/Shanghai', 'Asia/Urumqi']
```
Expand All @@ -27,11 +26,27 @@ Returns the timezone names found at `lat`, `lon`. The timezone names will be th

This library does an exact geographic lookup which has tradeoffs. It is perhaps a little bit slower that other libraries, has a larger installation size on disk and cannot be used in the browser. However, the results are more accurate than other libraries that compromise by approximating the lookup of the data.

The data is indexed for fast analysis with automatic caching with time expiration (or optional an unexpiring cache of the whole world) of subregions of geographic data for when a precise lookup is needed.
The data is indexed for fast analysis by caching subregions of geographic data when a precise lookup is needed.

### geoTz.preCache()
### geoTz.setCache(options || false)

Loads all geographic features into memory in an unexpiring cache. This has tradeoffs. More memory will be consumed and it will take a little longer before the program is ready to start looking up features, but future lookups will be a lot faster - especially for areas which haven't had a lookup in a while.
By default, geoTz uses a cache that expires after 60 seconds. This method can be used to change the caching behavior using the following options:

* `expires` - time in miliseconds to expire a cached file (cannot be used together with `store`)
* `preload` - if set to true will attempt to cache all files (slow startup time and requires lots of memory)
* `store` - offload the cache to a custom storage solution (must be compatible with the Map api)

Examples:

```js
geoTz.setCache(false) // disable caching
geoTz.setCache({expires:120000}) // cache expires after 2 minutes
geoTz.setCache({expires:0}) // cache never expires
geoTz.setCache({expires:0,preload:true}) // cache never expires and preloads all files

let map = new Map();
geoTz.setCache({store:map}) // pass a Map-like storage object
```

## Limitations

Expand Down
48 changes: 38 additions & 10 deletions lib/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,52 @@ var path = require('path')

var geobuf = require('geobuf')
var inside = require('@turf/boolean-point-in-polygon').default
var Cache = require('timed-cache')
var timedCache = require('timed-cache')
var Pbf = require('pbf')
var point = require('@turf/helpers').point

var tzData = require('../data/index.json')
const {getTimezoneAtSea, oceanZones} = require('./oceanUtils')

let featureCache = new Cache()
let featureCache;

/**
* A function that will load all features into an unexpiring cache
* Set caching behavior.
*/
var preCache = function () {
const _eternalCache = {}
featureCache = {
get: (quadPos) => _eternalCache[quadPos]
function cacheLevel(options) {
if(options === false || options === null) {
featureCache = {
get: function() { return undefined },
set: function() { return undefined }
}
} else if(options.store && typeof options.store.get === "function" && typeof options.store.set === "function") {
featureCache = options.store
} else if(typeof options.expires === "number") {
if(options.expires === 0) {
featureCache = new Map()
} else {
featureCache = new timedCache({ defaultTtl: options.expires })
featureCache.set = function(k,v) {
featureCache.put(k,v)
}
}
}
if(options.preload) {
preCache()
}
}

cacheLevel({expires:60000})

/**
* A function that will load all features into an unexpiring cache
*/
function preCache() {
// shoutout to github user @magwo for an initial version of this recursive function
var preloadFeaturesRecursive = function (curTzData, quadPos) {
if (curTzData === 'f') {
var geoJson = loadFeatures(quadPos)
_eternalCache[quadPos] = geoJson
featureCache.set(quadPos,geoJson)
} else if (typeof curTzData === 'object') {
Object.getOwnPropertyNames(curTzData).forEach(function (value) {
preloadFeaturesRecursive(curTzData[value], quadPos + value)
Expand Down Expand Up @@ -129,7 +152,7 @@ var getTimezone = function (originalLat, originalLon) {
var geoJson = featureCache.get(quadPos)
if (!geoJson) {
geoJson = loadFeatures(quadPos)
featureCache.put(quadPos, geoJson)
featureCache.set(quadPos, geoJson)
}

var timezonesContainingPoint = []
Expand Down Expand Up @@ -161,4 +184,9 @@ var getTimezone = function (originalLat, originalLon) {
}

module.exports = getTimezone
module.exports.preCache = preCache
module.exports.setCache = cacheLevel

// for backwards compatibility
module.exports.preCache = function() {
cacheLevel({expires:0,preload:true})
}

0 comments on commit b727fc6

Please sign in to comment.