Дедовский метод

Если вы жаждете грязных сексуальных игрищ, то немедленно отправляйтесь картировать деревья. В блогах OSM по этому поводу недавно был любопытный мануал, а в прошлом году я написал по этому поводу развернутую статью. Но ни в одной, ни в другой публикации не было сказано самого притягательного.

Когда вы укажете на плане местоположение дерева, запишете породу, окружность ствола, санитарное состояние и в очередной раз убедитесь, насколько уродлива схема тегирования деревьев в OpenStreetMap дело останется за измерением высоты. Конечно, можно использовать высотомер за сто евро или оценить высоту на глаз, но где же тут разврат?

Прислонитесь к дереву спиной, а после шагайте прочь в любом удобном направлении. Остановитесь примерно в той точке, где дерево, будь оно тут же свалено налетевшим штормом, хлестнуло бы вас по голове кончиком вершинки. Раздвиньте ноги, наклонитесь и постарайтесь через промежность увидеть вершину дерева. Если вершина не видна, пройдите еще немного вперед и повторите наблюдение. Если вершина слишком выступает — вернитесь назад, вы должны увидеть только самый кончик верхней ветви.

Когда искомое положение найдено, распрямитесь, а после испытайте настоящий оргазм, поскольку в этот момент расстояние от вас до ствола равно высоте дерева. И не надо вам знать ни теорему Фалеса, ни особенности лазерных дальномеров, результат работы которых ничуть не лучше.

Конечно, если ровно отмерить базис и глянуть на вершину через суунтовский высотомер, результат будет точнее, но уж больно это трудоемкий способ, да и работает не всегда. А уж про цены на дендрологические инструменты без слез не расскажешь.

Самое время, в рамках импортозамещения, перейти на старые проверенные дедовские методы. Тем более, что смотреть через промежность на вершины деревьев — это единственный полезный навык, который вы можете получить за десять лет обучения в Лесотехнической академии.

Закон первой маски

Когда покупаешь водку, просят вначале надеть маску а после показать паспорт. Потом удивляются количеству ковидных скептиков. Ладно, зафиксируем мысль о том, что маски нужны и скопления народа опасны. Но вот перед нами кадры демонстраций из Белоруссии. Где ожидаемая вспышка заболеваний? Пусть Лукашенко — злобный диктатор и все скрыл, но тогда почему до сих пор не лежат по койкам протестующие Хабаровска?

Число заболевших растет, но это не мешает задавать вопросы, первый из которых: насколько органичен такой рост? Где число заболеваний отражает естественную динамику развития болезни, а где результат вызван особенностями тестирования и подсчета?

Так совпало, что мне надоело в очередной раз проверять степенные распределения на соответствие закону Бенфорда. Полтора века назад Саймон Ньюком изучая потертости страниц в сборниках логарифмических таблиц обнаружил любопытный феномен, который спустя шестьдесят лет обобщил Френк Бенфорд: В экспоненциальных распределениях каждое третье число начинается с единицы. Точнее, вероятность встретить единицу 30.1, двойку 17.6, тройку 12.5 и далее согласно разработанной Бенфордом формуле.

Этому закону соответствует огромное количество экспоненциальных (и как обобщенный случай — степенных) распределений. Учитывая закон и все ограничения, с помощью распределения Бенфорда можно проверить данные на естественность, поэтому использовать его приходится часто. Для автоматизации процесса я написал небольшую программу, которая проверяет частоту первых цифр из вашего распределения на соответствие распределению Бенфорда и в качестве демонстрации подключил статистику по заболеваемости ковидом в регионах России. Можете проверять любые ваши данные, хоть результаты голосования, хоть статистику по зарплате, хоть общее проективное покрытие oxalis acetosella, как в моем случае.

Ну а что-же ковид? Оказалось, что в распределениях суммарного количества заболевших наибольшие отклонения от распределения Бенфорда наблюдаются в Москве с областью и соседствующими регионами, Северном Кавказе, Татарии и Башкирии, Туве, Чукотке и Камчатке.

Распределения по количеству выздоровевших наиболее соответствуют закону Бенфорда вдоль границы с Казахстаном, юго-востоку (Приморье и Сахалин) и Северо-Западу (Карелия и Мурманская) России.

Распределения по количеству погибших от коронавируса наиболее близки к распределению Бенфорда в юго-западных, западно-уральских регионах, частично на юге Западной Сибири и в Приморье.

У меня есть предположения о причинах таких географических особенностей, но я бы хотел услышать комментарий специалиста. И это не потому, что я диссидент, а ровно напротив: я сторонник самого жестокого карантина: с применением боевого оружия и превращением всех институтов в шарашки.

Каждый день пандемии ждешь прорыва научной мысли, а вместо этого слышишь рекомендации намотать на лицо тряпку и сидеть по домам.

P.S. Спасибо всем, кто откликнулся на призыв о поиске сырых данных по заболеваемости в регионах.

Рельефный эксперимент

Продолжая эксперименты в области картографического дизайна задумался о применимости отмывки рельефа. Для чего нам все эти астеры и эсэртээмы? С ними обычно красивее, но главная причина использования отмывки — заполнить пустоты на карте. А что если использовать этот метод в нестандартной ситуации?

В 2008 году я занимался геоботанической съемкой на территории Полярно-Альпийского сада-института. Это тот самый институт у подножия Хибин, откуда пошел гулять по стране знаменитый борщевик Сосновского. С тех времен у меня хранилась помятая схема дендрария, которую давно пора было отрисовать заново.

Дендрарий разбит на зоны по географическому признаку. В одном месте посажены деревья из Северной Америки, в другом из Восточной Сибири, в третьем еще откуда-то. Почему бы не сопроводить подпись региона характерным рельефом? Для этого потребовалось отрисовать схему начисто, а после скачать SRTM-ки для каждого из регионов. После совмещения, каждая зона на карте должна была бы выглядеть по-особенному.

Задумка хорошая, но итогом стало абсолютное фиаско. С визуальной точки зрения весь рельеф можно свести к двум типам: либо он выражен, либо нет. Скалистые горы ничем не отличаются от Камчатки, а те, в свою очередь можно спутать с Альпами или траппами Восточной Сибири. Исключение составляет только Якутия с ее субпаралелльными формами рельефа. Можно немного поиграть с прозрачностью отмывки или масштабом, но это грязный подход, с тем же успехом можно и топографическую карту подложить.

Из этого следует любопытный вывод: отмывка отражает частные особенности рельефа, но вцелом мало сообщает о характере региона. Если бы не привязанные к рельефу объекты на карте (прежде всего реки), можно было бы подменять одну отмывку другой. Например, для изображения Кавказа использовать марсианскую демку. То, что мы хотим показать в качестве особенности территории, на самом деле не более, чем эстетически приятный шум.

А еще не следует годами хранить кипу бесполезных бумаг. Лучше пусть они послужат для эксперимента, пусть даже неудачного.

Нейросети

Нет более модного направления сейчас, чем машинное обучение. О связанном с ним «Великом и опасном будущем» говорят даже представители глянцевых журналов и музыканты неоднозначных стилей. Дескать, еще чуть-чуть и нейросеть подомнет под себя человечество.

С одной стороны, я давно утверждаю, что это уже произошло. С тех пор, как сложность интернета превысила сложность мозга (была такая статья лет пять назад), глупо уповать на свободу личности. С другой стороны, не могу не вспомнить эпизод из старой книги, которую читал еще в школе. Там главный герой путешествовал в лесах Африки вместе с подручным аборигеном. Однажды абориген показал герою на синюю бабочку и сказал, что скоро пойдет дождь. И дождь пошел. «Как ты понял?» — спросил главный герой. «А хрен его знает» — ответил абориген, «только крылья у этой бабочки всегда тускнеют перед дождем».

Загадка решалась просто: цвет крыльев бабочки зависел от влажности воздуха. Перед дождем влажность воздуха повышалась и крылья тускнели. Любая нейросеть работает по принципу этого аборигена: верно предсказывает результат, но неспособна объяснить механику процесса. Прямо новая религия. Подставьте вместо слова «нейросеть» слово «бог». Эй, Яндекс-погода, как вы узнали, что дождь будет? — Ну, об этом говорит наш бог. К черту уравнения Навье-Стокса, закон Бойля-Мариотта, силу Кориолиса и прочую чепуху. Ответы на все вопросы даст нейросеть.

Машинное обучение — это великий, но переоцененный инструмент. Дело даже не в проблеме неопределенности Геделё для нейросетей (кому интересно — гуглите исследование в прошлогодних номерах Nature). Нейросети — это инструмент, что-бы видеть. Инструмента, что-бы объяснять пока не изобрели.

А как мы станем работать с исключениями? Сегодня мне рекомендуют музыку, которую я терпеть не могу, а завтра нейросеть будет выписывать таблетки. Как оценить отличие сущности от абстракции (тут я на стороне квир-тусовки)? Если не ясно выразился: нет ни одной нейросети, которая умеет отличать котиков от собачек, а вот фотографии котиков от фотографии собачек — сколько угодно.

А главное, мы еще посмотрим, что выдаст условный Тензорфлоу, после того, как начнем работать со всюду непрерывными, но нигде не дифференцируемыми функциями.

Нейросеть сможет заменить человека только когда сама станет человеком. А до тех пор, я скорее поверю, что топор заменит плотника. Но это не луддизм, а напротив — предложение видеть в ML нечто большее, чем игра в составление перцептронов.

Картографический экзерсис

Нет ничего сексуальнее тригонометрии в необычных местах. Например, представление двумерного массива в качестве суммы квадратов синуса и косинуса. Это позволяет извлечь угол, который мало что дает, но невероятно притягивает. Или взять индекс NDVI. В конце семидесятых Ричардсон и Виганд предложили перпендикулярный вегетационный индекс — по сути бесполезная фигня, но какой полет мысли!

Или взять перпендикулярную раскраску слоя в гисах. Обычно одноканальные растры красят примитивно: малые значения красненьким, а большие зелененьким или наоборот. Но иногда имеет смысл одним цветом выделить краевые значения, а другим центральные. Сделать это можно разными способами, начиная от ручного задания диапазона, заканчивая вычислением модуля разницы текущего и среднего пикселя. Но все это так грубо, что даже противно. Другое дело — подключить тригонометрию.

Представьте, что значения яркости в канале соответствуют величине некоторого мифического угла. Сам этот угол пусть никого не интересует, важно лишь то, что в прямоугольном равнобедренном треугольнике оба острых угла равны сорока пяти градусам. Это значит, что нормализовав значения яркости к диапазону 0-90, мы получим пересечение графика синуса и косинуса яркости для значения 45. Следовательно, чем ближе значения яркости к медиане, тем ближе значения тангенса яркости к единице.

В практическом смысле это дает возможность выделять объекты со средними значениями. Например, поля на растре вегетационного индекса. Более того, немного поиграв с настройками диапазона отображения или добавив в расчеты побольше степеней, мы можем неплохо отсеивать объекты с резко отклоненными значениями, например водоемы.

Много ли это дает в реальной работе? Да почти ничего. Но боже мой, как же это сексуально.

Обработка дендрологических кернов

Сел я после напряженного дня немного отдохнуть и поработать. Обрабатываю свои образцы, покуриваю трубку и наслаждаюсь напитком. И тут мысль. Почему бы не включить камеру? Добавим в этот прекрасный вечер небольшой гедонистический рассказ о технологии обработки образцов древесины.

Подкаст натуралиста. Использование данных OpenStreetMap

Этот выпуск предназначен для тех, кто решит использовать геоданные OSM в первый раз. Я не обсуждаю историю проекта, успехи, недостатки, OSMF и прочее — это тема отдельного разговора. Здесь касаюсь лишь вопросов, которые чаще всего мне задают.

В подкасте упоминаю поисковик по базам данных от гугла: datasetsearch.research.google.com, форум OSM: forum.openstreetmap.org и отличный чат русскоязычного сообщества в телеграмме: @ruosm. Кроме этого существует канал с обобщением текущих событий в OSM: @ruosm_news и канал Ильи Зверева: @shtosm. В этих источниках вы найдете если не все, то по крайней мере ссылки на все.

Если будут вопросы или замечания по теме использования OSM, уделю им время в начале нового выпуска, как сделал в этот раз с R. В следующий раз планирую поговорить о животных. Репосты и донейты чрезвычайно помогут ускорить этот процесс. Ссылка для донейтов прежняя:

Якубович

Окружающий мир — удивительная дрянь, но в нем достаточно забавных штук, которые могут скрасить жизнь, особенно если уметь такие штуки находить. Делать это несложно, главное помнить, что самое интересное чаще всего связано с эмергентностью. Объедините несколько различных явлений и у вас родится странный Франкенштейн неизвестной витальности.

Возьмем математику. Если объединить между собой теорию вероятностей и топологию, можно за несколько часов вывести альтернативную формулу определения вероятности. Достаточно вспомнить, что оценка вероятности связана с измерением, а значит привычная формула P=m/n — лишь частный случай измерения одномерного множества. В общем виде к формуле оценки вероятности следует добавить степень, которая соответствует размерности Хаусдорфа-Безиковича. А единичная метрика — лишь отношение величины всего пространства к величине наблюдаемого подпространства. В результате мы получаем очень простой инструмент, который помогает оценить математическое ожидание генеральной совокупности по неполным данным.

Или вот другой пример эмергентности. Если к прошлому наблюдению добавить зиму, хандру и бутылку коньяка, возникает странный кусок кода под названием Yacubovich.js. Это калькулятор, который приближенно оценивает средние значения по неполным данным. Например, у вас есть данные за неделю, а необходимо среднее значение за месяц. Укажите размер генеральной совокупности (n -> 31), введите данные, которые есть, нажмите ‘r’ и результат готов. Фактически это обычная экстраполяция, только с небольшой фичей, которая повышает однозначность и точность.

Вероятно, подобного эффекта можно добиться и другим способом, но сомневаюсь, что вы найдете более элегантное решение. Даже банальная модификация метода Херста на порядок более трудоемка. И еще неизвестно, что такая модификация даст.

С другой стороны, всегда можно добавить еще одну бутылку коньяка. А эмергентное объединение различных явлений позволяет находить в этом мире довольно много забавных штук.

Картографирование деревьев в городе

Картографирование деревьев в городе

Практикующие ботаники используют разные методы городской дендросъемки. Выбор зависит от исходных данных и ожидаемого результата. Самая трудная работа — обследование кгиоповских объектов, где от ботаника требуют подробную информацию о каждом дереве: породу, вид, местоположение, диаметр кроны и ствола в двух местах, контур кроны на плане, повреждения, заболевания, особенности (вплоть до ширины дупла и угла наклона ствола) и другую информацию. На другом конце списка — сбор данных для расчета восстановительной стоимости. Это ситуация при которой деревья все-равно вырубят под какую-нибудь стройку и необходимо лишь оценить нанесенный ущерб.

Если выйти за границы массовых методов, с одной стороны стоит добавить ультразвуковое обследование стволов и прочую фантастику, а с другой — картирование в OpenStreetMap, которое обычно даже не картирование, а так, «заодно дерево отметил». Сейчас в базе OSM около тринадцати миллионов деревьев, но мануала по полевому маппингу и обработке данных до сих пор нет. Появится он скорее всего не скоро (если вообще появится), поэтому я решил изложить опыт десяти интенсивных дней полевого маппинга деревьев, в результате которого удалось увеличить количество точек natural=tree на сотую долю процента. Это примерно полторы тысячи деревьев. Однако, не так важны сами деревья, как обнаруженная ущербность нынешней схемы тегирования городской растительности.

Для выхода в поле необходимо минимум две вещи: знание видов и полевые материалы. К сожалению, первое из мануалов не узнать: даже если выучить все определители, умение отличить один вид от другого приобретается только с опытом. Однако, если есть сомнения, рекомендую особое внимание перед выходом обратить на вид листьев, коры и самое главное: тип ветвления побегов для основных городских деревьев. Для Санкт-Петербурга это клен остролистный, липа, дуб, вяз, ясень, береза, тополь, осина, черемуха, рябина, ива, ольха серая и ольха черная, боярышник, вишня, яблоня, конский каштан, ель, сосна, лиственница, пихта и туя. Знание этих пород поможет вам в девяти случаях из десяти.

Полевые материалы каждый готовит под себя. Многие ботаники используют карту и бланк, но я убежден в недальновидности такого подхода. Если вам не требуется вносить много данных о каждом дереве — лучше сразу писать на карте. Это уменьшает количество работы, и снижает вероятность ошибки, поскольку пропадает потребность в проставлении номеров на карте и в бланке. Если данных много (как при обследовании объекта, который охраняет КГИОП), то на карте стоит лишь указывать местоположение дерева, его номер и контур кроны, а всю остальную информацию записывать на диктофон. Я указывал только местоположение, породу, высоту, диаметр, количество стволов и санитарное состояние по четырехбалльной шкале: такое количество данных можно вносить без всякого диктофона.

Полевые карты изготовил в QGIS, загрузив данные через QuickOSM — это такой модуль overpass для QGIS. Главное требование к полевым материалам — карты должны быть бледными и содержать только нужную информацию, в моем случае, это дома с номерами, дороги с подписями улиц и заборы.

Карта до выхода в поле

Кроме этого я отметил на карте границы зеленых насаждений общего пользования из питерского RGIS-a. Последнее я не рекомендую повторять, поскольку процедура сомнительна в плане лицензионной чистоты и помогает лишь ориентироваться (и то не всегда). Я спокойно признаюсь в этом по двум причинам: во-первых, никакие данные из посторонних источников в OSM не внесены, границы RGIS только помогают ориентироваться в больших дворах (вот аналогия: можно ли вносить название улицы на которой ты стоишь если ты пришел к ней используя условные карты от Google). Во-вторых, согласно OSM-Wiki эти данные доступны как общественное достояние (public domain). В любом случае, не так страшно использовать чужие данные, как умалчивать об этом.

Я работал в конце декабря (нашел время), что накладывало ряд сложностей. Короткий световой день, холод, затрудненное определение видов и полные газоны собачьих фекалий, которые в такую погоду не разлагаются неделями. Но хуже всего дождь от которого намокает карта. Дабы избежать простоев из-за непогоды и облегчить процесс письма я использовал планшетку для спортивного ориентирования. Она крепится к телу как столик у продавца папирос в начале двадцатого века, складывается при необходимости и позволяет накрыть карту или хотя-бы ее часть прозрачной пленкой. Единственный недостаток — видя эту штуку дворовые бабки проявляют повышенное внимание к картографу и отвлекают от работы. Еще немного устает спина, но это все-равно лучше любых испытанных альтернативных методов.

Планшетка для спортивного ориентирования

Планшетка для спортивного ориентирования

Размер планшетки определяет формат карт, в моем случае A5. По многолетнему опыту могу сказать, что это наиболее удобный формат для полевых работ. Главное, не забыть про обзорную карту:

Обзорная карта

Обзорная карта

Полевая работа не представляет большой сложности если не брать в расчет физиологические неудобства. Желательно отмечать деревья так, что-бы карта была ориентирована на север. Это не принципиально, но облегчает обработку. Важно меньше размышлять и больше делать. Для размышлений есть подготовительный этап, в поле это приводит к потерянному времени и ошибочному результату. Выбираете первое дерево, определяете направление движения и отмечаете на карте точки — местоположения ближайших 5-10 деревьев. Далее идете от дерева к дереву и для каждой точки записываете что-то типа «2л22+28у24», где 2 — количество стволов, л — липа, 22+28 — диаметры стволов на высоте груди в см, у — удовлетворительное состояние, 24 — высота в метрах.

Очень важно сохранять бланки в чистоте — вносить минимум исправлений, а те, что неизбежны не закрашивать, а просто аккуратно и однообразно зачеркивать. Иначе потом предстоят отдельные мучения по дешифрированию написанного. Удивительно, но этим банальным правилом пренебрегает огромное количество специалистов.

Информация на карте

Таким способом в день удается картировать около трехсот деревьев. Летом больше, но все-равно после третьей сотни наступает усталость и темпы работы заметно снижаются. После заполнения, карта выглядит так:

Карта после заполнения

Карта после заполнения

Здесь красным обведены границы — это уже результат камеральной обработки. Придя в тепло я прорисовываю границы съемки красным, здания черным и нумерую все деревья по порядку проводя через каждое дерево ходовую линию зеленого цвета. Важно, что-бы линия нигде не пересекала себя, а первое, последнее и каждое десятое дерево были подписаны. В этом вопросе необходима внимательность, поскольку ошибка или пропущенное дерево потом создадут много хлопот.

Карта с границами, зданиями и номерами

Теоретически, данные уже можно вносить в OpenStreetMap, но к сожалению OSM, который зародился как проект полевого картографирования, последний десяток лет развивается в направлении диванного маппинга. Вносить полевые данные с помощью JOSM или ID — это неоправданная трата сил и времени, поэтому прибегнем к дополнительным инструментам.

Первым делом вносим данные о деревьях в любой редактор, текстовый или табличный. Я использую WPS — это аналог Excel для линукса. Путем нехитрых манипуляций разбиваем формулу вида «2л22+28у24» на пять столбцов (количество, порода, диаметр, санитарное состояние и высота). В шестом стобце указываем номер дерева (по зеленой линии). Вносить лучше всего по порядку идя по зеленой линии от точки к точке. Когда все готово — переводим все в csv-формат (кому лень переводить — может подгрузить в QGIS модуль для импорта данных из табличных редакторов). На этом этапе создаем дополнительные столбцы: natural (все строки заполнены текстом «tree»), genus (род на латыни), genus:ru (род на русском), health (санитарное состояние), height (высота), kind:ru (порода).

Сканируем или фотографируем карты и привязываем их в QGIS. Если они не сильно измялись в поле и сканированы — для привязки достаточно трех точек и аффинной трансформации. В моем случае точек потребовалось больше, поскольку иначе как проективной трансформацией фотографию нормально не привяжешь.

Привязанная в QGIS полевая карта

Привязанная в QGIS полевая карта

После привязки создаем точечный слой и обклацываем все точки вдоль зеленой линии одну за другой, ничего не пропуская и не ставя лишнего. Это крайне важно, поскольку определяет как будут сцеплены атрибутивные данные с геометрией. Для контроля полезно включить отображение количества объектов в слое. Когда все готово, остается только создать в таблице столбец с целочисленными значениями и заполнить его формулой $id+1 — в результате каждой точке будет присвоен номер, под которым он упомянут в таблице с данными. Остается лишь связать геометрию с внешней таблицей и скопировать данные из привязанной таблицы в атрибуты слоя.

Внесение геометрии в QGIS

Внесение геометрии в QGIS

Сохраняем файл в формате geojson в WGS84 (EPSG:4326). Теперь через оверпас проверяем наличие деревьев в базе OSM на территорию обследованных районов. В моем случае таковых было всего пять. Сверяем их со своими данными и если все совпадает (при обследовании вы их нашли), а теги на деревьях не содержат важной информации — смело удаляем их через JOSM. Если не нашли, но вы в качестве своей работы уверены — тоже удаляйте.

Осталось только загрузить в JOSM ваш район работ, открыть файл geojson, копировать из него данные в слой openstreetmap и убедиться, что все на своих местах. У меня иногда вставленный слой по неизвестной причине съезжает, но это ошибка систематическая и исправляется элементарным перемещением.

JOSM с данными о деревьях

JOSM с данными о деревьях

Казалось бы, загружай в базу и радуйся. Но есть пару щепетильных моментов, на которые опытный осмер уже наверняка обратил внимание. Начнем с базового: а что именно мы вносим? Понятно, что деревья, но вот мы полторы тысячи деревьев осмотрели и ощупали. Самое время спросить: «Что такое дерево?». Сирень — дерево или нет? А липа? А дюжина лип, которые растут как кустарник? В OSM внесено не тринадцать миллионов деревьев, а тринадцать миллионов natural=tree, среди которых и трава, и кустарники, и деревья и все, что угодно.

Теперь наименование. Если следовать описанию тега natural=tree, мы должны указать вид (species), в крайнем случае род (genus). Подход логичный, но так делать нельзя. Могу ли я указать вид липы? Допустим, с очень большой погрешностью могу, благо их всего два в городе: платифиллос и кордата. А как быть с ясенями, у которых диагностический признак — почки, расположены на высоте трех человеческих ростов? А как быть с лиственницами, виды которых во всем Питере может нормально различить только один человек?

Для таких ситуаций есть род (genus). Но вот передо мной вишня. Если я внесу genus для вишни по всем правилам, то выйдет, что у меня никакая не вишня, а слива. Потому как вишня — это сливовый подрод (Prunus subgenus Cerasus). И черемуха, кстати тоже. А как быть с трактовкой разных таксонов? Никогда вы эту проблему не решите, если не признаете, что вносить вид в OSM столь же глупо, как и указывать для каждого дерева нуклеотидную цепь. Вид и род — это очень, очень, очень тяжело и сложно. То что определяем мы, называется «порода» — группа растений, выделенная на основе практической целесообразности. Это может быть и вид (конский каштан обыкновенный) и род (вяз) и подрод и все, что угодно. Хороший пример с тополями, которые разделяют на две породы: осину, которая populus tremula и тополь, который объединяет все остальные тополя.

А как обстоит дело с морфометрией? Есть circumference=* — окружность ствола в метрах. Но как быть, если стволов несколько? Как обозначить, что дерево является угрозой? Как тегировать кронированный обрубок тополя? Как тегировать особые случаи произрастания, например, когда береза вьется лианой по сосне? Или черемуха, ветви которой в четверть метра толщиной вросли в землю? Как обозначать скворечники и кормушки? А если в них живут белки? Хотя стоп, сейчас меня в нечеткое тегирование унесет, а я все это не для того рассказываю. Рассказываю я это для того, что неожиданно хороший повод для встречи образовался — поработать над схемой тегирования деревьев. Ну и еще немного для того, что-бы похвастаться. Что я, зря полторы недели корячился?

fleur.js

Оценка кормовых угодий на JavaScript

Заголовок кривой, но так вернее — я пишу статью в междисциплинарный вакуум: программисты бросят читать на втором слове, а ботаники на четвертом. По этой причине изложу мысль от лица человека, который геоботанику с программированием в гробу видал.

Представим, что вы заимели в распоряжение некоторую площадь земли и намереваетесь распорядиться ей по хозяйски. Решив финансовые, кадастровые и прочие вопросы вы неизбежно придете к вопросу: «Какова земля по своим качествам?». Годится ли для посадки помидоров или кроме кривой сосны ничего не вырастет? Какой цемент выбрать для фундамента: исходя из сухой почвы или периодически подтопляемой? Почему у соседа вызревает полна жопа огурцов, а у вас дохнет последний подорожник? Потому, что в почве элементов не хватает или соседские коровы все вытоптали?

Когда участок мал, ответ познают органолептическим методом. Но что делать, если вам нужны точные результаты? Например, ваша сестра вышла замуж за премьер-министра и вы завладели миллионами гектаров угодий. Первая мысль — отобрать пробы почв из разных мест и отдать в физико-химическую лабораторию. Идея хороша, но есть три «но». Во-первых, это будет стоить безумных денег. Во-вторых, физико-химические свойства почвы постоянно меняются. Прошел дождь — и вот вам иное соотношение растворимых солей. Выглянуло солнце — изменилась влажность. В третьих, и это самое главное, вам необходимо знать не абсолютные концентрации микроэлементов, а то, насколько успешно они поглощаются растениями.

Логично оценить угодья по местным растениям. Если условные редька и одуванчик нуждаются в одинаковых условиях, значит поле одуванчиков подходит для редьки. Это примитивная, но верная мысль. Преимущество растений в длительном росте, который накапливает свойства территории за большой период. Кроме того, изучая растительность мы снижаем риск ошибки, связанной с бочкой Либиха.
Бочка Либиха

Бочка Либиха — принцип названный по фамилии немецкого профессора. В скучной экологической литературе он чаще упоминается как закон лимитирующего фактора. Наполним водой деревянную бочку, которую сколотили из досок разного размера. По заполнению, вода начнет вытекать через самую короткую доску. Наша редька будет дохнуть именно от самого проблемного элемента. Мы проверили все: азот, фосфор, калий, серу, железо и кучу других элементов — все в порядке. Но случайно забыли про марганец и вот наша условная редька уже в точечных пятнах хлороза тщетно пытается синтезировать аскорбиновую кислоту, дожидаясь малейшего повода для смерти. Условный одуванчик реагирует на всю совокупность физико-химических условий произрастания. Если он бодр и весел, за редьку можно не переживать.

Жизнь устроена сложнее наших условностей. Не бывает двух организмов, а уж тем более видов с одинаковыми требованиями к условиям обитания. «Что русскому хорошо, то немцу смерть» в переводе на экологический язык называется нормой реакции и выражается в кривой жизнедеятельности:
Кривая жизнедеятельности

Принцип влияния экологических факторов на организм выражается пословицей «Все хорошо в меру». Задача — сравнить между собой «меры» различных видов и применить к ним школьный принцип «меньше большего, больше меньшего». Если мы нашли одуванчик, значит условия жизни для одуванчика подходят. Если рядом с одуванчиком сныть, значит условия жизни подходят для одуванчика и сныти одновременно. Если мы собрали тридцать разных видов, значит условия подходят одновременно для каждого из них. Чем больше видов, тем уже диапазон факторов произрастания:
Сужение диапазона факторов произрастания

Теоретически, мы можем построить такие кривые для любого фактора окружающей среды (вопрос эмергентности опустим — это тема долгого и сложного разговора). Нас не волнует медианное значение влажности почв. Мы хотим знать, достаточно ли влаги растениям? Это не одно и тоже: весной воды хоть залейся, но растения живут в условиях физиологической сухости, поскольку не могут впитать воду из холодной почвы. Вопрос шкалирования («в каких единицах измерять») решается принципом канторово-пелевинской «сиськой в себе». Рисуем пустую стобалльную шкалу, после идем в самое сухое место, определяем найденные растения и вписываем их в левую часть шкалы. Потом идем в самое сырое место и вписываем местные растения в правую часть шкалы. После делаем несколько десятков тысяч описаний из разных мест и расставляем на шкале встреченные виды.

В одну из ночей опустите луч фонарика вертикально вниз. На землю ляжет тень от травы — проекция растений на плоскость. Если забыть, что луч бьет из одной точки или взять громадный прожектор, то площадь тени будет пропорциональна густоте растений. В геоботанике этот показатель называется проективным покрытием. Глазомерно он вычисляется как доля покрытой растениями территории. Сумма проективных покрытий всех видов больше общего покрытия травостоя, поскольку разные виды перекрывают друг друга. Псевдоматематики называют проективное покрытие вероятностью обнаружения вида в точке со случайными координатами или говорят о других диких концепциях, но на практике без инструментов никто не способен оценить густоту растений точнее 5-10 процентов (хоть все говорят, что могут), поэтому описание дополняют словами «единично», «незначительно» и прочей гуманитарной фигней.

Идя по градиенту влажности от сырого к сухому месту, вы встретите новые виды. Пока еще чахлые и редкие. Они едва выживают при такой влажности. Скоро этих растений станет больше. В идеальных условиях проективное покрытие возрастет до ста процентов — вспомните непроходимые заросли крапивы urtica dioica. На подходе к сухому месту проективное покрытие уменьшается, в сухих условиях остаются лишь единичные растения. В очень сухих ваши они уступают другим видам. За время похода вы пройдете несколько куполообразных изменений проективного покрытия, которые вспомните составляя шкалу:
Градиент изменения условий среды

Когда первая шкала готова, делим весь массив описаний на группы по влажности территорий и для каждой группы тем же методом строим шкалу «бедность-богатство-засоленность». Затем итеративно повторяем процесс для переменности увлаженения, аллювиальности почв, пастбищной дегрессии (вытоптанности) и чего душа пожелает.

Для работы необходимы десятки лет, миллиарды рублей и армия ботаников. Сегодня такие ресурсы получить невозможно, но по счастью кровавый сталинизм оставил в наследство не только сопливый дудевский фильм, но и результат работы института луговой и болотной культуры (сейчас НИИ кормов имени Вильямса), где под руковоством Л.Г. Раменского подготовлена прекрасная монография «Экологическая оценка кормовых угодий по растительному покрову». Книга содержит короткую пояснительную записку, методы анализа и таблицу на сотни страниц, где указано размещение видов растений на экологических шкалах в зависимости от проективного покрытия.
Книга Экологическая оценка кормовых угодий по растительному покрову

Свыше полувека работа с этой книгой выглядит так: геоботаник описывает проективные покрытия видов на площадке, возвращается домой, достает миллиметровку и рисует на ней шкалу влажности (сто двадцать единиц). Смотрит на значение проективного покрытия первого вида, находит этот вид где-нибудь на триста седьмой странице и откладывает на миллиметровке указанный в книге диапазон. Потом второй вид, потом третий и так до конца. Вид а: от сорока до пятидесяти, вид б: от сорока пяти до семидесяти, вид в: от двадцати до сорока восьми. На основе «больше меньшего, меньше большего» оцениваем увлажнение участка от сорока пяти до сорока восьми баллов. Потом переходим к вычислению богатства почвы, потом к остальным показателям. Спустя несколько часов беремся за другое описание.

Это не единственный метод, но остальные еще хуже. Тратить на это жизнь в двадцать первом веке невыносимо, поэтому ботаники забросили шкалы на антресоль и достают только студентам показать. За минувшие десятилетия технология нисколько не развилась и видимо до следующего витка репрессий останется в забвении.

Казалось бы, любой первокурсник-технарь напишет алгоритм за пару часов, любой школьник, отличающий инкремент от компиляции закодит его за вечер. Все просто как две копейки. Но все программные реализации (включая мою работу десятилетней давности) напоминали сплетенные из вареных макарон костыли для безруких. Потому что легче «Анну Каренину» на машинный язык перевести, чем автоматизировать работу с экологическими шкалами Раменского.

Проблема исключительно гуманитарная. Ботаники — от студентов до докторов наук до сих пор не отличают электронную информацию от цифровой. Наука о растительности — это пещера в котором обитает карго-культ технологического развития. Попросите любого выслать метаданные описаний — столкнетесь с непониманием. Договоритесь о данных в цифровом виде — получите на почту вордовский файл с таблицами. Гусиные перья сменила печатная машинка, печатную машинку компьютер, но сама технология изучения растительности осталась на уровне гусиных перьев.

Геоботаническое описание обычно содержит в себе метаданные (где, кем, когда и др.), описание древостоя (при наличии оного и отсутствии отдельных таксационных работ), подроста, подлеска и таблицы проективных покрытий травяно-кустарничкового и мохово-лишайникового ярусов. Камеральная обработка сводится к переносу данных в эксель, часто в том же виде, в каком они представлены на бланке. Форма бланков у всех разная, поэтому данные разных авторов не сравнимы без мучительной корректорской работы. Я опускаю разность методик, разность понимания видов, здесь разговор только о технической стороне вопроса.
Образец геоботанического описания

Без общепринятого формата, любой код автоматизации придется переписывать под каждого автора. Но это не спасет без решения проблемы субъективных оценок. Нельзя вместо оценки проективного покрытия скормить алгоритму понятия «единично», «изредка», «две куртины» и прочий бред (все из реальных описаний). Предположим, мы исключим такие данные из выборки. Если речь об экологическом шкалировании, то это допустимо. Но следом возникает проблема таксономии.

Линней, работая с номенклатурой не думал о том, что латынь уйдет в прошлое, а коробка размером с небольшой саквояж уместит в себе всю ботаническую литературу. Сегодня виды сохраняют латинское название (и это правильно), но саму латынь никто не помнит, герундий от герундива не отличает, рода путают между собой. В результате окончания видов обычно записаны с ошибками. Другое проблемное место — нечитаемые буквы. Попробуйте спустя месяц по памяти верно воспроизвести krascheninnikovii, krascheninnikoviana, или krascheninnikoviorum. Тут ботаники с лицом честного гаишника воскликнут, что они, дескать все выверяют по справочнику Черепанова. Клевер луговой у них трифолиум пратенсе, а клевер ползучий — амория репенс. Не верьте. При мне за несколько лет луговик извилистый из дешампсии стал лерхенфельдией, а из последней превратился в авенеллу. Все обсуждают подобные мелочные вопросы и никто не ничего хочет менять всерьез. А без изменений весь накопленный материал стоит дешевле макулатуры.

Я давно не работаю в государственном институте. Пол-месяца ввода, пол-месяца обработки и месяц дальнейшей психотерапии в мой прайс не включен, поэтому пришлось уйти от ботанических практик и минуя табличные редакторы, вводить данные сразу в виде js-объекта (в данные внесены искажения по условиям контракта, комментарии добавил для наглядности):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var descript = [
{
time:20160602,
note:'GR-0602-1',
tags:'Сосняк, Мяглово-Карьер',
lat:59.82739,
lng:30.69896,
datum:'4326',
author:'S.N.Golubev',
feedback:'schwejk-rpnt@rambler.ru',
license:'CC-BY-NC-SA-3.0',
source:'fieldobserve',
aream:2411,
dendro:{   /*Характеристики древостоя*/
	allvolumemcb:329,   /*Запас, куб. м*/
	allfullmsq:34.4,    /*Абсолютная полнота, кв. м*/
	pins__sylrs:{       /*Данные по сосне - pinus sylvestris*/
		volumemcb:329,   /*Запас, куб. м*/
		fullmsq:34.4,    /*Абсолютная полнота, кв. м*/
		diasm:23,        /*Средний диаметр, cм*/
		heightm:24.7,    /*Высота, м*/
		age:70,          /*Возраст, лет*/
	},
},
grass:{   /*Данные по живому напочвенному покрову*/
	allcover:50,   /*Общее проективное покрытие яруса*/
	cover:{        /*Повидовое проективное покрытие*/
		vacnm_myrls/*Черника - Vaccinium_myrtillus_L*/:20,
		vacnm_vitd/*Брусника - Vaccinium_vitisidaea_L*/:30,
		conlr_majls/*Ландыш - Convallaria_majalis_L*/:5,
		trils_eurp_/*Седмичник - Trientalis_europaea_L*/:0.1,
		desps_flexs/*Луговик - Deschampsia_flexuosa_Trin*/:10,
		melrm_prans/*Марьянник - Melampyrum_pratense_L*/:0.1,
		luzl__pils_/*Ожика - Luzula_pilosa_L_Willd*/:0.1,
		calln_vulrs/*Вереск - Calluna_vulgaris_L_Hull*/:0.1,
		charn_anglm/*Кипрей - Chamerion_angustifolium_L_Holub*/:0.1,
		fragr_vesc_/*Земляника - Fragaria_vesca_L*/:0.1,
		soldg_virgr/*Золотарник - Solidago_virgaurea_L*/:0.1,
		maimm_biflm/*Майник - Maianthemum_bifolium_L_FW_Schmidt*/:0.1,
		desps_cests/*Щучка - Deschampsia_cespitosa_L_Beauv*/:0.1,
		},
	},
undergrass:{/*Данные по мохово-лишайниковому ярусу*/
	allcover:40/*Общее проективное покрытие яруса*/,
	cover:{
		polhm_specs:0.1/*Политрихум*/,
		plezm_schbr:40/*Плеуроциум*/,
		},
	},
},
]

Структура данных повторяет бланк описания (метаданные-древостой-живой напочвенный покров-мохово-лишайниковый ярус). Видам с незначительным обилием присвоено проективное покрытие 0.1%. Видовые названия записаны в виде одиннадцати символов: пять на род, пять на вид и символ нижнего подчеркивания между ними. Род и вид преобразуются в код вида по такому принципу:
— Первые три буквы таксона берутся без изменений (Convallaria — con);
— Последние две соответствуют двум последним согласным таксона (Convallaria — lr);
— Если букв в таксоне меньше пяти, пропуски заполняются нижним подчеркиванием (Poa pratense — poa___prans);
— Если после первых трех букв одна согласная или согласных нет — пустые места заполняются нижним подчеркиванием (Luzula_pilosa — luzl__pils_).

Это не самый удачный принцип, поскольку требует исключений. Например, одуванчики Taraxacum laticordatum и Taraxacum latisectum кодируются одинаково: tarcm_lattm. К более простому решению, которое обеспечивает автоматическую кодировку списка таксонов я пока не пришел. К счастью исключения редки даже для региональной флоры, для локальной совсем незначительны и легко отлавливаются простой проверкой по сортированному массиву.

После я перевел таблицу из книги Л. Г. Раменского в js-массив следующего вида:

1
2
3
4
5
6
7
8
9
10
11
12
var ramen = [
["КОД", "ВИД", "ШКАЛА", "ЗОНА", "ПОЧВА", "ПОКРЫТИЕ", "MIN", "MAX"],
["acalm_punns", "Acanthophyllum pungens (Bunge) Boiss.", "water", false, false, 0.3, 10, 15],
["acalm_punns", "Acanthophyllum pungens (Bunge) Boiss.", "water", false, false, 0.1, 8, 1000],
["acalm_punns", "Acanthophyllum pungens (Bunge) Boiss.", "rich", false, false, 0.3, 12, 15],
["acapr_schhr", "Acarospora schleicheri (Ach.). Mass.", "water", false, false, 2.5, 15, 19],
["acapr_schhr", "Acarospora schleicheri (Ach.). Mass.", "water", false, false, 0.3, 11, 22],
["acapr_schhr", "Acarospora schleicheri (Ach.). Mass.", "water", false, false, 0.1, 10, 35],
["acer__plads", "Acer platanoides L.", "water", false, false, 0.1, 65, 71],
["acer__plads", "Acer platanoides L.", "water", false, false, 0, 0, 91],
...
]

Массив состоит из 11 673 элементов, включая заголовок. Каждый элемент содержит информацию о видовом коде, таксоне, экологической шкале, минимальном и максимальном балле шкалы. Информация о типе почв и природно-климатической зоне отсутствует, но на случай развития проекта для этих данных оставлено место. В тех случаях, когда минимальный балл в книге не указан, в таблице стоит 0. Если не указан максимальный балл, в таблице стоит 1000.

Скрипт расчета Fleur.js содержит всего полторы сотни строк, но его следует сократить вдвое, поскольку вторая функция на 99% дублирует первую. На момент написания я вконец обленился и просто скопипастил свою же функцию, дополнив ее несколькими строками. Функция «ramenall(e)» подхватывает первое описание в серии, переводит абсолютные значения проективного покрытия из геоботанического описания в группы проективных покрытий шкал Л. Г. Раменского (единично-0.1, 0.1-0.3, 0.3-2.5, 2.5-8, 8 и более процентов). После сравнивает видовые списки из описания и таблицы экологических шкал на основе общего ключа кода видов. Найдя совпадение в коде, функция заполняет массив номером и таксонами описания с присвоением минимального и максимального балла для каждого вида. Если для вида информация отсутствует, скрипт выдает «-Infinity, Infinity;». После программа переходит к следующему описанию из серии. Когда описания заканчиваются, программа выводит собранный массив на html-страницу.

Функция «ramenbase(e)» выполняет те же самые операции, только для каждого описания в серии формирует массив с минимальными и максимальными значениями баллов. Из массива минимальных баллов отбирает наибольший, из массива максимальных — наименьший. Итогом выпадает таблица с номером описания, минимальным и максимальным значением на экологической шкале.
Больше меньшего, меньше большего

Обе функции потребляют на вход одинаковые аргументы: «rich» — богатство и засоленность почвы, «water» — влажность почвы, «waterwave» — переменность увлажнения, «alluvium» — аллювиальность почвы и «degrade» — пастбищная дегрессия.

Качество кода оставляет желать лучшего, но поскольку он написан три года назад по дороге из Кингисеппа в деревню Лисино-Корпус Ленинградской области, я доволен и без нужды ничего менять не планирую.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Полный расчет (значения для всех видов)
function ramenall(e){
 
  for(var a=0; a<descript.length; a++)
  {
	  var gbo = descript[a]; // Текущее описание в обработке
	  var spec=[];           // Вид
	  var pokr=[];           // Проективное покрытие в процентах
	  var pokrball=[];       // Балл покрытия по Раменскому
	  var spectable=[];      // Обертка для spec, pokr, pokrball
 
// Перевод % покрытия в % покрытия по Раменскому	  
	  for(var key in gbo.grass.cover)
	  {
		  spec.push(key);
		  pokr.push(gbo.grass.cover[key]);
		  if(gbo.grass.cover[key]>=8.0 &&
				gbo.grass.cover[key]<100){pokrball.push(8.0);}
		  if(gbo.grass.cover[key]>=2.5 &&
				gbo.grass.cover[key]<8.0){pokrball.push(2.5);}
		  if(gbo.grass.cover[key]>=0.3 &&
				gbo.grass.cover[key]<2.5){pokrball.push(0.3);}
		  if(gbo.grass.cover[key]>=0.1 &&
				gbo.grass.cover[key]<0.3){pokrball.push(0.1);}
		  if(gbo.grass.cover[key]>=0.0 &&
				gbo.grass.cover[key]<0.1){pokrball.push(0.0);}
		}
 
// Заполнение таблицы для сравнения со шкалами    
	  spectable.push(spec);
	  spectable.push(pokr);
	  spectable.push(pokrball);
 
// Сравнение со шкалами   
	  for(var i=0; i<spec.length; i++)
	  {
		  for(var k=0; k<ramen.length; k++)
		  {
			  if(spectable[0][i]==ramen[k][0] && //Код вида
				ramen[k][2]==e && // Шкала (указана в HTML)
				ramen[k][3]==false && // Природная зона (игнорируется)
				ramen[k][4]==false && // Тип почвы (игнорируется)
				ramen[k][5]==spectable[2][i] // Проективное покрытие
				)
				{
// Публикация отчета в HTML
				var str = document.getElementById('tableResult');
				var add = str.insertRow(-1);
				var addTr = document.createElement("tr");
				var addTd = document.createElement("td");
					addTd.innerHTML=descript[a].note+", ";
					addTr.appendChild(addTd); // Номер описания
				var addTd = document.createElement("td");
					addTd.innerHTML=ramen[k][1]+", ";
					addTr.appendChild(addTd); // Название вида
				var addTd = document.createElement("td");
					addTd.innerHTML=spectable[1][i]+"%,      ";
					addTr.appendChild(addTd); // Покрытие
				var addTd = document.createElement("td");
					addTd.innerHTML=ramen[k][6]+",      ";
					addTr.appendChild(addTd); // Максимум
				var addTd = document.createElement("td");
					addTd.innerHTML=ramen[k][7];
					addTr.appendChild(addTd); // Максимум
				str.appendChild(addTr);
				};
		};
	};
};
}
 
// Краткий расчет (классический, результаты для пробной площади в целом)
function ramenbase(e){
	for(var a=0; a<descript.length; a++)
	{
		var gbo = descript[a];
		var spec=[];
		var pokr=[];
		var pokrball=[];
		var spectable=[];
 
		for(var key in gbo.grass.cover)
		{
			spec.push(key);
			pokr.push(gbo.grass.cover[key]);
			if(gbo.grass.cover[key]>=8.0 &&
				gbo.grass.cover[key]<100){pokrball.push(8.0);}
			if(gbo.grass.cover[key]>=2.5 &&
				gbo.grass.cover[key]<8.0){pokrball.push(2.5);}
			if(gbo.grass.cover[key]>=0.3 &&
				gbo.grass.cover[key]<2.5){pokrball.push(0.3);}
			if(gbo.grass.cover[key]>=0.1 &&
				gbo.grass.cover[key]<0.3){pokrball.push(0.1);}
			if(gbo.grass.cover[key]>=0.0 &&
				gbo.grass.cover[key]<0.1){pokrball.push(0.0);}
		}
 
		spectable.push(spec);
		spectable.push(pokr);
		spectable.push(pokrball);
 
		var ecoscalemin=[];// Шкала минимумов
		var ecoscalemax=[];// Шкала максимумов
 
		for(var i=0; i<spec.length; i++)
		{
			for(var k=0; k<ramen.length; k++)
			{
				if(spectable[0][i]==ramen[k][0] &&
				ramen[k][2]==e &&
				ramen[k][3]==false &&
				ramen[k][4]==false &&
				ramen[k][5]==spectable[2][i]
				)
				{
					ecoscalemin.push(ramen[k][6]);
					ecoscalemax.push(ramen[k][7]);
				};
			};
		};
 
		var str = document.getElementById('tableResultKratk');
		var add = str.insertRow(-1);
		var addTr = document.createElement("tr");
		var addTd = document.createElement("td");
			addTd.innerHTML=descript[a].note+",  ";
			addTr.appendChild(addTd); // Номер описания
		var addTd = document.createElement("td");
 
			// Максимальное значение шкалы минимумов
			addTd.innerHTML=Math.max.apply(Math, ecoscalemin)+",  ";
			addTr.appendChild(addTd); // Минимум
		var addTd = document.createElement("td");
 
			// Минимальное значение шкалы максимумов
			addTd.innerHTML=Math.min.apply(Math, ecoscalemax)+";  ";
			addTr.appendChild(addTd); // Максимум
		str.appendChild(addTr);
	};
}

Остается сверстать простую html-страницу, без всяких цээсэсов, назначить функции кнопкам и радоваться жизни. Полноценный анализ тестового набора с помощью миллиметровки у меня бы занял дней десять, может больше. Наверняка есть профи, кто сделает это быстрее, но даже супермен не рассчитал бы показатели для сотни описаний за долю секунды.

Финализировать эту эпопею нужно тремя вопросами: почему JavaScript?, что дальше? и как использовать полученные результаты анализа?. JavaScript — потому что эти расчеты иногда требуется выполнять на чужих компьютерах без установленного R, Wine или другого софта. Что дальше — не знаю. Есть пару идей, но я три года ничего не менял, могу еще три года ничего не менять. А как использовать результаты я не расскажу, поскольку строки этой статьи все-равно никто не увидит. Программисты бросят читать на втором слове, а ботаники на четвертом.


По адресу городшахты.рф/source/fleur/ лежит готовая к использованию программа. Можете указать ссылку на свой набор геоботанических описаний в указанном выше формате и рассчитать богатство, увлажнение, переменность водного режима, аллювиальность и пастбищную дегрессию почв.
Полноценное теоретическое обоснование, альтернативные методы и материалы для контроля доступны в книге: Л. Г. Раменский, И. А. Цаценкин, О. Н. Чижиков, Н. А. Антипин «Экологическая оценка кормовых угодий по растительному покрову» Всесоз. науч. -исслед. ин-т кормов им. В. Р. Вильямса. М. : Сельхозгиз , 1956 470, [2] с.: ил., 1 л. граф.