уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Хотел было назвать пост как «Кому на Руси жить хорошо», но начал тестировать приложение и понял — заголовок не совсем будет соответствовать действительности. Учитывая, что большая часть посетителей блога webdelphi.ru из России (49% по Alexa Rank), то сегодняшний пост можно было бы назвать остросоциальным, так как сегодня я затрону тему экологии, а именно — выбросов загрязняющих веществ в атмосферный воздух. Ну, а так как блог по программированию, то, конечно же, основная часть статьи будет касаться практического применения библиотеки «Readers and Writers JSON Framework» для анализа открытых данных.

О чем речь?

Думаю, что, если вы житель России, то в курсе, что 2017 год был объявлен в стране годом экологии. Надо сказать, что за 2017-2018 гг., да и в 2019-м выявлено и предано публичной огласке достаточно много различных проблем, связанных с охраной окружающей среды. Всплывали и откровенно нелицеприятные проблемы, как, например, знаменитый в Омске «этилмеркаптановый» скандал, когда концентрация загрязняющего вещества (которое было принято за этилмеркаптан) была превышена в отдельных точках города более, чем в 400 раз или массовое отравление детей «неизвестным газом» в Волокаламске и так далее. Такие громкие скандалы, в свою очередь, приводили как к серьезным разбирательствам, так и к откровенной спекуляции, крикам «мы все умрем», «нас специально травят» и тому подобное. Но сегодня я не хочу кого-то обсуждать или, тем более, осуждать. Я не политик, не общественный деятель и не сторонник конспирационной теории, да и блог webdelphi не то место, где стоит публиковать темы, связанные с социальными проблемами общества.

Но знать кто и чем дышит (в прямом смысле этого слова) хотелось бы. И сегодня попробуем разобраться со следующими вопросами:

  1. На сколько является открытой и достоверной информация о загрязнении атмосферного воздуха в Российской Федерации?
  2. Где в нашей стране сосредоточено больше всего опасных производств?
  3. Какие предприятия и в каких регионах более всего загрязняют атмосферный воздух?

И ответить на эти вопросы нам поможет 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 будет таким:

  1. Создаем экземпляр TJSONIterator 
  2. Заходим внутрь массива (выполняем метод Next)
  3. Получаем доступ к полям очередного элемента json-массива (выполняем метод Recurse). На этом шаге мы должны оказаться внутри объекта перед полем Name
  4. Перебираем каждую пару в объекте (снова метод Next) и полезную для нас информацию временно сохраняем, например, в запись (record).
  5. Дойдя до пары Record.Code проверяем нужен ли нам этот объект для анализа — вполне возможно, что мы захотим получить информацию только по Московской области и лишние объекты надо будет пропускать.
    1. Если объект нам не нужен, то удаляем всю временно сохраненную информацию и переходим к следующему элементу массива (вызываем методы Return и Next)
    2.  Если объект нам необходим, то двигаемся по объекту дальше
  6. После прочтения всего объекта Record, снова вызываем Return, Next и попадаем на объект Facts
  7. Заходим внутрь Facts (метод Recurse)
  8. Заходим в объект Air (вызываем Next(‘Air’) и Recurse)
  9. Читаем количество источников загрязнения (ObjectCount), Вызываем Next(),. чтобы попасть в массив Air.Facts 
  10. Проходим по массиву Air.Facts и возвращаемся на самый верх, то есть вызываем один раз Return, чтобы подняться до уровня Air.Facts, чтобы подняться до уровня Air и третий раз — чтобы выйти на самый верх json-массива
  11. Повторяем все пункты, начиная со второго.

В общем, выглядит алгоритм не самым простым образом, но, главное здесь — это не запутаться в количестве вызовов Next/Recurse/Return. Иначе рискуем оказаться не на том уровне объекта и не дойти до конца файла.

Отдельно отмечу пункт 4 алгоритма. Дело в том, что, если нам необходимо фильтровать данные, то временная структура для хранения данных нам необходима по той причине, что TJSONIterator не может при прохождении json отойти на один шаг назад — только вперед или же на самое начало. И, если не предусмотреть временное хранение полученных ранее данных, то нам придётся бесконечно много раз подниматься на самый первый элемент массива, что потребует лишнего времени. Так что ИМХО лучше пожертвовать лишней парой сотней килобайт оперативной памяти, чем часом времени. Переходим к работе над приложением.

Разработка приложения для анализа данных Росприроднадзора

За основу интерфейса приложения я взял пример delphi по работе с компонентом TSplitView. Поэтому интерфейс (в черновом варианте) получился такой:

Будем фильтровать данные по регионам и выводить следующую информацию:

  1. Количество объектов НВОС в регионе
  2. Общий выброс загрязняющих веществ в атмосферу
  3. Информация по самому крупному объекту (с самым большим выбросом) — наименование, код, суммарный выброс, количество источников загрязнения
  4. Дополнительно, после парсинга 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.

В итоге, сейчас без лишних танцев с бубуном, используя данные, мы можем:

  1. Оценить количество объектов в каждом регионе
  2. Дать расклад по категориям
  3. Сравнивать регионы по количеству объектов разных категорий и так далее.

Что касается выбросов, то я пока что намерен разобраться с тем в какой момент происходит глюк с числами — благо, что есть возможность проверить некоторые объекты по другим реестрам. Если будет интересно — отпишусь в следующий раз по результатам. А пока я разбираюсь, можете скачать модуль парсинга json и попробовать проверить, что твориться в вашем регионе:

Скачать модуль можно тут:

Исходники — XML и JSON

 

5 1 голос
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
3 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
odyright
odyright
02/05/2020 18:01

Здравствуйте, я нашел его очень usefull, потому что я учусь, как использовать delphi с jsoniterator. Но можете ли вы предоставить нам файл .dfm? Я уже скачал файл «.pas» .
Всего хорошего!

odyright
odyright
06/05/2020 18:38
Ответить на  odyright

Я вижу, что никто не хочет мне помогать!

odyright
odyright
06/05/2020 18:41
Ответить на  odyright

that’s what i did..

Спойлер
[spoiler title=»NVOS.JsonParser.dfm «]

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

[/spoiler]