Хотел было назвать пост как «Кому на Руси жить хорошо», но начал тестировать приложение и понял — заголовок не совсем будет соответствовать действительности. Учитывая, что большая часть посетителей блога webdelphi.ru из России (49% по Alexa Rank), то сегодняшний пост можно было бы назвать остросоциальным, так как сегодня я затрону тему экологии, а именно — выбросов загрязняющих веществ в атмосферный воздух. Ну, а так как блог по программированию, то, конечно же, основная часть статьи будет касаться практического применения библиотеки «Readers and Writers JSON Framework» для анализа открытых данных.
О чем речь?
Думаю, что, если вы житель России, то в курсе, что 2017 год был объявлен в стране годом экологии. Надо сказать, что за 2017-2018 гг., да и в 2019-м выявлено и предано публичной огласке достаточно много различных проблем, связанных с охраной окружающей среды. Всплывали и откровенно нелицеприятные проблемы, как, например, знаменитый в Омске «этилмеркаптановый» скандал, когда концентрация загрязняющего вещества (которое было принято за этилмеркаптан) была превышена в отдельных точках города более, чем в 400 раз или массовое отравление детей «неизвестным газом» в Волокаламске и так далее. Такие громкие скандалы, в свою очередь, приводили как к серьезным разбирательствам, так и к откровенной спекуляции, крикам «мы все умрем», «нас специально травят» и тому подобное. Но сегодня я не хочу кого-то обсуждать или, тем более, осуждать. Я не политик, не общественный деятель и не сторонник конспирационной теории, да и блог webdelphi не то место, где стоит публиковать темы, связанные с социальными проблемами общества.
Но знать кто и чем дышит (в прямом смысле этого слова) хотелось бы. И сегодня попробуем разобраться со следующими вопросами:
- На сколько является открытой и достоверной информация о загрязнении атмосферного воздуха в Российской Федерации?
- Где в нашей стране сосредоточено больше всего опасных производств?
- Какие предприятия и в каких регионах более всего загрязняют атмосферный воздух?
И ответить на эти вопросы нам поможет Delphi 10.3 Rio и её библиотека «Readers and Writers JSON Framework«.
История вопроса
В 2015 году все промышленные предприятия России были обязаны встать на учёт в новом реестре объектов негативного воздействия на окружающую среду.
Что характерно, в числе первых, кто побежал вставать на учёт оказались наиболее крупные предприятия РФ. Так вот, в этот реестр теперь стекается практически вся информация по предприятиям, а, точнее их воздействии на окружающую среду — сколько и чего выбрасывается в воздух, какие отходы размещаются на территории и в каких объемах, что предприятие делает, чтобы снизить свое воздействие на окружающую среду и так далее. В общем, информация как интересная, так и, без шуток — ценная для экологов.
Отдельно отмечу, что, исходя из сложившейся ситуации, в этот реестр попадает информация намного более конкретная, чем, например, в Росстат. Например, в Росстате вы не получите информацию о выбросах в атмосферу по такому канцерогену, как бенз(а)пирен — такие данные не публикуются отдельно, но могут быть «зашиты» в графе «Твердые вещества» таблички по выбросам в атмосферу.
Кроме того, что теперь ведется такой реестр в нашей стране, часть информации из него предоставлена в публичный доступ, как в виде web-приложения, так и в виде открытых исходных данных.
Например, можно зайти на сайт реестра под своим логином с Госуслуг и посмотреть информацию по выбросам конкретного объекта:
Откровенно говоря, у меня складывается ощущение, что сайт сделан «для галочки» — поиск минимальный, фильтрации никакой. Захочешь найти конкретное предприятие — запаришься искать. В общем — это не наш метод. Мы будем использовать набор открытых «сырых» данных и получать ту информацию, которая нам необходима самостоятельно.
Исходные данные для анализа
«Сырые» данные реестра выгружаются в виде одного файла, содержащего большущий json-массив. Я о нем, кстати, писал в прошлой статье. В этом файле содержится ровно то, что было предоставлено в публичный доступ, но, в отличие от инструментов сайта, с этими данными мы можем, при желании делать, что угодно (естественно, не искажая их): фильтровать по определенным признакам, сравнивать значения между собой, анализировать и прочее.
Итак, на входе имеем файл размером 544 Мб, в кодировке UTF-8 (с BOM) в котором лежит json массив на более, чем 200 000 элементов. Каждый элемент массива — отдельный объект негативного воздействия на окружающую среду (принятое сокращение — объект НВОС).
Вот как выглядит отдельный элемент json-массива (для примера, взял самый первый элемент):
Разберемся, что здесь к чему:
Name | string | Название объекта НВОС |
Organization | object | Эксплуатирующая организация (одна организация может эксплуатировать несколько объектов НВОС) |
Organization.Name | string | Наименование эксплуатирующей организации |
Organization.RegistrationDate | date | Дата ввода в эксплуатацию |
Record | object | Запись объекта в реестре (информация по заявке о постановке на учёт) |
Record.Code | string | Уникальный идентификатор объекта НВОС. См. ниже, что содержит. |
Record.RegistryType | string | В какой части реестра находится объект: региональной или федеральной. |
Record.Category | string | К какой категории относится объект. Категорий всего четыре: I категория — самые опасные объекты, IV — наименее опасные |
Record.Timestamp | date | Дата постановки на учёт |
Record.WasSigned | boolean | Признак того, что заявка о постановке на учёт была одобрена |
Record.RiskCategory | integer | Категория риска. 6 категорий — чем меньше это число, тем чаще осуществляются проверки объекта гос.органами |
Facts | object | Фактическая информация о воздействии объекта НВОС на окружающую среду. |
Facts.Air | object | Информация по выбросам в атмосферу |
Facts.Air.ObjectCount | integer | Количество источников загрязнения на объекте НВОС |
Facts.Air.TotalAnnualValue | double | общий выброс в атмосферу, т/год всех веществ |
Facts.Air.Facts | array | Массив, содержащий данные по выбросам каждого загрязняющего вещества |
Facts.Air.Facts[] | object | Информация по выбросу конкретного загрязняющего вещества |
Facts.Air.Facts[].Code | integer | Код загрязняющего вещества |
Facts.Air.Facts[].Name | string | Наименования загрязняющего вещества |
Facts.Air.Facts[].AnnualValue | double | Выброс загрязняющего вещества, т/год |
Facts.Air.Facts[].Power | double | Мощность выброса, г/с |
Facts.Water | object | сбросы в водоемы. Содержимое объекта не рассматривается, т.к. к сегоднящней теме отношения не имеет. |
Facts.Waste | object | размещение отходов производства и потребления. Содержимое объекта не рассматривается, т.к. к сегоднящней теме отношения не имеет. |
Facts.ValCo2 | double | Общий выброс веществ, разрушающих озоновый слой, в пересчёте на эквивалент CO2 |
MeasureTools | object | Средства сокращения выбросов/сбросов, которые эксплуатируются на объекте. В статье не рассматриваются. |
Как видите, информации достаточно. Остается один вопрос, как понять, что тот или иной объект находится в моем регионе, например, в Московской, Новосибирской или Омской области? Для этого нам нужен код объекта (поле Record.Code). Первые две цифры в нем — это код ОКТМО региона. Например, 52 — это Омская область, 01 — Алтайский край, 15 — Брянская область и так далее. Не стоит путать этот код с тем, который вы каждый день видите на номерах автомобилей — это несколько разные коды.
Таким образом, этот файл с открытыми данными является для нас замечательным образцом для тестирования возможностей «Readers and Writers JSON Framework» — здесь вам и чтение большого объема данных, и прохождение по вложенным объектам и массивам json. В общем в самый раз начать этот json разбирать.
Алгоритм парсинга json
Алгоритм разбора этого JSON будет таким:
- Создаем экземпляр TJSONIterator
- Заходим внутрь массива (выполняем метод Next)
- Получаем доступ к полям очередного элемента json-массива (выполняем метод Recurse). На этом шаге мы должны оказаться внутри объекта перед полем Name
- Перебираем каждую пару в объекте (снова метод Next) и полезную для нас информацию временно сохраняем, например, в запись (record).
- Дойдя до пары Record.Code проверяем нужен ли нам этот объект для анализа — вполне возможно, что мы захотим получить информацию только по Московской области и лишние объекты надо будет пропускать.
- Если объект нам не нужен, то удаляем всю временно сохраненную информацию и переходим к следующему элементу массива (вызываем методы Return и Next)
- Если объект нам необходим, то двигаемся по объекту дальше
- После прочтения всего объекта Record, снова вызываем Return, Next и попадаем на объект Facts
- Заходим внутрь Facts (метод Recurse)
- Заходим в объект Air (вызываем Next(‘Air’) и Recurse)
- Читаем количество источников загрязнения (ObjectCount), Вызываем Next(),. чтобы попасть в массив Air.Facts
- Проходим по массиву Air.Facts и возвращаемся на самый верх, то есть вызываем один раз Return, чтобы подняться до уровня Air.Facts, чтобы подняться до уровня Air и третий раз — чтобы выйти на самый верх json-массива
- Повторяем все пункты, начиная со второго.
В общем, выглядит алгоритм не самым простым образом, но, главное здесь — это не запутаться в количестве вызовов Next/Recurse/Return. Иначе рискуем оказаться не на том уровне объекта и не дойти до конца файла.
Отдельно отмечу пункт 4 алгоритма. Дело в том, что, если нам необходимо фильтровать данные, то временная структура для хранения данных нам необходима по той причине, что TJSONIterator не может при прохождении json отойти на один шаг назад — только вперед или же на самое начало. И, если не предусмотреть временное хранение полученных ранее данных, то нам придётся бесконечно много раз подниматься на самый первый элемент массива, что потребует лишнего времени. Так что ИМХО лучше пожертвовать лишней парой сотней килобайт оперативной памяти, чем часом времени. Переходим к работе над приложением.
Разработка приложения для анализа данных Росприроднадзора
За основу интерфейса приложения я взял пример delphi по работе с компонентом TSplitView. Поэтому интерфейс (в черновом варианте) получился такой:
Будем фильтровать данные по регионам и выводить следующую информацию:
- Количество объектов НВОС в регионе
- Общий выброс загрязняющих веществ в атмосферу
- Информация по самому крупному объекту (с самым большим выбросом) — наименование, код, суммарный выброс, количество источников загрязнения
- Дополнительно, после парсинга json будем формировать статистику по категориям объектов в регионе — сколько объектов к какой категории относятся, в каком реестре содержаться и так далее.
Также можно будет увидеть расклад по конкретному объекту — какие вещества присутствуют в выбросе, сколько выбрасывает и так далее.
«Скелет» процедуры парсинга
Всем процессом работы с файлом открытых данных будет заниматься отдельный класс — TNVOSParser. Этот класс имеет следующее описание:
TNVOSParser = class {запись для временного хранения данных о выбросе конкретного вещества} private type TAirFactRec = record Code: integer; Name: string; Power: double; Annual: double; end; {запись для временного хранения данных об объекте НВОС} private type TCurrentRec = record Name: string; Organization: string; OrgRegDate: TDateTime; Code: string; RegType: TRegistryType; Category: byte; RegDate: TDateTime; RiskCategory: byte; AirObjects: integer; AirTotalAnnual: double; AirFacts: TArray; procedure Clear; end; private FIterator: TJSONIterator; FReader: TJsonTextReader; FTextReader: TStreamReader; FStatistic: TNVOSStatistic; FFileName: string; FOktmo: integer; FTotal: double; {Отфильтрованные объекты НВОС} FObjects: TObjectList; {выброс самого мощного источника} FMostPowerfulObjectValue: double; {сохраненная информация о текущей записи в массиве} FCurrentRec: TCurrentRec; public constructor Create; destructor Destroy; override; {парсинг открытых данных} procedure Parse(const AOKTMO: byte = 0); property FileName: string read FFileName write FFileName; property Obj[const ACode: string]: TNVOSObject read GetObject; end;
После того, как научимся разбирать JSON, этот класс можно будет дорабатывать — добавить сбор статистики по мере прохождения по массиву, определение объектов по разным признакам, например, найти того, кто раньше всех встал на учёт и так далее. Пока же рассмотрим как будет выглядеть основа процедуры для парсинга массива json:
procedure TNVOSParser.Parse(const AOKTMO: byte = 0); var IsFalse: boolean; begin FOktmo := AOKTMO; FTotal := 0; FMostPowerfulObjectValue := 0; FStatistic.Clear; FTextReader := nil; FReader := nil; FIterator := nil; FObjects.Clear; {создаем перечислитель JSON - TJSONIterator} FTextReader := TStreamReader.Create(FFileName, TEncoding.UTF8, True); FReader := TJsonTextReader.Create(FTextReader); FIterator := TJSONIterator.Create(FReader); try // ходим по массиву json while FIterator.Next() do begin //входим в очередной элемент массива - объект НВОС if FIterator.Recurse then begin //сначала считаем, что этот объект нам нужен IsFalse := False; // находимся внутри записи объекта - переходим к очередному полю объекта while FIterator.Next do begin //оказалось, что объект нам не нужен - забываем, что нашли до этого if IsFalse then begin FCurrentRec.Clear; break; end; //проверяем какой элемент JSON перед нами if FIterator.&Type = TJsonToken.StartObject then begin {перед нами один из вложенных объектов - 'Organization', 'Record', 'Facts' 'MeasureTools'} end else //единственное поле, не являющееся объектом - имя. Сохраняем FCurrentRec.Name := FIterator.AsString; end; //возвращаемся к объекту JSON FIterator.Return; end; //если получили информацию об объекте который нам нужен - переносим информацию в список if not IsFalse then CreateObject; //поднимаемся на уровень массива для получения следующего элемента (объекта НВОС) FIterator.Return; end; finally FreeAndNil(FIterator); FreeAndNil(FReader); FreeAndNil(FTextReader); end; end;
Процедуры парсинга каждого вложенного объекта я вынес в отдельные методы. Первый вложенный объект json, который встречается нам на пути — Organization. Метод парсинга будет следующим:
const cOrganizationPairs: array [0 .. 1] of string = ('Name', 'RegistrationDate'); function StrIndex(const AStr: string; AArray: array of string): integer; var I: integer; begin Result := -1; for I := Low(AArray) to High(AArray) do if SameText(AStr, AArray[I]) then Exit(I) end; procedure TNVOSParser.ParseOrganization; begin FIterator.Recurse; while FIterator.Next do begin case StrIndex(FIterator.Key, cOrganizationPairs) of 0: FCurrentRec.Organization := FIterator.AsString; 1: FCurrentRec.OrgRegDate := TimeStampToDateTime(FIterator.AsString); end; end; FIterator.Return; end;
Функция преобразования даты json в TDateTime:
function TimeStampToDateTime(const ATimeStamp: string): TDateTime; var myformat: TFormatSettings; begin // 2016-12-16T11:02:41.383Z myformat.ShortDateFormat := 'yyyy-mm-dd'; myformat.ShortTimeFormat := 'Thh:mm:ss.zzzZ'; myformat.DateSeparator := '-'; myformat.TimeSeparator := ':'; Result := StrToDateTime(ATimeStamp, myformat); end;
Следом за Organization идёт вложенный json-объект — Record. В этом объекте мы должны проверить код ОКТМО и, в случае необходимости, прервать дальнейшее прохождения по элементу массива. Этот метод я сделал в виде функции:
const cRecordPairs: array [0 .. 5] of string = ('Code', 'RegistryType', 'RiskCategory', 'Timestamp', 'WasSigned', 'Category'); function TNVOSParser.ParseRecord: boolean; var AStrings: TArray; Tmp: integer; begin FIterator.Recurse;//входим внутрь объекта while FIterator.Next do //перебираем все поля begin case StrIndex(FIterator.Key, cRecordPairs) of 0: begin AStrings := SplitString(FIterator.AsString, '-'); if (not TryStrToInt(AStrings[0], Tmp)) or ((FOktmo > 0) and (AStrings[0].ToInteger <> FOktmo)) then begin Result := True; break; end else FCurrentRec.Code := FIterator.AsString; end; // 'Code' 1: if SameText(FIterator.AsString, 'Regional') then FCurrentRec.RegType := TRegistryType.rtRegional else FCurrentRec.RegType := TRegistryType.rtFederal; // 'RegistryType' 2: FCurrentRec.RiskCategory := FIterator.AsInteger; // 'RiskCategory' 3: FCurrentRec.RegDate := TimeStampToDateTime(FIterator.AsString); // 'Timestamp' 4: continue; // 'WasSigned' пока пропускаем 5: FCurrentRec.Category := StrIndex(FIterator.AsString, cCategoryStr) + 1; // 'Category' end; end; FIterator.Return;//возвращаемся на уровень вверх end;
Следующий шаг парсинга — проход по объекту Facts. В этом объекте нас интересует только воздух (Air). Остальное мы должны пропустить и вернуться к основному массиву:
const cAirFactsPairs: array [0 .. 2] of string = ('ObjectCount', 'Facts', 'TotalAnnualValue'); procedure TNVOSParser.ParseAirFacts; var AirFactRec: TAirFactRec; begin FIterator.Recurse; FIterator.Next('Air'); FIterator.Recurse; while FIterator.Next do begin case StrIndex(FIterator.Key, cAirFactsPairs) of 0: FCurrentRec.AirObjects := FIterator.AsInteger; // 'ObjectCount' 1: begin FIterator.Recurse; while FIterator.Next() do begin FIterator.Recurse; with AirFactRec do while FIterator.Next do // проходим по объекту в массиве Air.Facts[] begin // ('AnnualValue','Code','Name','Power'); case StrIndex(FIterator.Key, cAirFactsArrayPairs) of 0: Annual := FIterator.AsDouble; 1: Code := StrToInt(FIterator.AsString); 2: Name := FIterator.AsString; 3: Power := FIterator.AsDouble; end; end; FCurrentRec.AirFacts := FCurrentRec.AirFacts + [AirFactRec]; FIterator.Return; end; FIterator.Return; end; // 'Facts' 2: begin FCurrentRec.AirTotalAnnual := FIterator.AsDouble; //ищем самый большой выброс if FCurrentRec.AirTotalAnnual > FMostPowerfulObjectValue then begin FMostPowerfulObjectValue := FCurrentRec.AirTotalAnnual; //если нашли - вызываем событие в котором передаем названия, код и выброс объекта if Assigned(FOnGetMostPowerful) then FOnGetMostPowerful(self, FCurrentRec.Name, FCurrentRec.Code, FCurrentRec.AirObjects, FCurrentRec.AirTotalAnnual); end; //попутно считаем общий выброс ото всех объектов в регионе FTotal := FTotal + FIterator.AsDouble; end; end; end; FIterator.Return; FIterator.Next(); FIterator.Return; end;
Все эти три метода вызываются внутри метода Parse следующим образом:
procedure TNVOSParser.Parse(const AOKTMO: byte = 0); var IsFalse: boolean; begin {.....} FTextReader := TStreamReader.Create(FFileName, TEncoding.UTF8, True); FReader := TJsonTextReader.Create(FTextReader); FIterator := TJSONIterator.Create(FReader); try // двигаемся по массиву объектов while FIterator.Next() do begin if FIterator.Recurse then begin IsFalse := False; // находимся внутри записи объекта while FIterator.Next do begin {...} if FIterator.&Type = TJsonToken.StartObject then begin case StrIndex(FIterator.Key, cNestedObjects) of 0: ParseOrganization; // 'Organization' 1: IsFalse := ParseRecord; // 'Record' 2: ParseAirFacts; // 'Facts' 3: break; // 'MeasureTools' end; end {...} end; FIterator.Return; end; {....} FIterator.Return; end; finally FreeAndNil(FIterator); FreeAndNil(FReader); FreeAndNil(FTextReader); end; end;
Ссылку на загрузку получившегося модуля я оставил в конце поста. А теперь посмотрим, что получилось в итоге. Результаты, прямо скажу, не однозначные.
Так и кто же и чем дышит?
Написал я тестовое приложение. Проверил — работает. Ну, думаю, сейчас посмотрим кто и что выбрасывает. Для интереса решил проверить Алтайский край. Всё-таки отдыхал там несколько раз — горы, воздух чистый.
Результат получил такой:
2 772 564 тонн в год! Да ладно…. камон! :)))) Причём, самый мощный источник — АЗС №2. АЗС, Карл! Это же что такое должно на той заправке происходить? Думал, что программа глючит. Нет, всё в норме — вот результат прямиком из реестра:
Судя по указанному выбросу, не то, что горящая спичка — косой взгляд со стороны должен приводить к взрыву газового облака :).
Ладно, дай думаю, проверю, что там у нас в Омской области происходит. Тут ещё смешнее:
Чтобы было проще понять: СОЛ = «Спортивно-Оздоровительный Лагерь». Итак, лагерь, в котором отдыхают детишки летом выбрасывает в атмосферу over 130 000 тонн загрязнений. Предположим (на минуточку), что детки попались не самые чистоплотные. Что же они выбрасывают. Внимание на экран:
Метан и Алканы! Что ж там им, бедным, подсыпают в еду, что их так пучит?
Москва оказалась тоже далеко не самым чистым городом:
Истово верю в то, что «Ашан» жгёт тряпки, потому как только этим можно объяснить такой выброс оксида углерода, который образуется преимущественно при горении:
И такие ляпы есть практически в каждом регионе. Что имеем в итоге?
Итог
Как было сказано выше, я не сторонник каких-то теорий заговоров. Более того, доподлинно знаю как работали экологи предприятий с этим реестром и, главное, что они вносили в заявки. И, могу с уверенностью сказать, что те ляпы, которые были выявлены выше — не намеренное искажение информации предприятиями РФ. Ну не придёт никому в здравом уме «засветить» выброс в 1000 000 раз превышающий то, что есть на самом деле. Другое дело: банальнейшие опечатки и выгрузка данных в JSON на сервере реестра и проверка введенных данных. Опечатки самые банальные — вместо 2,32E-09 записал 2,32E09 и получите-распешитесь. Но ведь не может так быть, что в каждом регионе нашелся такой человек, который взял и ошибся. Причём подозрительно одинаково в 109-1011 раз. Поэтому есть подозрение, что при выгрузке в JSON маленьких величин, типа 0,000000000001 происходит глюк и число «магическим» образом становиться 1000000000.
В итоге, сейчас без лишних танцев с бубуном, используя данные, мы можем:
- Оценить количество объектов в каждом регионе
- Дать расклад по категориям
- Сравнивать регионы по количеству объектов разных категорий и так далее.
Что касается выбросов, то я пока что намерен разобраться с тем в какой момент происходит глюк с числами — благо, что есть возможность проверить некоторые объекты по другим реестрам. Если будет интересно — отпишусь в следующий раз по результатам. А пока я разбираюсь, можете скачать модуль парсинга json и попробовать проверить, что твориться в вашем регионе:
Скачать модуль можно тут:
Здравствуйте, я нашел его очень usefull, потому что я учусь, как использовать delphi с jsoniterator. Но можете ли вы предоставить нам файл .dfm? Я уже скачал файл «.pas» .
Всего хорошего!
Я вижу, что никто не хочет мне помогать!
that’s what i did..
object Form1: TForm1
Left = 0
Top = 0
Caption = ‘Form1’
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnShow = FormShow
DesignerMasterStyle = 0
object FOktmo: TComboBox
Position.X = 272.000000000000000000
Position.Y = 120.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 22.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
end
object OKTMO: TLabel
Position.X = 96.000000000000000000
Position.Y = 120.000000000000000000
Size.Width = 120.000000000000000000
Size.Height = 25.000000000000000000
Size.PlatformDefault = False
Text = ‘OKTMO’
TabOrder = 1
end
object objets: TLabel
Position.X = 96.000000000000000000
Position.Y = 184.000000000000000000
Text = ‘Objets :’
TabOrder = 2
end
object Ejections: TLabel
Position.X = 96.000000000000000000
Position.Y = 216.000000000000000000
Size.Width = 161.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
Text = #201’jection dans l’#8217’atmosph’#232’re :’
TabOrder = 3
end
object plusdobjet: TLabel
Position.X = 96.000000000000000000
Position.Y = 248.000000000000000000
Text = ‘Le plus d’#8217’objet :’
TabOrder = 4
end
object CodeObjet: TLabel
Position.X = 96.000000000000000000
Position.Y = 288.000000000000000000
TextSettings.HorzAlign = Trailing
Text = ‘Code d’#8217’objet :’
TabOrder = 5
end
object Valeur: TLabel
Position.X = 104.000000000000000000
Position.Y = 320.000000000000000000
Size.Width = 112.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
TextSettings.HorzAlign = Trailing
Text = ‘Valeur , T/an :’
TabOrder = 6
end
object sourcepollution: TLabel
Position.X = 96.000000000000000000
Position.Y = 352.000000000000000000
Size.Width = 120.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
TextSettings.HorzAlign = Trailing
Text = ‘Source de pollution :’
TabOrder = 7
end
object Valeur1: TText
Position.X = 272.000000000000000000
Position.Y = 184.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
end
object Valeur2: TText
Position.X = 272.000000000000000000
Position.Y = 216.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
end
object Valeur3: TText
Position.X = 273.000000000000000000
Position.Y = 248.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
end
object Valeur4: TText
Position.X = 273.000000000000000000
Position.Y = 288.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
end
object Valeur5: TText
Position.X = 273.000000000000000000
Position.Y = 320.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
end
object Valeur6: TText
Position.X = 273.000000000000000000
Position.Y = 352.000000000000000000
Size.Width = 249.000000000000000000
Size.Height = 17.000000000000000000
Size.PlatformDefault = False
end
end