zondag 18 december 2011

A Google map and a Streetview panel in your application

Including a Google map in your APEX application is of course not difficult anymore. You can find lots of examples of how to do that on the internet. But what about a Google map and a streetview panel in one page? It is possible, including a marker on the map that corresponds to the picture on the streetview panel. And navigating through the streetview panel also moves the marker on the map. And the coordinates (latitude and longitue) are captured into two text items, together with the yaw, the pitch and the zoom. The yaw is the view movement from left to right and the pitch is the view movement from the ground to the sky.

Here's how to do it:

Note: regarding the items and the button, replace the X by the pagenumber you are working on. And replace <your_key> by your Google Maps API key. You can obtain a key via https://developers.google.com/maps/documentation/javascript/tutorial

- Create the following procedure in the database:

create or replace PROCEDURE show_map (
                      p_map_div IN VARCHAR2 DEFAULT 'map-canvas', p_streetview_div IN VARCHAR2 DEFAULT 'pano', p_lat_item_name IN VARCHAR2
                     ,p_lng_item_name IN VARCHAR2, p_yaw_item_name IN VARCHAR2, p_pitch_item_name IN VARCHAR2
                     ,p_zoom_item_name IN VARCHAR2
                     )
  IS
    l_mapstr       varchar2 (32000);
  begin
    l_mapstr    :=    '
    <script src="https://maps.googleapis.com/maps/api/js?key=<your_key>&sensor=false">
    </script>
    <script type="text/javascript">
    var map;
    var panorama;
    var marker;
    var streetview;
    function initialize(myAddress, myLat, myLng, myPOV) {
      var mapOptions = {zoom      : 12
                       ,mapTypeId : google.maps.MapTypeId.ROADMAP
      }   
      map            = new google.maps.Map(document.getElementById('||''''|| p_map_div ||''''||'),mapOptions);
      panorama       = new google.maps.StreetViewPanorama(document.getElementById('||''''||p_streetview_div||''''||'));
      streetview     = new google.maps.StreetViewService();
      geocoder       = new google.maps.Geocoder();
      geocoder.geocode( { '||''''||'address'||''''||': myAddress}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          var markerOptions = {map       : map
                              ,draggable : true
                              ,animation : google.maps.Animation.DROP
                              ,flat      : false
                              ,position  : results[0].geometry.location
          }
          map.setCenter(results[0].geometry.location);
          marker = new google.maps.Marker(markerOptions);
          streetview.getPanoramaByLocation(results[0].geometry.location, 50, showStreetviewData);
          google.maps.event.addListener(marker, '||''''||'dragstart'||''''||', function() {map.closeInfoWindow();} );
          google.maps.event.addListener(marker, '||''''||'dragend'||''''||', function(event) {
            document.getElementById("'||p_lat_item_name||'").value=event.latLng.lat();
            document.getElementById("'||p_lng_item_name||'").value=event.latLng.lng();
            streetview.getPanoramaByLocation(event.latLng, 50, showStreetviewData);
          });
        } else {
          document.getElementById("'||p_map_div||'").innerHTML = "No mapdata found for given address. Did you enter a correct address?";
        }
      });
    }
  
    function showStreetviewData(data, status) {
      if (status == google.maps.StreetViewStatus.OK) {
        panorama.setPano(data.location.pano);
        panorama.setPov({heading : 270
                        ,pitch   : 0
                        ,zoom    : 1
        });
        panorama.setVisible(true);
        google.maps.event.addListener(panorama, '||''''||'position_changed'||''''||', function(event) {
          var panoramaInfo = panorama.getPosition();
          document.getElementById("'||p_lat_item_name||'").value=panoramaInfo.lat();
          document.getElementById("'||p_lng_item_name||'").value=panoramaInfo.lng();
          marker.setPosition(new google.maps.LatLng(panoramaInfo.lat(),panoramaInfo.lng()));
        });
        google.maps.event.addListener(panorama, '||''''||'pov_changed'||''''||', function(event) {
            var panoInfo   = panorama.getPov();
            document.getElementById("'||p_pitch_item_name||'").value=panoInfo['||''''||'pitch'||''''||'];
            document.getElementById("'||p_yaw_item_name||'").value=panoInfo['||''''||'heading'||''''||'];
            document.getElementById("'||p_zoom_item_name||'").value=panoInfo['||''''||'zoom'||''''||'];
        });
      } else {
          document.getElementById("'||p_map_div||'").innerHTML = "No Streetview data for this location?";
        }
    }
    </script>';
    --
    sys.htp.p (l_mapstr);
  EXCEPTION
    WHEN OTHERS
    THEN
      raise_application_error (-20000, 'error in show_map: ' || SQLERRM);
  END show_map;




A little explanation of the code:

This procedure uses a variable that holds Javascript code which is outputted by the sys.htp.p function. The procedure is called from the pl/sql region. The parameters are the name of the two divs where the google map and streetview panel are rendered, and the names of the items that hold the geo details, like latitude, longitude, yaw, pitch and zoom.

The code makes use of the Google api to find the latitude and the longitude for a given address. Furthermore is the code event-driven for the movement of the marker and the navigation through the streetview map. This recipe makes use of the Google maps api v3. Previously, this recipe used v2, which will be deprecated.

In the APEX builder, create an empty page.
Create 4 HTML regions. Call them Address details, Geo details, Google Map and Streetview map.
Click on the Geo details region and select 2 from the Column selectlist in the User Interface section. Click the apply changes button. Do the same for the Streetview map region.
Click on the Google map region and put the following code in the region source text area:

<div align="center" style="width: 100%; height: 300px;">
  <div id="map_canvas" style="width: 500px; height: 300px"></div>


- Click apply changes
- Click on the streetview map region and put the following code in the region source text area:

  <div id="pano" style="width: 500px; height: 300px"></div>
</div>


- Click apply changes
- Create the following items:

Region Address details:

- PX_ADDRESS
- PX_CITY
- PX_COUNTRY

Region Geo details:

- PX_LAT
- PX_LNG
- PX_YAW
- PX_PTCH
- PX_ZM

- In the buttons section, click the add button icon
- Select the address details region
- Select "create button among this region's items"
- Enter a name for the button, as well as a label. Click next
- Click next
- In the action selectlist, select "defined by dynamic action"
- In the execute validations selectlist, select no. click next
- Click create button

- In the dynamic actions section, click the add button
- Select advanced
- Wnter a name for the dynamic action, ie find_address. Click next
- In the event selectlist, select mouse button press
- In the selection type selectlist, select button
- In the button select list, select the button you just created
- Click next
- In the action selectlist, select "execute javascript code"
- In the code text area, enter the following code:

initialize(document.getElementById("PX_ADDRESS" ).value + "," + document.getElementById("PX_CITY" ).value + "," + document.getElementById("PX_COUNTRY" ).value,0,0,{yaw:0,pitch:0,zoom:0});

- Click next, click create
- Create a pl/sql region and enter the following code into the region source text area:

show_map('map_canvas','pano','PX_LAT','PX_LNG','PX_YAW','PX_PTCH','PX_ZM');
sys.htp.p('<script type="text/javascript">');
sys.htp.p('initialize('||''''||'Leidscheplein, Amsterdam,NL'||''''||','||0||','||0||',{yaw:'||0||',pitch:'||0||',zoom:'||0||'});');
sys.htp.p('</script>');


You can of course enter your own favourite address. In the User Interface section, select "No Template"  in the Template select list. Click apply changes.

The page is ready now. The result should look something like this:




Try to move the red marker on the google map. You will see that the latitude and the longitude columns change and that the streetview changes. In the streetview map, click on the arrows on the road to navigate through the map. You will see that the red marker on the google map also moves and that the geo details change.

By the way, you can get the regions straight put next to one another and stacked by putting the following into the Region attributes text field in the Attributes section of the Address details and Geo details regions:

style="height:200px"

9 opmerkingen:

  1. Hi,
    When I try to follow your example, I get the message that I need an API key. Where do I get that and where do I put that?

    Thanks,
    Pat Miller

    BeantwoordenVerwijderen
  2. Hi,
    Actually I thought that ver 3 API did not require a key?

    Pat Miller

    BeantwoordenVerwijderen
  3. Hi I get an error message that says show_map must be declared. I am not that familiar with javascript. Thanks in advance

    BeantwoordenVerwijderen
    Reacties
    1. Hi Neil
      show_map is a database procedure. You need to create it in the database. The procedure is called from APEX and actually creates a javascript function that in turn is called from a pl/sql region. If you created the procedure and you still get the error message, you might have to grant privileges and create a (public) synonym.
      Regards,
      Marcel

      Verwijderen
  4. hi, please help me.. I follow your example,but dont show any map. any error, anything.

    BeantwoordenVerwijderen
    Reacties
    1. Hi Anthony, It seems that a lot of things have changed since the last time that I modified this page. This recipe was based on the Google Maps API v2 and that version will be deprecated soon. I will have to rewrite the code for v3, where a number of names of methods have changed, so it will take some time.

      Verwijderen
  5. Can we put marker image on street panorama view Google Map .

    BeantwoordenVerwijderen
  6. I did my own Google map and a Streetview panel http://findstreetview.com/?latlng=21.165542,-90.03644199999996&heading=-16.766851718943307&pitch=8.059430764328443&zoom=1

    BeantwoordenVerwijderen
  7. Hi I followed your example step by step, the page loads and the map and streetview show, but when I type in an address it does not give me the geodetails and the map stays in amsterdam. I even used the example address and it does the same

    BeantwoordenVerwijderen