logo

30 сент. 2013 г.

BIEE 11g: WriteBack на карте

Всем привет!
Наверняка, многие из читателей этого блога так или иначе использовали функциональность аналитики "на карте".
Причем, скорее всего, применяли отображение показателей по определенным точкам. Например, объем реализации по магазинам… С выделением цветом или изменением размера точки-"пузырька" в зависимости от значения показателя.




Для этого нужно не так уж много (не буду вдаваться во все детали) – основная сложность тут лишь в корректных значениях координат ваших точек.
Получить эти координаты можно различными способами: GPS-приборами, поиском по какому-нибудь картографическому сервису нужного дома (Google Map это отлично умеет) и т.д.
Но все это требует времени и сил.
А что если задача усложняется нерегулярностью появления новых гео-точек для бизнес-анализа?
Например, каждый запуск ETL-процедур может привести к появлению новых локаций с пустыми координатами в DWH,
либо как в моем случае – необходимо обеспечить возможность создания пользователем новых гео-локаций (магазины-конкуренты), а также возможность позиционирования этих точек на карте.

Я эту задачу решил следующим образом:



В специально настроенном анализе BI выводится информация с уникальным идентификатором магазина, его названием, его координатами (широта и долгота, пустые поначалу).
Рядом с табличной частью анализа выводится представление "Карта" без каких-либо слоев отображения.
В свойствах столбца с названием магазина на вкладке "Взаимодействие" настроен вызов ActionLink с типом BrowserScript, суть которого в том, чтобы при вызове отобразить на текущей активной карте "красную точку" с возможностью ее перемещения мышкой (очевидно, что "красная точка" - вырожденный инструмент mapviewer.redline – должна отображаться в текущем центре карты, если координаты пустые, либо в месте, соответствующем имеющимся координатам магазина).


Также в анализ я добавил фиктивный столбец-константу со значением "Сохранить", на который тоже назначен ActionScript, суть которого в передаче ajax-запроса на сервер с уникальным кодом редактируемого магазина и его измененными координатами.


Ясно, что основной интерес как раз представляет содержимое файла USERSCRIPTS.js, обеспечивающее указанную функциональность.
Напоминаю, что в Weblogic применяется хитрая стратегия кеширования, поэтому искать и править ВАШ файл UserScripts.js нужно в недрах WLS.
У меня на рабочем сервере искомый файл находится в папке
c:\Middleware117\user_projects\domains\bifoundation_domain\servers\AdminServer\tmp\_WL_user\analytics_11.1.1\silp1v\war\res\b_mozilla\actions\UserScripts.js

Вот его содержимое:
USERSCRIPT = function () {};
USERSCRIPT.parameter = function (b, a, c) {
    this.name = b;
    this.prompt = a;
    this.value = c
};

USERSCRIPT.startEditLocation = function (d) {

    var locationKey = d.locationKey;
    var locationName = d.locationName;

    var coordX = parseFloat(d.coordX);
    var coordY = parseFloat(d.coordY);

    //var mapview = window.myMapView;
    var mapview = obips.Map.getMapView(obips.Map.Factory.getSingleton().currentMapID);

    var redline = window.myRedline;

    if (redline != null) redline.clear();

    redline = new MVRedlineTool(null, "MDSYS.C.RED");

    redline.setAutoClose(false);
    redline.setControlPanelVisible(false);
    redline.setEditingMode({
        deletePoint: false,
        dragPoint: true,
        deleteLine: false,
        dragLine: false
    });

    window.myRedline = redline;
    mapview.addRedLineTool(redline);

    redline.init(1);

    var centerPoint;
    if (coordX != null && coordX != 0) {
        centerPoint = MVSdoGeometry.createPoint(coordX, coordY, 8307);
        centerPoint = mapview.transformGeom(centerPoint, 3785);

    } else {
        centerPoint = mapview.getCenter();
    }
    mapview.setCenter(centerPoint);

    redline.addVertex(0, centerPoint.getPointX(), centerPoint.getPointY());

    window.myLocationKey = locationKey;

    var md1 = window.myMapDecoration;

    if (md1 != null) {
        mapview.removeMapDecoration(md1);
    }

    md1 = new MVMapDecoration(locationName, 0.1, 0.1, 200, 20);
    mapview.addMapDecoration(md1);
    window.myMapDecoration = md1;
};
USERSCRIPT.startEditLocation.publish = {
    parameters: [new USERSCRIPT.parameter("locationKey", "Enter value for locationKey", ""),
        new USERSCRIPT.parameter("locationName", "Enter value for locationName", ""),
        new USERSCRIPT.parameter("coordX", "Enter value for coordX", ""),
        new USERSCRIPT.parameter("coordY", "Enter value for coordY", "")
    ]
};

USERSCRIPT.endEditLocation = function (d) {

    var locationKey = window.myLocationKey;

    if (locationKey == null) return;

    var redline = window.myRedline;
    var mapview = window.myMapView;

    if (redline == null) return;

    if (redline != null) {
        var coords = redline.getOrdinates().toString();
        var coordsArray = coords.split(",");

        var currentPoint = MVSdoGeometry.createPoint(parseFloat(coordsArray[0]), parseFloat(coordsArray[1]), 3785);
        currentPoint = mapview.transformGeom(currentPoint, 8307);

        var url = "http://" + document.location.host + "/analyticsTools/saveLocationCoord.jsp?";
        url += "locationKey=" + encodeURIComponent(locationKey);
        url += "&x=" + currentPoint.getPointX() + "&y=" + currentPoint.getPointY();

        var xmlHttp;
        try {
            // Firefox, Opera 8.0 + , Safari
            xmlHttp = new XMLHttpRequest();
        } catch (e) {
            // Internet Explorer
            try {
                xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                try {
                    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) {
                    alert("Your browser does not support AJAX!");
                    return false;
                }
            }
        }

        xmlHttp.onreadystatechange = function () {
            if (xmlHttp.readyState == 4) {
                window.status = "Done";
                document.body.style.cursor = "default";

                var h = xmlHttp.responseText;
                var d = obips.MessageDialog.TITLE_IMAGE_INFO;
                var c = new obips.MessageDialog.Model(null, h, null, d);
                var e = new obips.MessageDialog.Viewer(c, obips.FloatingWindow.Manager.getSingleton());
                e.footerDiv.style.textAlign = "center";

                RefreshPage();

            }
        }

        window.status = "Please wait...";
        document.body.style.cursor = "wait";

        xmlHttp.open("POST", url, true);
        xmlHttp.send(null);

    }

    var md1 = window.myMapDecoration;
    if (md1 != null) {
        mapview.removeMapDecoration(md1);
    }
};
USERSCRIPT.endEditLocation.publish = {
    parameters: [new USERSCRIPT.parameter("locationKey", "Enter value for locationKey", "")]
};

И наконец, код jsp-страницы saveLocationCoord.jsp, обеспечивающей прием ajax-запросов на сохранение координат и их запись в БД
(Я создал и задеплоил в WLS дополнительное веб-приложение analyticsTools, расположенное в каталоге сервера c:\Middleware117\Oracle_BI1\bifoundation\jee\analyticsTools.war
В корне которого и создал новую jsp-страницу)
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.net.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="java.util.*"%>
<%@ page import="javax.naming.*"%>

<%
try{
    String locationKey = URLEncoder.encode(request.getParameter("locationKey"), "UTF-8");
    String preCoordX = request.getParameter("x");
    String preCoordY = request.getParameter("y");
    
    double coordX = Double.parseDouble(preCoordX);
    double coordY = Double.parseDouble(preCoordY);    
        
    Context  ctx = new InitialContext();
    DataSource  ds = (DataSource) ctx.lookup ("jdbc/DWH_datasource");
    Connection conn = ds.getConnection();         
                                   
    String updateQuery = "update DWI_RIVAL_STORE_LCTN set longitude = ? , latitude = ? where location_key = ? ";   

    PreparedStatement updateStatement = conn.prepareStatement(updateQuery);
         
    updateStatement.setDouble(1, coordX);
    updateStatement.setDouble(2, coordY);
    updateStatement.setString(3, locationKey);
  
    updateStatement.executeUpdate();
   
    conn.commit();   
      
    updateStatement.close();
    updateStatement = null;

    out.write("Success! Refresh the page");
}
catch (Exception ex) {
    out.write(ex.toString());
}
%>

Очевидно, что для корректной работы JSP-кода необходимо наличие JNDI-источника данных с именем jdbc/DWH_datasource


Вот и все!

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

  1. Прикольная идея!

    Побуду буквоедом, сам сейчас с этим копаюсь:
    “(Google Map это отлично умеет)”
    — плохой совет, потому что:
    The Geocoding API may only be used in conjunction with a Google map; geocoding results without displaying them on a map is prohibited. For complete details on allowed usage, consult the Maps API Terms of Service License Restrictions.
    https://developers.google.com/maps/documentation/geocoding/#Limits

    ОтветитьУдалить
  2. Согласен с Вами. Если строго следовать букве закона - то так делать нельзя!
    Но обычно именно так и поступают: открывают GMaps, тыкают мышкой в точку и выбирают в контекстном меню "Что тут находится" - в поле поиска появляются координаты; их затем и используют.

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