Get access to Google Maps API from a Chrome extension - javascript

Say I'd like to find addresses on any webpage and have a click on each one of them insert a small Google Maps below the address.
The problem I'm running into is that the GMaps library must be loaded via a < script> tag. But because anything loaded via < script> is out of the Chrome extension execution context, the "google.maps" object won't be available to my content scripts.
Any thoughts on a workaround?

What you could do is create an iframe that contains a page to your map viewer. Then you will belong in the context of Content Scripts and you have full access to Chrome's Message Passing.
Since I have created hundreds of extensions, I have done exactly this in tons of them, and some of them are available in my github.com/mohamedmansour page. I will show an example that I just did for this problem below, unfortunately it may contain bugs. Check my github page above for a more complete example.
What I would do
Create a map_viewer.html page in your extension.
Include within the <head> tag the <script src="http://maps.google.com/maps?file=api.....
Use Chrome Message Passing to pass data between content scripts.
For example
map_viewer.html
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="/css/map_viewer.css" />
<script type="text/javascript" src="/js/map_viewer.js"></script>
</head>
<body>
<div id="map_canvas"></div>
</body>
</html>
map_viewer.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == 'RenderMap') {
renderMap(request.data.latitude, request.data.longitude);
}
});
function renderMap(latitude, latitude) {
var map = new GMap2(document.getElementById('map_canvas'));
map.setCenter(new GLatLng(latitude, latitude), 13);
var marker = new GMarker(new GPoint(lng, lat));
map.addOverlay(marker);
}
webpage_content_script.js
...
// Unique ID to differentiate this content script from the rest of the web.
// or use the extension id from ##__extension_id__, I recall there was a bug, haven't
// checked if it got resolved though.
var UNIQUE_MAP_VIEWER_ID = 'crx_myextension_iframe';
var latitude = -1;
var longitude = -1;
/**
* Here is where you want to render a latitude and longitude. We create an iframe so we
* we can inject it. We just want to maintain a single instance of it though.
*/
function onRenderMap() {
var mapViewerDOM = document.getElementById(UNIQUE_MAP_VIEWER_ID);
if (mapViewerDOM) {
mapViewerDOM.parentNode.removeChild(mapViewerDOM);
}
mapViewerDOM = document.createElement('iframe');
mapViewerDOM.setAttribute('id', UNIQUE_MAP_VIEWER_ID);
mapViewerDOM.setAttribute('src', chrome.extension.getURL('map_viewer.html');
mapViewerDOM.setAttribute('frameBorder', '0');
mapViewerDOM.setAttribute('width', '99.90%');
mapViewerDOM.setAttribute('height', '100%');
mapViewerDOM.setAttribute('style', 'position: fixed; top: 0; left: 0; overflow: hidden; z-index: 99999');
mapViewerDOM.onload = function(e) {
sendResponse({
method: 'RenderMap',
data: {
latitude: latitude,
longitude: longitude
}
});
}
document.body.appendChild(mapViewerDOM);
}
...
I hope this would steer you in the right direction.
Thanks,
Mohamed Mansour

Related

How to display an Esri Map on MVC view from an HTML file?

I would like to know how to display an Esri Map on a ASP.Net MVC view from an HTML file.
I have an HTML file that contains a JavaScript code. This file can display the map and the widgets when I open it with any internet browser. It is working fine. But now I want to display that map on an ASP.Net MVC view. This is what I have done:
<h2 class="panel-title" data-toggle="collapse" href="#projectmap">Project Map</h2>
<div id="projectmap" class="panel-collapse collapse">
<div class="panel-body">
<div class="form-group">
#Html.Raw(File.ReadAllText(Server.MapPath("~/Views/Application/default.html")))
</div>
</div>
</div>
But it displays only the Sketch widget, it does not display the map. I have also tried to write the JavaScript code manually on the .cshtml file and use the div container to display the map, it still displays the Sketch widget only. I have also tried the partial views, it does not work.
Below is the HTML code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Load a basic WebMap - 4.14</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet"
href="https://js.arcgis.com/4.14/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.14/"></script>
<script>
var x;
var y;
require(["esri/views/MapView", "esri/WebMap", "esri/config", "esri/widgets/Sketch", "esri/layers/GraphicsLayer"], function (MapView, WebMap, esriConfig, Sketch, GraphicsLayer) {
/************************************************************
* Creates a new WebMap instance. A WebMap must reference
* a PortalItem ID that represents a WebMap saved to
* arcgis.com or an on-premise portal.
*
* To load a WebMap from an on-premise portal, set the portal
* url with esriConfig.portalUrl.
************************************************************/
esriConfig.portalUrl = "https://portal.environment.gov.za/portal";
var webmap = new WebMap({
portalItem: {
// autocasts as new PortalItem()
id: "04582be14885483da48f29398960f653"
}
});
var graphicsLayer = new GraphicsLayer();
/************************************************************
* Set the WebMap instance to the map property in a MapView.
************************************************************/
var view = new MapView({
map: webmap,
container: "viewDiv"
});
webmap.layers.add(graphicsLayer);
var sketch = new Sketch({
layer: graphicsLayer,
view: view,
container: "viewDiv"
});
view.ui.add(sketch, "top-left");
// Listen to sketch widget's create event.
sketch.on("create", function (event) {
// check if the create event's state has changed to complete indicating
// the graphic create operation is completed.
console.log(view.zoom);
//if (event.state === "complete") {
// // remove the graphic from the layer. Sketch adds
// // the completed graphic to the layer by default.
// graphicsLayer.remove(event.graphic);
// // use the graphic.geometry to query features that intersect it
// selectFeatures(event.graphic.geometry);
//}
console.info(event.graphic.geometry);
x = event.graphic.geometry.x;
y = event.graphic.geometry.y;
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>

javascript embedded in html not running in wkwebview

I am implementing wkwebview in an app that was using UIWebView. I am unable to get javascript to execute when pointing to a local html file that has the javascript embedded within it. The javascript is stripped to do a simple alert and load a basic google map. None of that is getting executed. Do I need to run a local server? GCDWebserver..
I should add, the html/javascript works in safari, google browser no problem.
Solutions attempted include:
1. AppTransportSecuritySettings AllowArbitrary loads.
2. ViewController.swift webview.configuration.preferences.javaScriptEnabled = true
3. This question addresses the issue and says is was fixed in iOS 10.3 Load Javascript files through HTML file on WKWebView in iOS The simulator is running 12.1
4. This question also addresses the issue with an answer of requiring GCDWebserver to be able to execute javascript using wkwebview. WKWebView not executing any js code This however was also solved in a laster version of iOS. Here is some code:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
//#IBOutlet var googleMap: WKWebView!
var webview: WKWebView!
override func loadView() {
webview = WKWebView()
webview.navigationDelegate = self
view = webview
}
override func viewDidLoad() {
super.viewDidLoad()
//let url = URL(string: "https://schallerf1.com")!
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "www")!
webview.load(URLRequest(url: url))
webview.allowsBackForwardNavigationGestures = true
let request = URLRequest(url: url)
webview.configuration.preferences.javaScriptEnabled = true
webview.load(request)
}
}
<!DOCTYPE html>
<html>
<head>
<title>Simple Map</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<b>WHYYYYYYYYYY!!!!</b>
<div style="height:100%;width:100%;" id="map"></div>
<script type="text/javascript">
var name = "TESTTESTTEST";
alert('code: ' + name + '\n');
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 39.976068, lng: -83.003297},
zoom: 8
});
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxxxxxxxxxx&callback=initMap"></script>
</body>
</html>
None of the javascript works, I should get an alert and a simple google map should display. Do I need to look into the local web server GCDWebserver?
You should be calling your javascript in this WKNavigationDelegate method, which is called when the webview finishes loading.
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("initMap()", completionHandler: { (value, err) in
// Handle response here.
})
}
Also, I'm not sure why you're calling two different webview.load() requests - maybe don't do that?

Trigger an event on single markers in leaflet LayerGroup

Hi everyone (or should I say "anyone [who would read this...]?) :)
I have tried to find the answer for two days but without success (most likely because I am a brand new user). I have on a leaflet map markers organized in a Layergroup so I can manage them with Layercontrol. I would like that when a marker is clicked it triggers an event (in my case the creation of a circle representing a specific distance around this marker). I would like to manage that outside of the individual markers because I want also to set some different options for the radius of the circle according to the distance selected by the user.
Below are different pieces of code showing what I mean :
<!DOCTYPE html>
<head>
<title>Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://unpkg.com/leaflet#1.0.2/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.2/dist/leaflet.css"/>
<style>
body {
font-family: Helvetica;
margin:0;
}
#map{
position:absolute;
top:0;
bottom:0;
width:100%;}
</style>
</head>
<body>
<div id="map"></div>
<script>
var sites = [
{'loc':[42.2793869135936,9.53257201027757],'title':'TORRA A I CASELLI'},
{'loc':[42.2713622720985,9.50678498185463],'title':'TOUR GIANDINELLI'},
{'loc':[42.641518105666,9.00587322013212],'title':'TOUR DE LOSARI'},];
var map = new L.Map('map');
map.addLayer(new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'));
map.setView([42.5,9.2210706018535],9);
var marks = new L.LayerGroup();
for(i in sites) {
var title = sites[i].title,
loc = sites[i].loc,
markersites = new L.Marker(new L.latLng(loc), {title: title});
markersites.bindPopup('<strong>'+title+'</strong><br>');
marks.addLayer(markersites);
}
marks.addTo(map);
// for now it is all good, the next part is where I fail
marks.on('click',function(){
console.log("Yeah!");
new L.circle(this.getLatLng(), 10000, {color:'red', fillOpacity:0.3}).addTo(map)});
</script>
</body>
</html>
Any help would be much appreciated, thank you for your attention.
Cheers,
Guillaume.
The fact you are using a LayerGroup is not part of your problem.
First, you have to attach a 'click' listener on all your markers. This way, you can draw your circle when the popup is opened. You also must keep a reference to this circle in the javascript popup object.
// keep a reference on the current active circle
var activeCircle = false;
markersites.on('click', function(e) {
// marker clicked is e.target
// remove active circle if any
if(activeCircle) {
map.removeLayer(activeCircle);
}
// draw a 10km circle with the same center as the marker
activeCircle = L.circle(e.target.getLatLng(), { radius: 10000 , color: "#ff0000" }).addTo(map);
});
Example is here: http://plnkr.co/edit/OufPbq07ywEZh1N5VA8Y?p=preview
Since you are binding popups to your markers, a leaflet popup is going to open up in response to click events. However, you can hook into those events by adding your own callbacks, too like:
map.on('popupopen', function (e) {
currentPopup = e.popup; // keep track of current popup
// Do stuff with the popup or inspect it's marker here...
});
There are probably other ways to solve this with the leaflet api which is very flexible. This approach has worked for me in the past.
Also at the time you are creating popups, you can bind additional info if necessary like this:
var content = '<b>'+ something + '</b>: '+ etc;
var popup = L.popup();
popup.setContent(content);
popup.markerId = 'some id';
layer.bindPopup(popup);

Show users coordinates and trigger event

I am using the Geolocation Marker Script from the Google Maps Utilities Library V3 in order to display the position of a user.
What I want to achieve (I am a newbie to the Google Maps API!) is:
have the users current coordinates displayed (e.g. in a simple CSS container somewhere on the page)
connect an event to a marker. I should be triggered when the user is close.
Appreciate your help!
To display coordinates to the user, you would need a reference to a DOM Element. Then it's a simple matter of updating the content.
HTML On the Page
<div id="UserCoordinates">Waiting on GPS Position ...</div>
In Script
google.maps.event.addListener(GeoMarker, 'position_changed', function() {
var UserPosition = this.getPosition();
var DisplayElement = document.getElementById('UserCoordinates');
if(UserPosition === null) {
DisplayElement.innerHTML = 'Waiting on GPS Position...';
} else {
DisplayElement.innerHTML =
'Current Position: ' + UserPosition.toUrlValue();
}
});
This will show the user their current position as it changes. If you are going to continue using a full screen map, you'll probably want to implement the UserCoordinates div as a map control. The API Reference has a good overview and multiple examples on this.
Display an info window when the user is within X meters of a location
This is a little tricky because there are multiple scenarios to handle and you don't want the infowindow opening repeatedly as they move within your radius.
Distance calculation
I see you have a distance function in your code, but I recommend using the one in the Spherical Geometry library of the API. You just have to specifically load the library with your api script tag:
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?libraries=geometry&sensor=true_or_false">
</script>
Then you need to add to the position_changed event handler:
var IsWithinRadius = false; //assume they start outside of the circle
var RadiusInMeters = 1000; //within 1 km
var LocationOfInterest = map.getCenter();
google.maps.event.addListener(GeoMarker, 'position_changed', function() {
var UserPosition = this.getPosition();
var DisplayElement = document.getElementById('UserCoordinates');
if(UserPosition === null) {
DisplayElement.innerHTML = 'Waiting on GPS Position...';
IsWithinRadius = false; //you don't know where they are
} else {
DisplayElement.innerHTML =
'Current Position: ' + UserPosition.toUrlValue();
var IsCurrentPositionInRadius =
Math.abs(google.maps.geometry.spherical.computeDistanceBetween(
UserPosition, LocationOfInterest)) <= RadiusInMeters;
var JustEnteredRadius = !IsWithinRadius && IsCurrentPositionInRadius;
IsWithinRadius = IsCurrentPositionInRadius;
if(JustEnteredRadius) {
//trigger action here.
alert("Within raidus");
}
}
});

Google Maps API v2: MarkerManager clearMarkers(); doesn't work

I have GPS tracking server and problem with clearing markers added with MarkerManager from database. My truncated code:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<script src='http://maps.google.com/maps?file=api&v=2&hl=pl&key=my_key' type='text/javascript'></script>
</head>
<body onload='mapStart()' onunload='GUnload()'>
<script type='text/javascript'>
var map;
var mgr=false;
var timeOut=null;
function mapStart()
{
if(GBrowserIsCompatible())
{
map = new GMap2(document.getElementById("map"),{mapTypes: [G_NORMAL_MAP,G_HYBRID_MAP,G_SATELLITE_MAP,G_PHYSICAL_MAP]});
center = new GLatLng(52.536395, 13.42534);
map.setCenter(center, 6);
mgr = new GMarkerManager(map,{maxZoom: 19});
refreshMarkers();
}
}
function refreshMarkers()
{
clearTimeout(timeOut);
GDownloadUrl('dane2.php', function(dane,respCode)
{
if(respCode==200)
{
var xml = GXml.parse(dane);
var marker = dodajMarker(arguments, 15, 15);
}
else
{
alert('Cant open dane2.php');
}
});
mgr.clearMarkers(); // ???
timeOut=setInterval("refreshMarkers()",2000);
}
function dodajMarker(arguments, minZoom, maxZoom)
{
var ikona = new GIcon();
ikona.image = 'http://www.google.com/intl/en_ALL/mapfiles/dd-start.png';
ikona.iconSize = new GSize(20, 34);
ikona.iconAnchor = new GPoint(10, 34);
var marker = new GMarker(new GLatLng(latitude,longitude),{icon: ikona});
mgr.addMarker(marker,minZoom,maxZoom);
return marker;
}
</script>
<div id="map" style="align: center; width: 1000px; height: 490px; solid black; background: gray;"></div>
</body>
</html>
My page: http://m2mgsm.com/gps/index.php You can login: "admin", password: "12345" Click Test Map ("Mapatesty" - polish language only, english soon) in menu and then Select IMEI ("Wybierz IMEI") e.g. 355832010123229 and check Route ("Pokaż trasę:") and From/To ("Od/Do") date (e.g. 05.01.2012/05.01.2012) and "Filtruj". You can now view source of my map script in frame. I want to refresh ONLY markers with e.g. 3 sec. interval and it works, but new markers are OVERLAY on old markers...
Ps. Sorry for my English.
You have errors is your JS:
ReferenceError: kontener is not defined [http://m2mgsm.com/gps/mapatesty.php:259]
TypeError: Object #<yv> has no method 'clearMarkers' [http://m2mgsm.com/gps/mapatesty.php:459]
Try to use Firefox with Firebug extension or Chrome with its built-in Debugger to trace through your JavaScript code and eliminate the bugs.

Categories