Skip to content

Instantly share code, notes, and snippets.

@johan
Last active March 22, 2018 10:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johan/28d2718997f6a84f71326d10ba359bb4 to your computer and use it in GitHub Desktop.
Save johan/28d2718997f6a84f71326d10ba359bb4 to your computer and use it in GitHub Desktop.
Dig up dates in/out of [the continental] US from your http://www.moves-export.com/ logs; handy for taking stock of your visa type travel history
/date-from.txt
/date-to.txt
/dates.txt
/export_dir.txt
/in-us.js
/json
/node_modules
/us.txt
/world.geo.json
#! /usr/bin/env pike
int main(int argc, array(string) argv) {
Calendar.Day d1 = Calendar.dwim_day(argv[-2]);
Calendar.Day d2 = Calendar.dwim_day(argv[-1]);
write("%{%s\n%}", d1->range(d2)->days()->format_ymd());
}
#! /usr/bin/env node
// requires node 8 - v8.9.4, for instance
const path = require('path');
const cwd = process.cwd();
const { polygonContains } = require('d3-polygon');
let countries = require('./world.geo.json/countries.geo.json').features;
function country(point) {
const index = countries.findIndex((country) => {
return country.geometry.coordinates.some(
(polys) => polys.some((poly) => polygonContains(poly, point))
);
});
if (index === -1) {
// console.warn(point);
return '???';
}
// reorder the array we test to have the last found country first:
if (index) {
countries = countries.splice(index, 1).concat(countries);
}
return countries[0].id;
}
const resolve = (fn) => path.resolve(cwd, fn);
const json_files = process.argv.filter(fn => /\.json$/.test(fn)).map(resolve);
// console.log(JSON.stringify(json_files, null, 2));
let dates = {};
json_files.forEach((fn) => {
const days = require(fn);
(days || []).forEach((day) => {
const countries = {};
(day.segments || []).forEach((seg) => {
const loc = seg.place && seg.place.location;
if (loc) {
const { lat, lon } = loc;
countries[country([lon, lat])] = true;
}
});
const date = day.date.replace(/^(\d{4})(\d\d)(\d\d)$/, '$1-$2-$3');
dates[date] = Object.keys(countries).sort();
});
});
let ordered = Object.keys(dates).sort().map((date) => {
return { date, countries: dates[date].join(' + ') };
});
ordered.forEach(({date, countries}) => {
console.log(`${date}: ${countries}`);
});
console.log('\ncountry detail:\n');
let non_empty = ordered.filter(({ countries }) => countries.length);
const show = (c) => c === 'not' ? ' not' : c;
let prior = non_empty[0];
console.log(non_empty.reduce(
({ ranges }, {date, countries}) => {
const last = ranges[ranges.length - 1];
if (JSON.stringify(countries) == JSON.stringify(last.countries)) {
last.tail = date;
}
else {
ranges.push({ countries, head: date });
}
return { ranges };
},
{ ranges: [{ countries: prior.countries, head: prior.date }] }
).ranges.map(
({ countries, head, tail }) => `${head}...${tail||head}: ${show(countries)}`
).join('\n'));
console.log('\nUSA vs not:\n');
dates = {};
json_files.forEach((fn) => {
const days = require(fn);
(days || []).forEach((day) => {
const countries = {};
(day.segments || []).forEach((seg) => {
const loc = seg.place && seg.place.location;
if (loc) {
const { lat, lon } = loc;
countries[country([lon, lat]) === 'USA' ? 'USA' : 'not'] = true;
}
});
const date = day.date.replace(/^(\d{4})(\d\d)(\d\d)$/, '$1-$2-$3');
dates[date] = Object.keys(countries).sort();
});
});
ordered = Object.keys(dates).sort().map((date) => {
return { date, countries: dates[date].join(' + ') };
});
non_empty = ordered.filter(({ countries }) => countries.length);
prior = non_empty[0];
console.log(non_empty.reduce(
({ ranges }, {date, countries}) => {
const last = ranges[ranges.length - 1];
if (JSON.stringify(countries) == JSON.stringify(last.countries)) {
last.tail = date;
}
else {
ranges.push({ countries, head: date });
}
return { ranges };
},
{ ranges: [{ countries: prior.countries, head: prior.date }] }
).ranges.map(
({ countries, head, tail }) => `${head}...${tail||head}: ${show(countries)}`
).join('\n'));
all: us.txt
date-from.txt:
read -p "What start date (on YYYY-MM-DD format)? " from && \
echo "$$from" > $@
date-to.txt:
read -p "What end date (today is $$(date +%Y-%m-%d))? " to && \
echo "$$to" > $@
export_dir.txt:
read -p "In what directory did you unpack moves_export.zip? " dir && \
echo "$$dir" > $@
dates.txt: gen-dates.txt.pike date-from.txt date-to.txt
pike $< `< date-from.txt` `< date-to.txt` > $@ || rm -f $@
world.geo.json:
git clone https://github.com/johan/world.geo.json.git
json: dates.txt export_dir.txt
rm -rf $@
mkdir json
dir=`cat export_dir.txt` ; \
for d in `cat dates.txt` ; do \
json="$$dir/json/daily/storyline/storyline_`echo $$d|sed s/-//g`.json" ; \
[ -f $$json ] && ln -s $$json json/$$d.json ; \
done
node_modules: package.json
yarn install
us.txt: in-us.js json node_modules world.geo.json
node $< json/*.json | tee $@
{
"dependencies": {
"d3-polygon": "^1.0.5"
}
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
d3-polygon@^1.0.5:
version "1.0.5"
resolved "https://npm.apple.com/d3-polygon/-/d3-polygon-1.0.5.tgz#9a645a0a64ff6cbf9efda96ee0b4a6909184c363"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment