Programming, electronics, lifestyle
В ноябре 2018 года меня вновь посетила идея создания древа моей семьи. Особенно на это подтолкнула оцифровка архивов Великой Отечественной Войны, в которой я нашел своих предков:
До этого я пытался как-то записать все на бумаге, но буквально через месяц это было утеряно. В связи с чем в этот раз я решил хранить данные в электронном формате.
Прежде чем делать что-то своё, я:
К сожалению, я не нашел удовлетворяющего критериям сервиса. Пример сервиса:
Подавляющая масса была нацелена на создание простых вертикальных деревьев, и, конечно, не учитывала горизонтальные связи + сложные ситуации, с которыми я и столкнулся, о чем напишу далее.
В последствии я решил сделать универсальный инструмент для хранения, отображения реальной картины родословной.
Для начала я принял соглашение о том, что связью будет являться указание на родителя. Тк это обуславливается природой людей. И всегда у каждого человека будет мать и отец. Удивительно, но даже тут есть исключительные ситуации о которых я напишу ниже.
Свое решение я начал с создания примитивной структуры данных, и сбора данных в таблицу Numbers
(Excel).
Поле | Ключ |
---|---|
ID | key |
Sex | s |
Last name or maiden name | surn |
Married name | marn |
First name | firn |
Second name | secn |
Date of birthday | bday |
Date of death | dday |
Father ID | f |
Mother ID | m |
Comment | com |
В этой модели данных единицей является некоторый человек с уникальным ID, у него могут быть ссылки на ID матери и отца. Также у каждого элемента есть поля которые его описывают: имя, дата рождения и смерти, пол, комментарий для дополнительной информации.
До этого в университете, в 2013 году в рамках хакатона и курсовой работы, я делал сервис для поиска родственных связей по базе жителей Самарской области. Мой интерес представляло – по данным места жительства, дате рождения, фамилии, имени и отчеству определить с некоторой вероятностью и отобразить на графе какую-то семью. Тогда для отображения графа я использовал библиотеку sigmajs. Эта библиотека написана на JavaScript и она удовлетворяла моему видению того, что я собирался сделать.
Просто перебрав в голове всех ближайших родственников, у меня получилось 26 человек. Конечно информация была не полной, но это был 1 день, когда я начал.
После я напряг бабушку вспоминать и узнавать всех наших предков и родственников, и начал всё активно записывать в свою таблицу. Уже к ночи следующего дня мы записали информацию о ≈100 людях. А также я добавил заполнение черным цветом, узлов умерших людей.
После чего мы начали показывать эту картинку нашим родственникам и просить их рассказать о тех, кого мы не знаем. Благодаря обильному общению через мессенджеры и телефонные звонки к следующему вечеру мы собрали информацию ≈200 наших родственниках.
Хочу заметить, что на графе отображены ближаишие родственники по горизонтали и их ближайшие предки, которых они помнят. Что естественно является не полной картиной при построении вертикального древа. Но уже на этом этапе стало понятно, что используемая библиотека построения графа не подходит для отображения дерева.
Буквально через 6 дней я попробовал с десяток разных библиотек на JS и выбрал библиотеку GoJS. Одна из причин по которой я это сделал было то, что я нашел пример для построения графа с автоматической визуализацией поколений. Это позволяло автоматически расположить людей на графе, а также визуализировать связи под прямым углом.
Однако после этого и начали проступать основные проблемы отображения, которые раньше были не видны.
Дальнейший поиск информации о родственниках легко продолжать горизонтально (по своему поколению), однако чтобы искать вертикальные связи (искать своих предков) нужно заниматься изучением архивов, баз данных, кладбищ, изучением родословных и фамилий. Вот кстати два сервиса которые могут в этом помочь: vgd.ru и geno.ru.
Нужно заметить, что GoJS
подошла на стадии создания прототипа, однако ввиду неадекватной цены лицензии в 3495$ за 3 года, использовать её в конечном решении я не планирую. Однако в её примерах содержатся неплохие алгоритмы и принципы, которые можно позаимствовать.
Для начала, если не рассматривать сложных случаев, стандартные алгоритмы GoJS
для расположения вершин не справлялись:
При рисовании ребер под 90 градусов многие ребра накладывались друг на друга, и не сдвинув ребра, было не понять откуда какое выходит. Поэтому я сразу исправил их рисование на прямые линии.
Некоторые связи улетали не в те поколения. Дети как бы перепрыгивали сильно вниз.
Граф переплетался и накладывался, даже в тех случаях, где можно было этого не делать.
Стало очевидно, что нужны правила отображения такого графа. И вообще задача создания алгоритма расположения вершин – основная работа.
Также я добавил функции для импорта в формате .csv
, также экспорта в .svg
, .json
, а хранение данных перенес в LocalStorage
браузера, таким образом перезагружая страницу, данные не терялись и отпала необходимость использовать БД.
А также я понял, что:
Numbers
не удобно, нужен интегрированный веб-интерфейс.Эти задачи я пока отложил и начал думать про реализацию нормального отображения графа, а для этого сначала нужно побороть теорию.
Писать алгоритм я начал с задания ограничений и изучения теории.
Наука, занимающаяся выявлением родословных закономерностей, обобщением сведений о происхождении человека, его семьи, называется – генеалогия
. Очень хорошо о ней написано в этой статье, я же изложу выжимку.
Генеалогия содержит 3 закона:
Также есть много материалов по построению генетических деревьев все это ищется по слову genogram
. Основная часть этих материалов про построение этих деревьев с помощью софта EdrawMax
:
Также я принял собственные правила отображения:
Для решения проблем планарного отображения рассмотрим несколько ситуаций:
Инцест (1 случай - меж-поколений)
Касаемо графа справа, очень долго думал через сколько колен максимальна возможна эта офера. Но не стал визуализировать, остановился на одном 😅
Инцест (2 случай - внутри-поколения)
Много/мульти-жёнство/-мужество
Такой пример достаточно распространен в жизни, и встречается в моем древе 👇
Однополые семьи, усыновление (удочерение), искусственное зачатие, смена пола, замороженный генетический материал – те случаи, которые могут сломать вообще всю логику.
Вместе с тем как быстро граф расширяется в ширь, стало понятно, что отображать такую структуру становится сложно. Нужно как-то её ограничивать. Для этого я придумал следующий алгоритм 👇
PHASE “A” Для узла-точки-входа отображаем всех прямых предков.
PHASE “B” Для всех элементов на графе отображаем всех прямых потомков, при этом для каждого потомка отображаем вторых родителей (на графе отмечены серым кружком).
Для всех вторых родителей повторяем PHASE “A”
Повторяем PHASE “B”
Переходим к 3.
Это первая статья описывающая часть моей работы, за которую я:
Для тех, кто дочитал бонусом прилагаю полученный прототип.
PS работает не во всех браузерах, лучше просматривать с компьютера.
При нажатии на ссылку будет всплывающее окно, если согласиться, то загрузитсяJSON
файл с моим деревом (отображение происходит не моментально, обычно это занимает ≈2 секунды).
Также можно загрузить своё древо, в форматеcsv
илиjson
, согласно модели данных выше.
Спасибо за прочтение.