Timeline
– показывает соответствия событий
романа реальному ходу исторических событий.
Каждой
главе, присвоен временной диапазон или какая-то конкретная дата.
Вся эта
информация содержится в одном из столбцов исходной таблицы.
Для выделенных
12 паттернов были написаны обработчики и
данные столбца нормализованы к
единому формату:
convertDate: function (date, year) {
// чистим строку с предполагаемой датой
date = util.clearValue(date);
// может быть указано, что даты нет или ячейка будет пуста
if (['нет', ''].indexOf(date) !== -1) {
return null;
}
// в качестве даты может быть обычный формат 28.01.1812
if (this.tests.testDate(date)) {
return this.getDate(date);
}
// убираем слова паразиты, которые никак не влияют на процесс
распознания даты
var dateParts = _.without(date
.split(/[\s-]/g), 'с', 'по', 'и', 'за', 'романа');
var patternId = '';
// ищем для каждой подстроки возможный паттерн и собираем id паттерна
// конечный id имеет следующий вид "42423", не найденные паттерны
содержат 9-ки
for (var key in dateParts) {
patternId += this.getPatternId(dateParts[key]);
}
// если паттерн найден, возвращаем дату или диапазон дат
if (this.patterns[patternId]) {
return this.patterns[patternId](dateParts, year);
}
}
Главы,
которые соответствуют историческому периоду, описаны в другом столбце.
Их первоначальное хаотическое представление
было упорядочено:
getChapters: function (chapters) {
// может быть указано, что глав нет или ячейка будет пуста
if (['нет', ''].indexOf(this.clearValue(chapters)) !== -1) {
return [];
}
return _.chain(this.clearValue(chapters, true)
// чистим ничего не значащие точку или запятую в конце строки
.replace(/[\.\,]$/g, '')
// разделителями выступают, как точки так и запятые
.split(/[\.\,]/g))
.map(chapter => {
// диапазоны указываются через -
var chapters = chapter.split('-');
if (chapters.length == 1) {
return chapters[0];
}
return _.invoke(_.range(Number(chapters[0]), Number(chapters[1]) + 1), 'toString');
})
// сворачиваем внутренние массивы на один уровень
.flatten()
.uniq()
.value()
;
}
Исходные
данные были представлены 4-мя таблицами.
Основная
таблица описывает три рода событий:
-
только
в романе,
-
только
в истории
-
смежные события.
Одно
смежное событие описано двумя строками данных, а связь проставляется через поле
related_book_id в смежном историческом событии.
Так как в
качестве основного таймлайна была выбрана книга, таблицу пришлось вывернуть в
два зависимых списка, связанных по полю related_book_id.
Все
инфографические работы покрыты ссылками на роман, сделано это для того, чтобы не
отрываясь от кассы перейти и продолжить чтение романа, начиная с выбранной
цитаты.
Книга в
свою очередь содержит обратные ссылки на инфографику.
В сопоставлении ссылок из инфографики на нужный фрагмент
в книге единственным ключом для получения ссылки
являлась цитата и был
применен алгоритм неточного сравнения строк. Точное
сравнение дало только 30% соответствия.
Сетка
соответствия романа истории оказалась слишком громоздкой - 350 временных
засечек.
События
романа развиваются с 1805 по 1820 год, но покрытие событиями неравномерное. Половина
из них приходится на скромный период в 1812 году.
Было принято решение провести масштабирование лет от количества событий.
Алгоритм: 35% высоты таймлайна приходится на все года в равных долях, оставшиеся
65% делятся между годами с событиями, пропорционально количеству событий.
Многие исторические события, проходили последовательно с небольшим разрывом во
времени, чтобы избежать смешения их, точки событий немного сдвигаются вниз
относительно своих начальных позиций, если происходит наезжание точек.
Исторические события, приходящиеся на начало года заползали на разделительные
линии, как и в предыдущем пункте их немного сдвинули вниз относительно
начального положения.
В качестве системы сборки
использовался
webpack.
В репозитарии Германа Гурова на
GitHub:
|