I load an AT5 file into a google map object using the following code:
map = new GMap2(document.getElementById("map_canvas"));
geoXml = new GGeoXml(at5);
GEvent.addListener(geoXml, "load", function() {
geoXml.gotoDefaultViewport(map);
// I would like to read the AT5 contents here
});
map.addOverlay(geoXml);
Thanks for any kind of help.
This does not refer to Version 3 of the Maps API. GMap2, GGeoXml and GEvent are all part of Version 2.
GGeoXml is opaque: you cannot inspect it. Use a third-party parser if you want the content to be available.
There's a rather old list available at http://econym.org.uk/gmap/extensions.htm -- have a look at EGeoXml and GeoXml.
Related
I am using Google Apps Script to create a page, on which I would like to embed maps. The maps themselves would be static, but the map could be different depending on other parameters (it’s a genealogy page, and I’d like to display a map of birth and death locations, and maybe some other map points, based on a selected individual).
Using Google’s Maps service, I know that I can create a map, with a couple points built in.
Function getMapImage() {
var map = Maps.newStaticMap()
.setSize(600,400)
.addMarker('Chicago, Illinois') // markers would be based on a passed parm; this is just test data
.addMarker('Pocatello, Idaho');
// *** This is where I am looking for some guidance
return(); // obviously, I'm not returning a blank for real
}
Within the map class, there are a number of things I can do with it at this point.
I could create a URL, and pass that back. That appears to require an API account, which at this point, I do not have (and ideally, would like to avoid, but maybe I’ll have to do that). It also appears that I will run into CORB issues with that, which I think is beyond my knowledge (so if that’s the solution, I’ll be back for more guidance).
I could create a blob as an image, and pass that back to my page. I have tried this using a few different examples I have found while researching this.
Server Side
function getMapImage() {
var map = Maps.newStaticMap()
.setSize(600,400)
.addMarker('Chicago, Illinois')
.addMarker('Pocatello, Idaho');
var mapImage = map.getAs("image/png");
// OR
// var mapImage = map.getBlob();
return(mapImage);
}
Page side
<div id=”mapDiv”></div>
<script>
$(function() {
google.script.run.withSuccessHandler(displayMap).getMapImage();
}
function displayMap(mapImage) {
var binaryData = [];
binaryData.push(mapImage);
var mapURL = window.URL.createObjectURL(new Blob(binaryData, {type: "image/png"}))
var mapIMG = "<img src=\'" + mapURL + "\'>"
$('#mapDiv').html(mapIMG);
}
</script>
The page calls getMapImage() on the server, and the return data is sent as a parm to displayMap().
var mapIMG ends up resolving to <img src='blob:https://n-a4slffdg23u3pai7jxk7xfeg4t7dfweecjbruoa-0lu-script.googleusercontent.com/51b3d383-0eef-41c1-9a50-3397cbe83e0d'> This version doesn't create any errors in the console, which other options I tried did. But on the page, I'm just getting the standard 16x16 image not found icon.
I’ve tried a few other things based on what I’ve come across in researching this, but don’t want to litter this post with all sorts of different code snippets. I’ve tried a lot of things, but clearly not the right thing yet.
What’s the best / correct (dare I ask, simplest) way to build a map with Google’s Map class, and then serve it to a web page?
EDIT: I added a little more detail on how the server and page interact, in response to Tanaike's question.
Modification points:
I think that in your script, Blob is returned from Google Apps Script to Javascript using google.script.run. Unfortunately, in the current stage, Blob data cannot be directly sent from from Google Apps Script to Javascript. I think that this might be the reason of your issue.
In this case, I would like to propose to directly create the data URL at the Google Apps Script side. When your script is modified, it becomes as follows.
Modified script:
Google Apps Script side:
function getMapImage() {
var map = Maps.newStaticMap()
.setSize(600, 400)
.addMarker('Chicago, Illinois')
.addMarker('Pocatello, Idaho');
var blob = map.getAs("image/png"); // or map.getBlob()
var dataUrl = `data:image/png;base64,${Utilities.base64Encode(blob.getBytes())}`;
return dataUrl;
}
Javascript side:
$(function() {
google.script.run.withSuccessHandler(displayMap).getMapImage();
});
function displayMap(mapURL) {
var mapIMG = "<img src=\'" + mapURL + "\'>"
$('#mapDiv').html(mapIMG);
}
In your Javascript side, $(function() {google.script.run.withSuccessHandler(displayMap).getMapImage();} is not enclosed by ). Please be careful this.
Note:
In my environment, when I saw <div id=”mapDiv”></div>, this double quote ” couldn't be used. So if in your environment, an error occurs by <div id=”mapDiv”></div>, please modify ” to " like <div id="mapDiv"></div>.
Reference:
base64Encode(data)
I'm using the GeoServer to hold some maps, and just a simple OpenLayers app to load and show the data (for now).
I'm successfully loading the demo data (which is in WGS84), but when it comes to my data (which is in Balkans Zone 7, EPSG:31277), when I look at the request, it seems like the BBOX is completely out of order.
I checked the BBOX from the GeoServer preview page (which is made with openLayers) and it looks like this, and works:
http://127.0.0.1:2113/geoserver/GISHome/wms?LAYERS=GISHome%3ANis11Katastar&STYLES=&FORMAT=image%2Fjpeg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG%3A31277&BBOX=7572000,4796000,7574000,4798000&WIDTH=512&HEIGHT=512
The only thing that's different with my request is the BBOX. When copying the BBOX into my request, it works.
http://127.0.0.1:2113/geoserver/wms?LAYERS=Nis11Katastar&FORMAT=image%2Fpng&WIDTH=256&HEIGHT=256&PROJECTION=EPSG%3A31277&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=-180,-90,0,90
In the GeoServer source, the bounds are somehow calculated and hardcoded. The question is, how? Is there a way not to hardcode them? How should I calculate them. I've tried adding bounds, maxExtent, resolution, I'm obviously missing something more than that here. The GeoServer works fine, I'm using the layers from QuantumGIS.
Ext.onReady(function() {
var map = new OpenLayers.Map();
var layer = new OpenLayers.Layer.WMS(
"Global Imagery",
"http://127.0.0.1:2113/geoserver/wms",
{
LAYERS:'Nis11Katastar',
format: 'image/png',
width:600,
height:400,
projection: new OpenLayers.Projection("EPSG:31277"),
}
);
Thankyou.
Oh, yes, I'm using GeoExt, but that doesn't change much.
It seems solved. I was setting the right properties, but to the WMS layer object, not to the map object. As for the bounds question, I'm just copying the bounds from the geoserver control panel.
I have got a Google Maps and Places API via javascript code.
When the page loads it will automatically go to the Latitude defined in the code. Is there a way to have it AUTOMATICALLY go to where it thinks your location is, without having a button or search field?
Using this code currently:
google.maps.LatLng(37.783259, -122.402708);
Have you tried setting sensor=true when calling the API?
Source
if(google.loader.ClientLocation)
{
visitor_lat = google.loader.ClientLocation.latitude;
visitor_lon = google.loader.ClientLocation.longitude;
};
new google.maps.LatLng(visitor_lat, visitor_lon);
The Google Maps JavaScript version 3 API library documentation clearly explains:
The Google Maps API supports the KML
and GeoRSS data formats for displaying
geographic information. These data
formats are displayed on a map using a
KmlLayer object, whose constructor
takes the URL of a publicly accessible
KML or GeoRSS file.
There are even several Stack Overflow questions about how to load local data:
Loading a local .kml file using google maps?
Google Maps kml files
Some of the answers have pointed to third party libraries which can parse KML locally without the file needing to be public:
geoxml3
GeoXML
EGeoXml
And while these solutions are good if you have a need to keep your data private, I simply want to make development easier. When running locally I obviously cannot parse my KML and therefore lose functionality that I am trying to test. I've posted a single generic KML file on a publicly available site, but then have to have different development code to render one thing vs. something else when running for real.
What are my options for local development to render what would be publicly available dynamically generated KML files?
It seems like you've outlined the options pretty well:
If you want to work with local data, without involving a publicly accessible webserver, you'll need to use a javascript-based approach to parse the KML and load it onto the map. While this won't perfectly replicate the Google functionality, it is likely good enough for initial development if you only care about displaying the KML features. In this case, I'd probably set up a stub class, like this:
// I'll assume you have a global namespace called MyProject
MyProject.LOCAL_KML = true;
MyProject.KmlLayer = function(url) {
// parse the KML, maybe caching an array of markers or polygons,
// using one of the libraries you list in your question
};
// now stub out the methods you care about, based on
// http://code.google.com/apis/maps/documentation/javascript/reference.html#KmlLayer
MyProject.KmlLayer.prototype.setMap = function(map) {
// add the markers and polygons to the map, or remove them if !map
}
// etc
Now either put a switch in the code, or comment/uncomment, or use a build script to switch, or whatever your current process is for switching between dev and production code:
var kmlPath = "/my.kml";
var kmlLayer = MyProject.LOCAL_KML ?
new MyProject.KmlLayer(MyProject.LOCAL_KML_HOST + kmlPath) :
new google.maps.KmlLayer(MyProject.PRODUCTION_KML_HOST + kmlPath);
kmlLayer.setMap(myMap);
If, on the other hand, you need all of the functionality in the Google KmlLayer, or you want to make sure things work with the production setup, or you don't want to bother stubbing out the functionality Google provides, then you'll need to upload it to a publicly available server, so that Google can do its server-side processing.
Aside from the obvious options (FTP, a command-line script to upload your new KML file, etc), most of which require you to do something manually before you load your map page, you might consider building the update into the page you're loading. Depending on the platform you're using, this might be easier to do on the back-end or the front-end; the key would be to have a script on your public server that would allow the KML to be updated:
Get KML string from request.POST
Validate the KML string (just so you aren't opening your server to attacks)
Write to a single file, e.g. "my.kml"
Then, when you view your map page, update the remote KML based on the data from localhost. Here's a client-side version, using jQuery:
// again, you'd probably have a way to kill this block in production
if (MyProject.UPDATE_KML_FROM_LOCALHOST) {
// get localhost KML
$.get(MyProject.LOCAL_KML_HOST + kmlPath, function(data) {
// now post it to the remote server
$.post(
MyProject.DEV_KML_HOST + '/update_kml.php',
{ kml: data },
function() {
// after the post completes, get the KML layer from Google
var kmlLayer new google.maps.KmlLayer(MyProject.DEV_KML_HOST + kmlPath);
kmlLayer.setMap(myMap);
}
);
})
}
Admittedly, there are a lot of round-trips here (page -> localhost, page -> remote server, Google -> remote server, Google -> page), so this is going to be slow. But it would allow you to have Google's code properly render dynamic KML data produced on localhost, without having to take a separate manual step every time you reload the page.
Definitely, Google Maps KmlLayer is designed for you to send your data to them.
https://developers.google.com/maps/documentation/javascript/kml
Have a look the following log.
//console
var src = 'https://developers.google.com/maps/documentation/javascript/examples/kml/westcampus.kml';
var kmlLayer = new google.maps.KmlLayer(src, {
suppressInfoWindows: true,
preserveViewport: false,
map: your_gmap_object
});
Creating Marker, Polygon, they are all browser side parsing and rendering.
As you can see from next network log, KmlLayer class send source URL to Google Server to parse it and (do something in their end) and send the parsed result back to your browser to render.
//REQUEST from browser
https://maps.googleapis.com/maps/api/js/KmlOverlayService.GetOverlays?1shttps%3A%2F%2Fdevelopers.google.com%2Fmaps%2Fdocumentation%2Fjavascript%2Fexamples%2Fkml%2Fwestcampus.kml&callback=_xdc_._lidt3k&key=AIzaSyBeLTP20qMgxsQFz1mwLlzNuhrS5xD_a_U&token=103685
//RESPONSE from google server
/**/_xdc_._lidt3k && _xdc_._lidt3k( [0,"kml:cXOw0bjKUSmlnTN2l67v0Sai6WfXhSSWuyNaDD0mAzh6xfi2fYnBo78Y2Eg","|ks:;dc:tg;ts:51385071|kv:3|api:3",...
["KmlFile"],[[37.423017,-122.0927],[37.424194,-122.091498]],[["g74cf1503d602f2e5"],["g58e8cf8fd6da8d29"],["ge39d22e72437b02e"]],1,[["client","2"]],-21505,[["ks",";dc:tg;ts:51385071"],["kv","3"],["api","3"]]] )
As #capdragon mentioned above, it would be better parse KML by yourself.
UPDATE
Here is compact KML parser code.
This only for google.maps Marker and Polygon.
html
<input type='file' accept=".kml,.kmz" onchange="fileChanged()">
script, I used typescript but it is pretty same with javascript
file: any
fileChanged(e) {
this.file = e.target.files[0]
this.parseDocument(this.file)
}
parseDocument(file) {
let fileReader = new FileReader()
fileReader.onload = async (e: any) => {
let result = await this.extractGoogleCoords(e.target.result)
//CREATE MARKER OR POLYGON WITH result here
console.log(result)
}
fileReader.readAsText(file)
}
async extractGoogleCoords(plainText) {
let parser = new DOMParser()
let xmlDoc = parser.parseFromString(plainText, "text/xml")
let googlePolygons = []
let googleMarkers = []
if (xmlDoc.documentElement.nodeName == "kml") {
for (const item of xmlDoc.getElementsByTagName('Placemark') as any) {
let placeMarkName = item.getElementsByTagName('name')[0].childNodes[0].nodeValue.trim()
let polygons = item.getElementsByTagName('Polygon')
let markers = item.getElementsByTagName('Point')
/** POLYGONS PARSE **/
for (const polygon of polygons) {
let coords = polygon.getElementsByTagName('coordinates')[0].childNodes[0].nodeValue.trim()
let points = coords.split(" ")
let googlePolygonsPaths = []
for (const point of points) {
let coord = point.split(",")
googlePolygonsPaths.push({ lat: +coord[1], lng: +coord[0] })
}
googlePolygons.push(googlePolygonsPaths)
}
/** MARKER PARSE **/
for (const marker of markers) {
var coords = marker.getElementsByTagName('coordinates')[0].childNodes[0].nodeValue.trim()
let coord = coords.split(",")
googleMarkers.push({ lat: +coord[1], lng: +coord[0] })
}
}
} else {
throw "error while parsing"
}
return { markers: googleMarkers, polygons: googlePolygons }
}
output
markers: Array(3)
0: {lat: 37.42390182131783, lng: -122.0914977709329}
...
polygons: Array(1)
0: Array(88)
0: {lat: -37.79825999283025, lng: 144.9165994157198}
...
I have added a kml to google earth by use of button with javascript. How can I delete that kml or clear all kml's by use of another button? thanks
To remove all the features you can use the following method. It presumes that 'ge' references your plug-in object.
function RemoveAllFeatures()
{
var features = ge.getFeatures();
while (features.getLastChild() != null)
{
features.removeChild(features.getLastChild());
}
}
Do you mean you added a KML file? I guess you did this by adding a "network link" using functions like
var networkLink = ge.createNetworkLink('ID_MyNetworkLink');
var link = ge.createLink('MyHREF');
link.setHref('http://bla.bla.bla.kml');
networkLink.setLink(link);
ge.getFeatures().appendChild(networkLink);
So your "file" is a child of the whole KML tree with id "ID_MyNetworkLink". You can remove it by
ge.getFeatures().removeChild(ge.getElementById('ID_MyNetworkLink'));
Hope that helps
Though not quite what you are likely looking for you can have a NetworkLink that loads kml with a NetworkLinkController change things. Check out the docs.