SASGIS - SAS.Планета
View Issue Details
0002763SAS.Планета[All Projects] Хотелкаpublic10-07-2015 20:3303-08-2015 08:43
GunSmoker 
GunSmoker 
normalminorN/A
resolvedfixed 
Windows8.1x64
141212 
150915150915 
0002763: Добавить возможность наложения слоёв при экспорте/копировании
Хочется выгрузить карту в PNG тайлы и подсунуть её программе. Нужен спутник (Google) с наложением поверх его гибрида.

В настоящее время возможность наложения есть только для экспорта в sqlite/RMaps ( 0001823 ).

Я посмотрел результат склейки RMaps - там в БД лежат готовые тайлы с уже наложенным поверх слоем. Т.е. код наложения уже есть и работает. Собственно, непонятно, что мешает сделать эту же возможность для всех прочих операций копирования/экспорта.
Когда-то такая задача решалась утилитой SASMerge, которая сегодня мертва и больше не доступна.
SQLite, склейка, экспорт
related to 0000325resolved GunSmoker При копировании кэша менять тип файлов (jpg->png) 
related to 0001680confirmed  экспорт в тайловый кэш с заменой проекции 
related to 0002779assigned GunSmoker Перенести копирование тайлов с изменением со влкадки Скопировать на вкладку Экспорт 
Issue History
10-07-2015 20:33GunSmokerNew Issue
10-07-2015 20:36GunSmokerTag Attached: SQLite
10-07-2015 20:36GunSmokerTag Attached: склейка
10-07-2015 20:36GunSmokerTag Attached: экспорт
10-07-2015 20:59zedNote Added: 0016120
12-07-2015 04:36GunSmokerNote Added: 0016142
12-07-2015 06:40zedNote Added: 0016143
12-07-2015 06:48zedNote Added: 0016144
12-07-2015 10:14vdemidovNote Added: 0016145
12-07-2015 16:10GunSmokerNote Added: 0016146
12-07-2015 16:14GunSmokerNote Added: 0016147
12-07-2015 16:24GunSmokerNote Edited: 0016146bug_revision_view_page.php?bugnote_id=16146#r6651
12-07-2015 16:27GunSmokerNote Edited: 0016146bug_revision_view_page.php?bugnote_id=16146#r6652
12-07-2015 16:29GunSmokerNote Edited: 0016146bug_revision_view_page.php?bugnote_id=16146#r6653
12-07-2015 17:08zedNote Added: 0016148
12-07-2015 17:23zedNote Added: 0016149
12-07-2015 18:00GunSmokerNote Added: 0016150
12-07-2015 18:39zedNote Added: 0016151
12-07-2015 19:32GunSmokerNote Added: 0016152
12-07-2015 19:52GunSmokerNote Added: 0016153
13-07-2015 00:19GunSmokerNote Added: 0016154
13-07-2015 01:34GunSmokerNote Added: 0016155
13-07-2015 02:05zedNote Added: 0016156
13-07-2015 02:11zedAssigned To => GunSmoker
13-07-2015 02:11zedStatusnew => assigned
13-07-2015 02:11zedProduct Version.Nightly => 141212
13-07-2015 02:11zedTarget Version => 150915
18-07-2015 09:43GunSmokerNote Added: 0016172
18-07-2015 10:00zedNote Added: 0016173
19-07-2015 04:10GunSmokerNote Added: 0016174
19-07-2015 07:58zedNote Added: 0016175
19-07-2015 09:22zedNote Added: 0016176
19-07-2015 15:06GunSmokerNote Added: 0016177
21-07-2015 04:37GunSmokerNote Added: 0016191
21-07-2015 05:22zedNote Added: 0016192
21-07-2015 05:36GunSmokerNote Added: 0016193
21-07-2015 07:30vdemidovNote Added: 0016199
21-07-2015 08:16GunSmokerNote Added: 0016201
21-07-2015 08:22vdemidovNote Added: 0016202
21-07-2015 11:32zedNote Added: 0016206
21-07-2015 11:37zedNote Added: 0016207
23-07-2015 07:01GunSmokerNote Added: 0016219
24-07-2015 09:54GunSmokerNote Added: 0016220
24-07-2015 10:29vdemidovNote Added: 0016221
24-07-2015 12:24GunSmokerNote Added: 0016222
24-07-2015 12:32GunSmokerNote Added: 0016223
24-07-2015 12:43vdemidovNote Added: 0016224
24-07-2015 12:44vdemidovNote Added: 0016225
26-07-2015 16:13zedNote Added: 0016229
26-07-2015 17:11vdemidovStatusassigned => resolved
26-07-2015 17:11vdemidovFixed in Version => 150915
26-07-2015 17:11vdemidovResolutionopen => fixed
31-07-2015 11:56vdemidovRelationship addedrelated to 0000325
31-07-2015 12:27vdemidovRelationship addedrelated to 0001680
03-08-2015 08:43vdemidovRelationship addedrelated to 0002779

Notes
(0016120)
zed   
10-07-2015 20:59   
>непонятно, что мешает сделать
Ничего не мешает. Как говорится - бери да делай :)
(0016142)
GunSmoker   
12-07-2015 04:36   
Если это, типа, подколка, то лично мне мешает полное незнание предметной области. Есть подозрение, что с какими-нибудь координатами и типами хранилища я так наворочу - мама не горюй. Но пока пытаюсь разобраться.
(0016143)
zed   
12-07-2015 06:40   
Это правда жизни - вопрос "что мешает?" можно задать в любом открытом тикете и ответ будет точно таким же. И незнание предметной области не является веской причиной, чтобы ничего не делать. Всё прекрасно познаётся "в процессе", так что велкам.

По поводу "наворочу": глядя на пример того же RMaps, я думаю, будет трудно наворотить. Для комбинирования слоёв с картой используется TBitmapLayerProviderMapWithLayer, который инициализируется картой и слоем (или списком слоёв), а потом у него последовательно запрашиваются тайлы в нужной проекции. Далее, результат конвертируется в нужный формат при помощи IBitmapTileSaver (который возвращает фабрика TBitmapTileSaveLoadFactory) и сохраняется в хранилище или ещё куда угодно. Главное понимать в какой проекции получился ваш тайл и какую проекцию ожидает/поддерживает хранилище или целевой формат, если речь о экспорте.
 
Проекцию вы може взять либо у исходного хранилища из которого читаете тайлы:
VGeoConvert := FTileStorage.CoordConverter;
VProjection := FProjectionFactory.GetByConverterAndZoom(VGeoConvert, FZoom);

Либо создать одну из 3-х доступных:
VGeoConvert := FCoordConverterFactory.GetCoordConverterByCode(
        CGoogleProjectionEPSG,
        CTileSplitQuadrate256x256
      );
VProjection := FProjectionFactory.GetByConverterAndZoom(VGeoConvert, FZoom);

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

Если что-то не понятно, спрашивайте.
(0016144)
zed   
12-07-2015 06:48   
Хороший пример создания IBitmapTileUniProvider можно подсмотреть в склейке, в функции TfrMapCombine.GetProvider - там на выбор можно либо наложить все отображаемые слои, либо один конкретный, ну или вообще не накладывать. Правда, в экспорте нужно учитывать, что если мы ничего не накладываем поверх карты и нас устраивает проекция и формат изображений тайлохранилища, то тайлы нужно передавать напрямую из хранилища, без лишней конвертации в битмапку. Т.е. этим провайдером не пользоваться.
(0016145)
vdemidov   
12-07-2015 10:14   
Вообще то, я давно планирую перевести экспорты оперирующие тайлами (JNX, сохранение в тайлохранилище и тд) на использование интерфейса ITileInfoUniProvider, у которого экспорт может запросить требуемый тайл с инофрмацией о типе и времени создания. А уже реализация этого провайдера может или напрямую передавать инфу из тайлохранилища, или склеивать и пределывать тайлы налету, или даже просто рендерить тайлы слоя заполнения или меток. Просто все руки никак не доходят сделать.
(0016146)
GunSmoker   
12-07-2015 16:10   
(edited on: 12-07-2015 16:29)
zed, может тогда подскажешь (запишу вопросы в том числе и себе, для упорядочивания в голове):
- Пока вижу так: в TThreadCopyFromStorageToStorage.ProcessTile загрузить тайл в bitmap, слить его с наложением, подсунуть в SaveTile.
- Тайлы всегда одинаковые по размеру (256x256)?
- ITileStorage.SaveTile.AData - передаётся, как я понимаю, содержимое файла. Как узнать, в каком формате нужно передавать (jpg/png)? IContentTypeInfoBasic? По строкам анализировать что-ли? Или по BOM из AData? Как узнать качество сжатия и цветность? Или выносить это как настройку в UI?
- ITileStorage.GetTileInfoEx - аналогичный вопрос: в каком формате возвращает.
- В TThreadCopyFromStorageToStorage.ProcessTile не вижу преобразования форматов (jpg->png;png->jpg). Кто это выполняет?
- Пока нет понимания, что делать с проекциями, как выполнять преобразования, если они нужны.
- В коде для экспорта в RMaps вижу упоминания CGoogleProjectionEPSG. Чем она специальна?
- Количество поясняющих комментариев угнетает :)

(0016147)
GunSmoker   
12-07-2015 16:14   
Кстати, с FastMM в FullDebugMode при закрытии вываливаются утечки, иногда - ошибки. Это так и должно быть? :)
(0016148)
zed   
12-07-2015 17:08   
>в TThreadCopyFromStorageToStorage.ProcessTile загрузить тайл в bitmap
Копирование из хранилища в хранилище происходит на самом низком уровне и там нету никаких преобразований. Тупо перекидывается бинарь из одного в другое. Чтобы оперировать битмапами и проекциями, нужно подняться на уровень выше, до IMapType - это уже карта, у неё можно запрашивать битмапы в любой известной проекции. А брать из хранилища тайлы и самому пытаться их распаковать в битмап, не стоит. И TBitmapLayerProviderMapWithLayer оперирует как раз картами, его и нужно использовать.

>Тайлы всегда одинаковые по размеру
Пока что да.

>Как узнать, в каком формате нужно передавать (jpg/png)?
Можно взять Saver у карты, а можно спросить и пользователя. См. как это сделано в TfrExportRMapsSQLite.GetBitmapTileSaver, там как раз все варианты присутствуют.

>ITileStorage.GetTileInfoEx - аналогичный вопрос: в каком формате возвращает
Можно узнать какой из интерфейсов поддерживается: IContentTypeInfoBitmap или IContentTypeInfoVectorData и получить Saver/Loader. Далее, можно сравнить свой Saver с полученным. Так можно проверить формат, но не настройки сохранения, они могут отличаться.

>вижу упоминания CGoogleProjectionEPSG. Чем она специальна?
Ничем. Просто RMaps других не понимает, если правильно помню, вот и происходит принудительная конвертация при необходимости.

>при закрытии вываливаются утечки, иногда - ошибки
Возможно это 0002050
(0016149)
zed   
12-07-2015 17:23   
Советую сделать свой отдельный экспорт с функцией наложения слоёв и выбором результирующего хранилища, проекции, формата тайлов и т.д. Взять за основу всё тот же RMaps. Из копирования можно взять момент с созданием интерфейса целевого хранилища. Скомбинировать всё это и получится желаемый результат. А вкладка копирования, это всё же отдельная песня и копирование обычно не подразумевает изменения. Так что туда лучше не встревать, особенно учитывая, что там есть возможность копировать сразу пачку хранилищ (и оно копируется не в одно хранилище, а точно в такую же пачку, но уже в другом месте).
(0016150)
GunSmoker   
12-07-2015 18:00   
Да-да, уже делаю две вкладки, одна - прямое копирование (как есть), вторая - с преобразованием (моё).
(0016151)
zed   
12-07-2015 18:39   
А смысл прямого копирования?
(0016152)
GunSmoker   
12-07-2015 19:32   
В смысле? Под "как есть" я имел в виду то, что сейчас делает вкладка "Скопировать". Там же прямое копирование тогда получается, или нет?
(0016153)
GunSmoker   
12-07-2015 19:52   
> Возможно это 0002050

Похоже некоторые потоки не закрываются при выходе из программы. Или не успевают завершиться. Возможно не отпускается ссылка на поток?
(0016154)
GunSmoker   
13-07-2015 00:19   
> Копирование из хранилища в хранилище происходит на самом низком уровне и там нету никаких преобразований. Тупо перекидывается бинарь из одного в другое.

1. Если источник - png, а сохраняется в jpg? Что тогда? Или получается, что формат хранилища не диктует формат файла?
2. Если не совпадают проекции источника/цели? Надо же конвертацию какую-то выполнить?
(0016155)
GunSmoker   
13-07-2015 01:34   
Кажется, что-то получилось.

Сделал новую вкладку, сделал отдельный модуль с новым потоком, на вход - BitmapLayerProviderMapWithLayer, как в RMaps, и TileStorage, как в Copy. Копирует с наложением в выбранный формат хранилища - ОК. Результат такого "модифицирующего" копирования вроде совпадает с "прямым копированием" - по файловой структуре и координатам в тайлах (за исключением самого содержимого рисунков, конечно - но если убрать слой наложения, то совпадает и контент рисунков).

Но, похоже, с преобразованием координат между слоями что-то не то (??). Если копировать гугл+гугл = ОК, если гугл+яндекс = ОК, а если яндекс+гугл = срабатывает Assert в TBitmap32StaticFactory.BuildWithOwnBuffer (FBackEndByStatic.FBitmapStatic = nil). Вроде как цикл в TMapType.LoadBitmap не выполняется ни разу. Сложно отлаживать, т.к. почему-то в этом коде 2007-я виснет постоянно (вместе с ОС).
(0016156)
zed   
13-07-2015 02:05   
>Под "как есть" я имел в виду
Ну вот мне и не понятно зачем делать тоже самое, что уже есть на другой вкладке. Разве что чисто потренироваться?

>Если источник - png, а сохраняется в jpg?
Как оно вдруг станет jpeg, если там нету конвертирования?

>Если не совпадают проекции источника/цели?
Источник и цель это кэш одной и той же карты, поэтому проекция там всегда одна. А вот если попытаться скопировать в кэш другой карты, то это уже на свой страх и риск, и нужно понимать что делаешь. Перепроецирования нету.

>а если яндекс+гугл = срабатывает Assert
Не видя кода, сложно сказать. Но "гугл+яндекс" это тоже разные проекции.

>2007-я виснет постоянно (вместе с ОС)
Я обычно использую XE2 при разработке, попробуй её. А D2007 только если приходится на старом железе в XP работать, ну и для проверки, что ночнушка соберётся.
(0016172)
GunSmoker   
18-07-2015 09:43   
Я XE в основном использую. Под ней на хостовой машине всё отладил - нашёл у себя в коде ошибку. Теперь вроде всё работает. Пока, правда, особо не тестировал.

Теперь вопрос - эти изменения как и куда заливать?
(0016173)
zed   
18-07-2015 10:00   
Порядок принятия изменений в код
(0016174)
GunSmoker   
19-07-2015 04:10   
zed, а не подскажешь, вот в 0000780 просили кэш вида <ZOOM>\<Y>\<X>.png. Мне как раз он нужен для экспорта. Вроде как он реализован в u_TileFileNameGM3.pas и обзывается rsGlobalMapperBingCacheName = 'GlobalMapper Bing'. Вопрос: почему он не создаётся в u_TileStorageTypeListSimple.pas?
(0016175)
zed   
19-07-2015 07:58   
Похоже, что про него просто забыли. Нужно добавлять.
(0016176)
zed   
19-07-2015 09:22   
Пофиксил: https://bitbucket.org/sas_team/sas.planet.src/commits/d271b7d95284db9041580a9865c3569a57eab0d3
(0016177)
GunSmoker   
19-07-2015 15:06   
Гы, я его тоже восстановил и думал заливать это или нет :)


Я ещё поиграюсь немного.
(0016191)
GunSmoker   
21-07-2015 04:37   
По основным исходникам - создал пул-реквест.

Но не могу сообразить, как правильно сделать внесение изменений в либы (requires)? Мне бы дефайны поправить для поддержки Delphi XE. Делаю форк и клон, вношу изменения, делаю коммит - а там же подхранилища, клиент хочет вносить изменения напрямую в ваши, а не в мой форк.
(0016192)
zed   
21-07-2015 05:22   
>По основным исходникам - создал пул-реквест.
Тогда ждём резолюции от vdemidov-а.
Ты, кстати, зачем свой форк приватным сделал? Этот реквест вживую теперь фиг проверишь перед принятием.

>как правильно сделать внесение изменений в либы (requires)
Делаешь клон конкретной либы, делаешь туда пул-реквест, а как их принять в корневой репо (requires) уже наша задача.

В какую либу надо вносить изменения?
(0016193)
GunSmoker   
21-07-2015 05:36   
> Ты, кстати, зачем свой форк приватным сделал? Этот реквест вживую теперь фиг проверишь перед принятием.

Я ж не знал :)

Поправил.

> Делаешь клон конкретной либы, делаешь туда пул-реквест, а как их принять в корневой репо (requires) уже наша задача.

Понял. У меня была проблема с поиском. Сейчас сделаю.
(0016199)
vdemidov   
21-07-2015 07:30   
Смотрю реквест. Ощущения двойственные. За первых три коммита вообще очень благодарен. Особенно за "Добавлены проверки на наличие указания выходного файла/папки перед началом работ по региону"
А вот к остальным есть претензии.
Во-первых, мне не нравится добавление работы с расширением в ITileFileNameGenerator, так как я его от туда старательно убирал (по факту этот интерфейс нужно было переименовать в ITileNameGenerator). А логика по добавлению '.tile' к именам файлов должна жить внутри соответствующего типа тайлохранилища (отдельный класс или дополнительный параметр в конструктор TTileStorageFileSystem)
Во-вторых, лучше было закладку копирования вообще не трогать, а добавить экспорт в тайлохранилище и там уже выбирать тип тайлохранилища в том числе и добавленный отдельно 'OsmAnd+ Tiles'
Ну и в-третьих, хорошо бы причесывать коммиты перед созданием пул реквеста, что бы по возможности не было некомпилируемых по-отдельности коммитов как получились два предпоследних.

Но, если сильно лень переделывать и так приму, так как пользы от реквеста все-таки гораздо больше чем моих претензий.
(0016201)
GunSmoker   
21-07-2015 08:16   
Могу переделать вот это, но, наверное, будет это не ранее след. выходных:

"Во-первых, мне не нравится добавление работы с расширением в ITileFileNameGenerator, так как я его от туда старательно убирал (по факту этот интерфейс нужно было переименовать в ITileNameGenerator). А логика по добавлению '.tile' к именам файлов должна жить внутри соответствующего типа тайлохранилища (отдельный класс или дополнительный параметр в конструктор TTileStorageFileSystem)"
(0016202)
vdemidov   
21-07-2015 08:22   
Тогда принимаю как есть, а там разберемся.
(0016206)
zed   
21-07-2015 11:32   
Обращаю внимание на:
1. [DCC Warning] u_TileFileNameOsmAnd.pas(101): H2443 Inline function 'SameFileName' has not been expanded because unit 'Windows' is not specified in USES list
2. В dpr файле не отсортированы имена юнитов (нужно запустить Tools\UnitsSort.py см. 0002693)
3. В гуе на вкладке скопировать не вмещаются контролы, окно нужно растянуть по вертикали, чтобы по-умолчанию всё было видно.
(0016207)
zed   
21-07-2015 11:37   
И вопрос по поводу пул-реквеста в alcinoe - там точно эти фиксы необходимы?
(0016219)
GunSmoker   
23-07-2015 07:01   
Замечания поправлю.

> там точно эти фиксы необходимы?

Да, конечно. В XE же нет namespace-в для стандартных модулей. Они появились только в XE2. Так что для XE2 - "System.AnsiStrings.ЧТОТО", для XE - "AnsiStrings.ЧТОТО".
(0016220)
GunSmoker   
24-07-2015 09:54   
Можно вопрос: зачем пишется код вида:

destructor TSomething.Destroy;
var
  I: integer;
begin
  for I := 0 to Length(FItems) - 1 do begin
    FItems[I] := nil;
  end;
  FItems := nil;
  inherited;
end;

Где FItems - динамический массив из интерфейсов.

Это для упрощения отладки? Или остатки от старого не-интерфейсного кода?
(0016221)
vdemidov   
24-07-2015 10:29   
Скорее всего остатки старого неинтерфейсного кода, или случая когда FItems было просто указателем, или случая когда FItems было TList
(0016222)
GunSmoker   
24-07-2015 12:24   
"мне не нравится добавление работы с расширением в ITileFileNameGenerator, так как я его от туда старательно убирал (по факту этот интерфейс нужно было переименовать в ITileNameGenerator). А логика по добавлению '.tile' к именам файлов должна жить внутри соответствующего типа тайлохранилища (отдельный класс или дополнительный параметр в конструктор TTileStorageFileSystem)"

Как в этом случае быть с экспортом в архив? Для генерации имени файла внутри архива там используется TileNameGenerator, а не хранилище (хранилище идёт только как источник). А расширение там вообще присовокупляется вручную - прямо в цикле. Создавать полноценный ITileStorage только для этого?
(0016223)
GunSmoker   
24-07-2015 12:32   
Может сделать так: пусть TileNameGenerator поддерживает (новый) доп. интерфейс, в который вынести формирование имени файла. Пусть дефолтный TileStorage пытается запрашивать у TileNameGenerator этот новый интерфейс. Если он есть - делегировать формирования имени ему. Если нет - добавить расширение самому. Тогда не понадобится два новых класса для OsmAnd (TileStorage и TileStorageType). И экспорт в архив сможет правильно формировать имена файлов, не имея на руках полноценного TileStorage.
(0016224)
vdemidov   
24-07-2015 12:43   
> Создавать полноценный ITileStorage только для этого?
Может и не совсем полноценный, а работающий только на запись, но ИМХО это правильней.
Сделать список всех доступных типов тайлохранилищ. У каждого есть набор доступных режимов работы: на импорт (однократный проход итератором), на экспорт (однократная запись), произвольный доступ на чтение, произвольный доступ на запись. И уже из этого списка формировать доступные для разных операций типа экспорта или параметров карты.
Тогда можно будет избавиться от отдельных экспортов в разные архивы. Это будет один экспорт в тайлохранилище с выбором типа этого тайлохранилища.
 
Но это так - мечты. Я пытаюсь постепенно к этом привести всю систему, но времени мало и часто отвлекаюсь на другие задачи. Поэтому и не настаивал на каких либо исправлениях.
(0016225)
vdemidov   
24-07-2015 12:44   
>Может сделать так: пусть TileNameGenerator поддерживает (новый) доп. интерфейс, в который вынести формирование имени файла.
Не, оно того не стоит.
(0016229)
zed   
26-07-2015 16:13   
Может уже стоит отметить тикет как отработанный? Или там планируется ещё что-то сделать?