Differences
This shows you the differences between two versions of the page.
google_maps [2012/03/31 21:48] srdjan.lukovic created |
google_maps [2012/07/09 10:56] (current) srdjan.lukovic [GUI Code] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Google Maps Sample ====== | + | ====== Interactive Maps ====== |
- | ... | + | |
+ | **Interactive Maps** is a [[SOLoist Sample Applications|SOLoist sample application]] that fully embeds [[http://maps.google.com/ | Google Maps]] that interact with the enclosing application by exhanging geo-coordinates. This example demonstrates how complex external components can be integrated with SOLoist code in order to interchange the data and keep them in sync. | ||
+ | |||
+ | The user can browse the map. By selecting an object of //House// in the suggest box, the map view will be positioned on the location based on the coordinates stored in the object's //coordinates// attribute. | ||
+ | |||
+ | When the marker is unlocked by unchecking the checkbox, and a different location is selected in the map, all the data from Google Maps will be automatically saved to the selected object's attributes. | ||
+ | |||
+ | The user can also type in an address in the text input field, when Google Maps provides the coordinates. | ||
+ | |||
+ | This way, the geo-coordinates from Google Maps and the values of attributes from SOLoist objects are kept in sync. | ||
==== Live example ==== | ==== Live example ==== | ||
- | [[http://soloistdemo.org/SampleApplications/googlemaps.html]]\\ | + | [[http://soloistdemo.org/SampleApplications/gmaps.html]]\\ |
[[http://soloistdemo.org/SampleApplications/oql?q=SELECT+h%2C+h.code%2C+h.address%2C+h.city%2C+h.country%2C+h.coordinates%0D%0AFROM+House+h&f=html | OQL Query: Houses]] | [[http://soloistdemo.org/SampleApplications/oql?q=SELECT+h%2C+h.code%2C+h.address%2C+h.city%2C+h.country%2C+h.coordinates%0D%0AFROM+House+h&f=html | OQL Query: Houses]] | ||
+ | |{{screen:googlemaps.png?300}}| | ||
===== UML Model ===== | ===== UML Model ===== | ||
Line 11: | Line 21: | ||
===== Business Logic Code ===== | ===== Business Logic Code ===== | ||
+ | None. | ||
+ | |||
+ | ===== GUI Code ===== | ||
<code java> | <code java> | ||
- | //... | + | package rs.sol.sampleapps.gmaps; |
+ | |||
+ | import rs.sol.sampleapps.House; | ||
+ | import rs.sol.soloist.server.guiconfiguration.components.GUIHTMLComponent; | ||
+ | import rs.sol.soloist.server.guiconfiguration.components.GUILabelComponent; | ||
+ | import rs.sol.soloist.server.guiconfiguration.components.GUIPanelComponent; | ||
+ | import rs.sol.soloist.server.guiconfiguration.construction.GUIComponentBinding; | ||
+ | import rs.sol.soloist.server.guiconfiguration.elementcomponents.GUIEdit; | ||
+ | import rs.sol.soloist.server.guiconfiguration.elementcomponents.GUIInput; | ||
+ | import rs.sol.soloist.server.guiconfiguration.nonvisualcompoments.GUIFindAllInstancesSAPComponent; | ||
+ | import rs.sol.soloist.server.uml.concepts.runtime.ISlot; | ||
+ | |||
+ | public class GoogleMapsFragment { | ||
+ | |||
+ | private ISlot<?> house; | ||
+ | private GUIPanelComponent rootPanel; | ||
+ | |||
+ | public GoogleMapsFragment(GUIPanelComponent rootPanel) { | ||
+ | this.rootPanel = rootPanel; | ||
+ | this.house = rootPanel.opRelay1(); | ||
+ | init(); | ||
+ | } | ||
+ | |||
+ | private void init() { | ||
+ | houseLocationDetails(); | ||
+ | |||
+ | GUIEdit coordinates = GUIEdit.createField(rootPanel, House.PROPERTIES.coordinates); | ||
+ | GUIComponentBinding.create(house, coordinates.ipElement()); | ||
+ | coordinates.setStyle("#coordinatesH"); | ||
+ | GUIHTMLComponent.create(rootPanel, "<div id=\"map_canvasH\"></div>"); | ||
+ | |||
+ | GUIHTMLComponent html1 = GUIHTMLComponent.create(rootPanel, ""); | ||
+ | html1.setStyle("#tab1886H"); | ||
+ | |||
+ | GUIComponentBinding.create(house, html1.ipHTML()); | ||
+ | } | ||
+ | |||
+ | private void houseLocationDetails() { | ||
+ | GUIPanelComponent table = GUIPanelComponent.createTable(rootPanel); | ||
+ | table.setStyle("form"); | ||
+ | |||
+ | int row = 0, col = 0; | ||
+ | GUILabelComponent.create(table, "House", row, col++).setStyle("formLabel"); | ||
+ | GUILabelComponent.create(table, "Address", row, col++).setStyle("formLabel"); | ||
+ | GUILabelComponent.create(table, "City", row, col++).setStyle("formLabel"); | ||
+ | GUILabelComponent.create(table, "Country", row, col++).setStyle("formLabel"); | ||
+ | |||
+ | GUIFindAllInstancesSAPComponent allHouses = GUIFindAllInstancesSAPComponent.create(rootPanel, House.CLASSIFIER); | ||
+ | GUIInput suggestBox = GUIInput.createSuggest(table); | ||
+ | GUIComponentBinding.create(allHouses.opValue(), suggestBox.ipCollection()); | ||
+ | |||
+ | GUIEdit seAddress = GUIEdit.createField(table, House.PROPERTIES.address); | ||
+ | GUIComponentBinding.create(house, seAddress.ipElement()); | ||
+ | seAddress.setStyle("#addressH"); | ||
+ | |||
+ | GUIEdit seCity = GUIEdit.createField(table, House.PROPERTIES.city); | ||
+ | GUIComponentBinding.create(house, seCity.ipElement()); | ||
+ | seCity.setStyle("#cityH"); | ||
+ | |||
+ | GUIEdit seCountry = GUIEdit.createField(table, House.PROPERTIES.country); | ||
+ | GUIComponentBinding.create(house, seCountry.ipElement()); | ||
+ | seCountry.setStyle("#countryH"); | ||
+ | |||
+ | row++; col = 0; | ||
+ | suggestBox.setRowColumn(row, col++); | ||
+ | seAddress.setRowColumn(row, col++); | ||
+ | seCity.setRowColumn(row, col++); | ||
+ | seCountry.setRowColumn(row, col++); | ||
+ | |||
+ | GUIComponentBinding.create(suggestBox.opValue(), rootPanel.ipRelay1()); | ||
+ | } | ||
+ | |||
+ | } | ||
</code> | </code> | ||
- | ===== GUI Code ===== | ||
<code java> | <code java> | ||
- | ... | + | package rs.sol.sampleapps.gmaps; |
+ | |||
+ | import rs.sol.sampleapps.House; | ||
+ | import rs.sol.soloist.helpers.init.DefaultContextInit; | ||
+ | import rs.sol.soloist.helpers.init.Initializer; | ||
+ | import rs.sol.soloist.helpers.init.InitializerFailedException; | ||
+ | import rs.sol.soloist.server.guiconfiguration.components.GUIApplicationComponent; | ||
+ | import rs.sol.soloist.server.guiconfiguration.components.GUILabelComponent; | ||
+ | import rs.sol.soloist.server.guiconfiguration.components.GUIPanelComponent; | ||
+ | import rs.sol.soloist.server.guiconfiguration.style.GUIContext; | ||
+ | import rs.sol.soloist.server.guiconfiguration.style.GUIObjectSetting; | ||
+ | import rs.sol.soloist.server.guiconfiguration.style.GUITextFeature; | ||
+ | import rs.sol.soloist.server.server.SoloistServiceServlet; | ||
+ | |||
+ | public enum GMaps implements Initializer { | ||
+ | |||
+ | INSTANCE; | ||
+ | |||
+ | @Override | ||
+ | public void init() throws InitializerFailedException { | ||
+ | GUIApplicationComponent application = new GUIApplicationComponent(); | ||
+ | application.setName("GMapsSample"); | ||
+ | SoloistServiceServlet.registerApplication(application); | ||
+ | application.setContext(createContextAndStyles()); | ||
+ | |||
+ | GUIPanelComponent root = GUIPanelComponent.createFlow(application); | ||
+ | |||
+ | GUILabelComponent title = GUILabelComponent.create(root, "Google Maps"); | ||
+ | title.setStyle("titleStyle"); | ||
+ | |||
+ | GUIPanelComponent topPanel = GUIPanelComponent.createFlow(root); | ||
+ | topPanel.setStyle("topPanel"); | ||
+ | |||
+ | new GoogleMapsFragment(topPanel); | ||
+ | } | ||
+ | |||
+ | private GUIContext createContextAndStyles() { | ||
+ | GUIContext context = new GUIContext(); | ||
+ | DefaultContextInit.getRoot().addContext(context); | ||
+ | |||
+ | GUIObjectSetting person = GUIObjectSetting.create(context, House.CLASSIFIER); | ||
+ | GUITextFeature.createName(person, House.PROPERTIES.code); | ||
+ | |||
+ | return context; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code javascript extension.js> | ||
+ | /** | ||
+ | * Javascript that controls behavior of external components. | ||
+ | */ | ||
+ | |||
+ | var mapH, markerH, positionH; | ||
+ | var geocoder; | ||
+ | var timeout = 100; | ||
+ | var lock = true; | ||
+ | var first = true; | ||
+ | |||
+ | var solHQ = new google.maps.LatLng(44.813209013839284, 20.464512819311494); | ||
+ | var lockText = 'Lock marker'; | ||
+ | var coorText = 'Coordinates'; | ||
+ | var buttonText = 'Geocode'; | ||
+ | |||
+ | function initialize() { | ||
+ | |||
+ | geocoder = new google.maps.Geocoder(); | ||
+ | markerH = new google.maps.Marker(); | ||
+ | |||
+ | var myOptions = { | ||
+ | zoom : 14, | ||
+ | center : solHQ, | ||
+ | scaleControl : true, | ||
+ | mapTypeId : google.maps.MapTypeId.ROADMAP | ||
+ | }; | ||
+ | |||
+ | var canvasId = "map_canvasH"; | ||
+ | var coorId = "coordinatesH"; | ||
+ | |||
+ | mapH = new google.maps.Map(document.getElementById(canvasId), myOptions); | ||
+ | positionH = coordinatesControl(mapH); | ||
+ | |||
+ | geocode(mapH); | ||
+ | lockMarkerControl(mapH); | ||
+ | |||
+ | google.maps.event.addListener(mapH, 'click', function(event) { | ||
+ | if (!lock) { | ||
+ | var coor = document.getElementById(coorId); | ||
+ | markerH.setPosition(event.latLng); | ||
+ | markerH.setMap(mapH); | ||
+ | coor.value = event.latLng; | ||
+ | positionH.innerHTML = parseLatLng(event.latLng); | ||
+ | var address = document.getElementById("addressH"); | ||
+ | var city = document.getElementById("cityH"); | ||
+ | var country = document.getElementById("countryH"); | ||
+ | codeLatLng(event.latLng, address, city, country); | ||
+ | coor.focus(); | ||
+ | coor.blur(); | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | drawMap(); | ||
+ | |||
+ | } | ||
+ | |||
+ | function codeLatLng(latlng, address, city, country) { | ||
+ | geocoder.geocode({ | ||
+ | 'latLng' : latlng | ||
+ | }, function(results, status) { | ||
+ | if (status == google.maps.GeocoderStatus.OK) { | ||
+ | if (results[0]) { | ||
+ | var ac = results[0].address_components; | ||
+ | var str = "", num = "", cty = "", cnt = ""; | ||
+ | for ( var i in ac) { | ||
+ | if (ac[i].types[0] == "street_number") | ||
+ | num = ac[i].long_name; | ||
+ | if (ac[i].types[0] == "street_address" || ac[i].types[0] == "route" || ac[i].types[0] == "intersection") | ||
+ | str = ac[i].long_name; | ||
+ | if (ac[i].types[0] == "locality") | ||
+ | cty = ac[i].long_name; | ||
+ | if (ac[i].types[0] == "country") | ||
+ | cnt = ac[i].long_name; | ||
+ | } | ||
+ | address.value = str + " " + num; | ||
+ | city.value = cty; | ||
+ | country.value = cnt; | ||
+ | address.focus(); | ||
+ | address.blur(); | ||
+ | city.focus(); | ||
+ | city.blur(); | ||
+ | country.focus(); | ||
+ | country.blur(); | ||
+ | } | ||
+ | } else { | ||
+ | alert("Geocoder failed due to: " + status); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function codeAddress(address, coor) { | ||
+ | geocoder.geocode({ | ||
+ | 'address' : address | ||
+ | }, function(results, status) { | ||
+ | if (status == google.maps.GeocoderStatus.OK) { | ||
+ | coor.value = results[0].geometry.location; | ||
+ | coor.focus(); | ||
+ | coor.blur(); | ||
+ | drawMap(); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function parseLatLng(latLng) { | ||
+ | if (latLng === "") | ||
+ | return coorText + " ( - )"; | ||
+ | |||
+ | var dec = 1000000; | ||
+ | var ll = new Array(); | ||
+ | ll = latLng.toString().substring(1, latLng.toString().length - 1).split(', '); | ||
+ | return coorText + " (" + (Math.round(parseFloat(ll[0]) * dec) / dec) + ", " | ||
+ | + (Math.round(parseFloat(ll[1]) * dec) / dec) + ")"; | ||
+ | } | ||
+ | |||
+ | function coordinatesControl(map) { | ||
+ | var controlDiv = document.createElement('DIV'); | ||
+ | controlDiv.id = 'mapsCoor'; | ||
+ | map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(controlDiv); | ||
+ | return controlDiv; | ||
+ | } | ||
+ | |||
+ | function geocode(map) { | ||
+ | var button = document.createElement('button'); | ||
+ | button.id = 'geocodeButton'; | ||
+ | button.innerHTML = buttonText; | ||
+ | |||
+ | map.controls[google.maps.ControlPosition.TOP_RIGHT].push(button); | ||
+ | |||
+ | var address = document.getElementById("addressH"); | ||
+ | var city = document.getElementById("cityH"); | ||
+ | var country = document.getElementById("countryH"); | ||
+ | |||
+ | google.maps.event.addDomListener(button, 'click', function() { | ||
+ | var fullAddress = address.value + ' ' + city.value + ' ' + country.value; | ||
+ | var coor = document.getElementById("coordinatesH"); | ||
+ | codeAddress(fullAddress, coor); | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function lockMarkerControl(map) { | ||
+ | var controlDiv = document.createElement('DIV'); | ||
+ | controlDiv.id = 'mapsCheckbox'; | ||
+ | var cb = document.createElement('input'); | ||
+ | cb.type = 'checkbox'; | ||
+ | cb.checked = lock; | ||
+ | controlDiv.innerHTML = '<div id=\'text\'>' + lockText + '</div>'; | ||
+ | controlDiv.appendChild(cb); | ||
+ | |||
+ | map.controls[google.maps.ControlPosition.TOP_RIGHT].push(controlDiv); | ||
+ | |||
+ | google.maps.event.addDomListener(cb, 'click', function() { | ||
+ | lock = cb.checked; | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function drawMap() { | ||
+ | |||
+ | var coor = document.getElementById("coordinatesH"); | ||
+ | if (coor != null && coor.value !== "") { | ||
+ | var latLng = new Array(); | ||
+ | latLng = coor.value.substring(1, coor.value.length - 1).split(', '); | ||
+ | position = new google.maps.LatLng(latLng[0], latLng[1]); | ||
+ | markerH.setMap(mapH); | ||
+ | markerH.setPosition(position); | ||
+ | mapH.setCenter(position); | ||
+ | positionH.innerHTML = parseLatLng(position); | ||
+ | } else { | ||
+ | mapH.setCenter(solHQ); | ||
+ | markerH.setMap(null); | ||
+ | positionH.innerHTML = parseLatLng(""); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function htmlComponentChanged(id) { | ||
+ | |||
+ | if (id == "tab1886H") { | ||
+ | if (first) { | ||
+ | setTimeout(function() { | ||
+ | initialize(); | ||
+ | }, timeout); | ||
+ | first = false; | ||
+ | } else { | ||
+ | setTimeout(function() { | ||
+ | drawMap(); | ||
+ | }, timeout); | ||
+ | } | ||
+ | } | ||
+ | } | ||
</code> | </code> |