logo

11 дек. 2014 г.

BIEE11g: Mapviewer + мозаичная карта OSM Mapnik

Всем привет!

Сегодня в очередной раз хочу вернуться к теме визуализации данных Oracle BI на географической карте.
Тема эта в моем блоге мусолилась часто: BIEE 11g: Mapviewer + OpenStreetMap, BIEE11g: Mapviewer + OpenStreetMap как WMS + HttpProxy, BIEE 11g: WriteBack на карте
Но мне все равно есть что рассказать нового - как показывать в качестве фоновой карты картинки-ячейки мозаичной карты от LGPL-ресурса OpenStreetMap Mapnik.

Зачем это нужно?
Чтобы показывать в BI "красивые карты" с графической информацией о морях, реках, дорогах, рельефом местности, городами и т.д.
Все то, к чему мы привыкли, используя общедоступные картографические ресурсы (Google Maps, Yandex Maps etc). Если не использовать подгрузку этих внешних данных в виде картинок, то придется хранить в БД Oracle в виде SDO_GEOMETRY все привычные пользователям геометрии ("и тропинку, и лесок"), откуда-то брать эти данные, поддерживать в актуальном виде, и визуализировать с помощью mapviewer, нагружая его отрисовкой множества геометрических слоев, не связанных непосредственно с задачами аналитики в BI.

Но у нас уже настроена подгрузка фоновой карты в javascript-функциях
Да, и я сам описывал как это сделать в сообщении BIEE 11g: Mapviewer + OpenStreetMap. Но минус этого решения в том, что для отображения карты в браузере пользователя необходимо подключение к интернету. Также следует понимать, что при работе сотни пользователей с аналитическим отчетом в BI, данные которого визуализируются на карте, каждый пользователь будет скачивать из интернета одни и те же картинки-ячейки, увеличивая интернет-трафик.

Но разве не эта же проблема была решена в сообщении BIEE11g: Mapviewer + OpenStreetMap как WMS + HttpProxy
Да, именно. Вот только там есть ряд неточностей, связанных с тем, что WMS (web map service) ресурсы с данными OpenStreetMap периодически меняют свои адреса - http://wiki.openstreetmap.org/wiki/WMS. А также неприятность в том, что эти WMS ресурсы содержат не очень подробные данные по России. Гораздо более качественная, подробная и красивая картинка получается при работе с OSM Mapnik.



Итак, создадим новую фоновую карту в интерфейсе Oracle Mapviewer.
Тип карты - EXTERNAL


Прежде чем двигаться дальше - нужно скопировать в файловую систему сервера BI jar-архив mvadapter.jar. Этот jar-архив содержит скомпилированный java-класс, реализующий логику загрузки картинок-ячеек OSM Mapnik (код класс будет далее).


Указываем основные свойства карты

Name - OSM_MAPNIK
Data source - доступный в вашей настройке mapviewer
Map service Url - http://tile.openstreetmap.org/
Adapter class - mcsadapter.OSMMapnikAdapter
Jar file location - укажите путь в файловой системе сервера BI до mvadapter.jar (см. предыдущий шаг)
Tile storage - укажите путь в файловой системе до каталога, где будет храниться кеш всех картинок-ячеек мозаичной карты
Zoom levels - 1
Остальные свойства оставьте без изменений.

Сохраняем новую фоновую карту


SQL-запросом к БД, на которую настроен mapviewer, проверяем наличие созданной карты и значение поля DEFINITION
select t.rowid, t.* from USER_SDO_CACHED_MAPS t
where t.NAME = 'OSM_MAPNIK'


Но итоговое определение карты должно быть таким:

update USER_SDO_CACHED_MAPS t
set t.DEFINITION = 
    '<map_tile_layer name="OSM_MAPNIK" http_header_expires="168.0" image_format="PNG" fetch_larger_tiles="false">
       <external_map_source url="http://tile.openstreetmap.org/" request_method="GET" adapter_class="mcsadapter.OSMMapnikAdapter" transparent="false"/>
       <tile_storage root_path="/u02/Middleware/Oracle_BI1/bifoundation/jee/mapviewer.ear/web.war/WEB-INF/tilecache/SUPERMAG.OSM_MAPNIK"/>
       <coordinate_system srid="3785" minX="-2.0037508E7" maxX="2.0037508E7" minY="-2.0037508E7" maxY="2.0037508E7"/>
       <tile_image width="256" height="256"/>
       <zoom_levels levels="20" min_scale="0.0" max_scale="0.0" min_tile_width="76.43702697753906" max_tile_width="4.00750166855785E7">
          <zoom_level level="0" name="" description="" scale="0.0" tile_width="4.00750166855785E7" tile_height="4.00750166855785E7"/>
          <zoom_level level="1" name="" description="" scale="0.0" tile_width="2.0037508E7" tile_height="2.0037508E7"/>
          <zoom_level level="2" name="" description="" scale="0.0" tile_width="1.0018754E7" tile_height="1.0018754E7"/>
          <zoom_level level="3" name="" description="" scale="0.0" tile_width="5009377.0" tile_height="5009377.0"/>
          <zoom_level level="4" name="" description="" scale="0.0" tile_width="2504688.5" tile_height="2504688.5"/>
          <zoom_level level="5" name="" description="" scale="0.0" tile_width="1252344.25" tile_height="1252344.25"/>
          <zoom_level level="6" name="" description="" scale="0.0" tile_width="626172.125" tile_height="626172.125"/>
          <zoom_level level="7" name="" description="" scale="0.0" tile_width="313086.0625" tile_height="313086.0625"/>
          <zoom_level level="8" name="" description="" scale="0.0" tile_width="156543.03125" tile_height="156543.03125"/>
          <zoom_level level="9" name="" description="" scale="0.0" tile_width="78271.515625" tile_height="78271.515625"/>
          <zoom_level level="10" name="" description="" scale="0.0" tile_width="39135.7578125" tile_height="39135.7578125"/>
          <zoom_level level="11" name="" description="" scale="0.0" tile_width="19567.87890625" tile_height="19567.87890625"/>
          <zoom_level level="12" name="" description="" scale="0.0" tile_width="9783.939453125" tile_height="9783.939453125"/>
          <zoom_level level="13" name="" description="" scale="0.0" tile_width="4891.9697265625" tile_height="4891.9697265625"/>
          <zoom_level level="14" name="" description="" scale="0.0" tile_width="2445.98486328125" tile_height="2445.98486328125"/>
          <zoom_level level="15" name="" description="" scale="0.0" tile_width="1222.992431640625" tile_height="1222.992431640625"/>
          <zoom_level level="16" name="" description="" scale="0.0" tile_width="611.4962158203125" tile_height="611.4962158203125"/>
          <zoom_level level="17" name="" description="" scale="0.0" tile_width="305.74810791015625" tile_height="305.74810791015625"/>
          <zoom_level level="18" name="" description="" scale="0.0" tile_width="152.87405395507812" tile_height="152.87405395507812"/>
          <zoom_level level="19" name="" description="" scale="0.0" tile_width="76.43702697753906" tile_height="76.43702697753906"/>
       </zoom_levels>
    </map_tile_layer>'
where t.NAME = 'OSM_MAPNIK'
Необходимость этого шага обусловлена тем, что в веб-интерфейсе mapviewer нельзя задать важное свойство fetch_larger_tiles="false", а также сложностью ручного определения всех уровней детализации.

Выполняем обновление определения карты. И проверяем что изменилось в веб-интерфейсе mapviewer.


При попытке посмотреть превью карты, скорее всего, появится следующая ошибка


Для ее устранения необходимо в файле weblogic.xml раскомментировать узел library-ref для библиотеки jstl 1.2


После необходимо обязательно рестартовать приложение mapviewer через консоль WLS


И тогда превью карты будет работать без ошибок


Вот так будет выглядеть созданная фоновая карта в режиме настройки метаданных Oracle BI.


А вот так будет выглядить директория с кешируемыми картинками мозаичной карты


Содержимое подпапки 1 уровня детализации


И сами картинки-ячейки 1 уровня детализации


Теперь пользователи при работе с анализами BI, содержащими визуализацию на карте, будут получать данные из общего кеша, а не из интернета.

И напоследок - код класса, реализующего подгрузку tile-ячеек:
package mcsadapter;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Logger;

import oracle.lbs.mapserver.core.OMSException;
import oracle.mapviewer.share.mapcache.MapSourceAdapter;
import oracle.mapviewer.share.mapcache.TileDefinition;
import oracle.mapviewer.share.util.LogFactory;

public class OSMMapnikAdapter extends MapSourceAdapter {


private static Logger log;

static {
log = LogFactory.getLogger(oracle.mapviewer.share.util.LogFactory.LoggerEnum.SHARE);


public OSMMapnikAdapter() {
}

public String getMapTileRequest(TileDefinition tile) {
int zoomLevel = tile.getZoomLevel();
long mX = tile.getMeshX();
long mY = (long)Math.pow(2, zoomLevel) -  (long)tile.getMeshY() - 1;

return (new StringBuilder()).
append(Integer.toString(zoomLevel)).
append("/").append(Long.toString(mX)).
append("/").append(Long.toString(mY)).
append(".png").toString();
}

 
public byte[] getTileImageBytes(TileDefinition tiledefinition) throws Exception {  

String tileRequest = this.getMapServiceURL() + getMapTileRequest(tiledefinition);
    log.finer("XX_CUSTOM: " + tileRequest);
             
    Proxy proxy = null;
    boolean proxyFlag = "NONE".equalsIgnoreCase(getProxyHost());
    
    if (getProxyHost() != null && !proxyFlag) {
     InetSocketAddress inetsocketaddress = new InetSocketAddress(this.getProxyHost(), this.getProxyPort());
        proxy = new Proxy(java.net.Proxy.Type.HTTP, inetsocketaddress);
    }
        
    BufferedInputStream bufferedinputstream = null;
    byte tileBytes[];
    try {
     URL url = new URL(tileRequest);
        URLConnection urlconnection = proxy == null ? proxyFlag ? url.openConnection(Proxy.NO_PROXY) : url.openConnection() : url.openConnection(proxy);
        urlconnection.setConnectTimeout(this.getConnectionTimeout());
        urlconnection.connect();
        bufferedinputstream = new BufferedInputStream(urlconnection.getInputStream());
        tileBytes = toBytes(bufferedinputstream);
        bufferedinputstream.close();
        bufferedinputstream = null;
    }
    catch(Exception exception) {
     throw new Exception("Failed to fetch external map tile.", exception);
    }
    finally {
     try {
     if(bufferedinputstream != null) {
     bufferedinputstream.close();
            bufferedinputstream = null;
     }
     }
     catch(IOException ioexception1) {
     throw ioexception1;
     }
    }
        
    return tileBytes;
}
 
private byte[] toBytes(BufferedInputStream bufferedinputstream) throws OMSException {
ByteArrayOutputStream bytearrayoutputstream = null;
        try {
         bytearrayoutputstream = new ByteArrayOutputStream();
         byte abyte0[] = new byte[2048];
         for (int i = 0; 0 < (i = bufferedinputstream.read(abyte0));)
          bytearrayoutputstream.write(abyte0, 0, i);

         bytearrayoutputstream.close();        
        }
        catch(IOException ioexception) {
            throw new OMSException(ioexception);
        }

    return bytearrayoutputstream.toByteArray();

 
}


P.S. Что делать если на сервере с Oracle BI нет прямого доступа в интернет. Например, он доступен через корпоративную HTTP-прокси с аутентификацией. В этом случае рекомендую внимательно прочитать мою прошлу статью - BIEE11g: Mapviewer + OpenStreetMap как WMS + HttpProxy

2 комментария:

  1. Анонимный18 мая 2015 г., 17:40

    Ждем продолжений в стиле "BIEE11g: Mapviewer + локальный тайл сервер на базе ......?".
    Огромное Вам Спасибо за полезную информацию!

    ОтветитьУдалить
  2. Пожалуйста!
    Рад что кому-то полезны мои статьи.

    ОтветитьУдалить