现在,在google.com/maps页面上点击某个国家/地区或城市的名称/标签后,此区域会突出显示并打开左侧弹出窗口并显示相关信息。
是否可以通过Google Maps API实现相同的功能?
要在鼠标悬停时突出显示标签,请在点击时突出显示国家/地区,州或城市的边界,并通过用户标签/名称在js中进行选择。
我查看了Google Maps API JavaScript文档,但未找到类似的功能。
谢谢!
<小时/> 对不完整的问题感到抱歉。
有没有办法在地图上使用API设置可点击的国家/城市名称,例如google.com/maps页面?
答案 0 :(得分:0)
推荐&#39;准系统&#39; GMap课程。碰巧我只是自己重新访问这个,所以尝试这个代码(它可能需要一些更改)
/**
* 'GMap'
* extends googlemaps to allow simpler coding in other apps, should be loaded after the main google maps
* a new instance of a google map is contructed calling this with identical arguments to the standard class and then returned
* the map name must be unique and reserved as a global by the client code, eg var pagemap outside of function, so the external callbacks
* can use this name to run a function against, eg pagemap.queryMove_do
*
*/
function GMap(mapId, options) {
this.name = null;
this.map = null;
this.bounds = null; //view point boundary
this.gui_reg;
this.mapoptions;
this.calledbackonload;
this.polygons = {};
this.lngoffset = {'direction': '', referenceelem: ''};
this.infowindows = {};
this.infowindowindex = 0;
GMap.prototype.__construct = function (mapId, options) {
var mapbuffer = new Data_buffer(); //this has to be vague as 3rd party api data is not always the same format, simply return data to the logged map interface objects
if (typeof window.MAP_INTERFACE_CTRL === 'undefined') {//if not created, global constant to co-ordinate ajax requests with map objects and 3rd party API, and allow funneling back to local objects
window.MAP_INTERFACE_CTRL = new Data_buffer(); //this has to be vague as 3rd party api data is not always the same format, simply return data to the logged map interface objects
}
options.panControlOptions = model.array_defaults(
options.ctrlcfg,
{
position: google.maps.ControlPosition.RIGHT_BOTTOM
}
);
options.zoomControlOptions = model.array_defaults(
options.ctrlcfg,
{
position: google.maps.ControlPosition.RIGHT_CENTER
}
);
this.mapoptions = model.array_defaults(
options,
{
center: new google.maps.LatLng(54.5, -7),
zoom: 6,
minzoom: 15,
maxzoom: 21
}
);
this.gui_reg = {location: {address: new Array()}};
this.name = mapId;
this.map = new google.maps.Map(document.getElementById(mapId), this.mapoptions);
var selfobj = this;
google.maps.event.addListener(//processes client registration for registered events
this.map,
'dragend', //center changed causes too many requests
function () {
selfobj.update_client_go("location");
}
);
//create invisible scratchpad
if ($('#map_pool').length == 0) {
$('body').append("<div id='map_pool'></div>");
$('#map_pool').hide();
}
/*
callback_obj = this;
google.maps.event.addListener(this.map, 'tilesloaded', function(evt) {
callback_obj.map.setZoom(9);
});*/
}
/*****************************************************************/
/* METHODS FOR BOTH GMAP AND CLIENT */
/*****************************************************************/
/*
* defines offset of mapcentre away from an element such as custom controls
* which overlay a large amount of map
* positive or negative according to offset direction where the element might be on the
* left or the right
*/
GMap.prototype.set_lng_offset = function (elemid, dir) {
var direction = 'right';
if (dir == 'left') {
direction = dir;
}
this.lngoffset = {direction: direction, referenceelem: elemid};
}
/*
* returns lng offset according to definitions and current map situation
*/
GMap.prototype.calc_lng_offset = function () {
//get lng width of map
var eastlng = this.map.getBounds().getNorthEast().lng();
var westlng = this.map.getBounds().getSouthWest().lng();
var lngspan;
if (westlng <= 0 && eastlng >= 0) {
lngspan = Math.abs(westlng) + eastlng;
} else if (westlng >= 0 && eastlng <= 0) {
lngspan = (180 - westlng) + (180 - Math.abs(eastlng));
} else if (westlng < 0 && eastlng < 0) {
lngspan = Math.abs(eastlng) + westlng;
} else {
lngspan = eastlng - westlng;
}
//get px width of map and referenced element
var mapwidth = $("#" + this.name).width();
var offsetpx = $("#" + this.lngoffset.referenceelem).width();
//convert this px to lng offset for map by multiplying by lngpx
var lngperpx = lngspan / mapwidth; //calc lng/px as lngpx
//calc the offset lng
var offsetlng = offsetpx * lngperpx;
if (this.lngoffset.direction == 'left') {
var offsetlng = 0 - offsetlng;
}
return offsetlng;
}
/*
* returns an object with north south east west and centre coordinates of the current view
* args.precision states number of figures after decimal returned - if not give a full result is returned.
* @returns {float north,float east,float south,float west,float lat,float lng}
*/
GMap.prototype.get_viewport_coords = function (args) {
if (args == undefined) {
args = {};
}
var coords = {};
coords.north = this.map.getBounds().getNorthEast().lat();
coords.east = this.map.getBounds().getNorthEast().lng();
coords.south = this.map.getBounds().getSouthWest().lat();
coords.west = this.map.getBounds().getSouthWest().lng();
coords.lat = this.map.getCenter().lat();
coords.lng = this.map.getCenter().lng();
if (args.precision != undefined) {
coords.north = coords.north.toFixed(args.precision);
coords.east = coords.east.toFixed(args.precision);
coords.south = coords.south.toFixed(args.precision);
coords.west = coords.west.toFixed(args.precision);
}
return coords;
}
/*
* 'map_query'
* takes a text query and queries to google for info
*
* required due to APIs not being the same, a uuid is registered globally and a global process used to redirect
* because of this the global process must identify a map object by UUID and only expect a single argument as an object literal (JSON)
* this is then passed back to the object that initiated the ajax or UUID request
*
* @param OBJECT query_cfg to send to gmap geocode
* @param string query_cfg.type states what type of query and what action to take, allowing multiple use for this method
* @param string query_cfg.query_data a string containing address data such as '<postcode>, <street>, <property>'
* @param string query_cfg.callback to return the data to
*/
GMap.prototype.geo_query = function (query_cfg) {
//package pre query data and set defaults if not set
query_cfg = model.array_defaults(
query_cfg,
{
clientid: this.name,
callback: 'update_client_do'//client code can set its own callback,else set callback to this
}
);
var geo_queryid = MAP_INTERFACE_CTRL.callback_push(query_cfg);
switch (query_cfg.type) {
case "location"://returns location data at given lat lng coords
var gcoder = new google.maps.Geocoder();
gcoder.geocode({'location': {lat: query_cfg.lat, lng: query_cfg.lng}}, function (results, status) {
var server_response = {results: results, status: status};
eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
});
break;
case "address_latlng":
var gcoder = new google.maps.Geocoder();
gcoder.geocode({'address': query_cfg.query_data}, function (results, status) {
var server_response = {results: results, status: status};
eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
});
break;
case "sv_pano_latlng"://gets streetview pano for given lat lng
var sv_service = new google.maps.StreetViewService();
sv_service.getPanoramaByLocation(query_cfg.latlng, query_cfg.radius, function (results, status) {
var server_response = {results: results, status: status};
eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
});
break;
case "viewport_range"://returns lat lng for viewport and centre to callback
var gcoder = new google.maps.Geocoder();
var bounds = this.bounds;
var center = this.map.getCenter();
gcoder.geocode({address: change_request}, function (results, status) {
callback_obj.viewport_range_do(results, status)
});
break;
}
}
/*
* 'formatted_geo_result'
* interprets geocoded results to a standard uniform independent of map api
* @param OBJECT geo_result data returned (in this map interface from google)
*/
GMap.prototype.formatted_geo_result = function (geodata) {
var formatted = {};
var itemdefaults = {
address: '',
lat: null,
lng: null
}
for (var i in geodata.results) {
var item = {
address: geodata.results[i].formatted_address,
lat: geodata.results[i].geometry.location.lat(),
lng: geodata.results[i].geometry.location.lng(),
}
formatted[i] = model.array_defaults(item, itemdefaults);
}
return formatted;
}
/*
* 'map_change'
* takes a request and processes and changes map accordingly
*
* callerobj is optional, if not given then callback will be back to this map object
* if a callerobj is given this must be to an object which can accept it
* (if callerobj is a top level function it needs to pass the object as keyword window - not 'window')
* The callback method is named as query type with _do appended eg address_move_do
* Some query types require the caller object to handle the query and DO NOT have a method defined in the map object
* If this map object DOES have the callback but a callback_obj is given then it will override this map object
*
* @param string change_request free text to send to gmap object
* @param string change_type states what type of query and what action to take, allowing multiple use for this method
* @param string callback function to run on change completion.
*/
GMap.prototype.map_change = function (change_type, change_data, callback) {
switch (change_type) {
case "lat_lng":
this.map.panTo({lat: change_data.latlng.lat, lng: change_data.latlng.lng + this.calc_lng_offset()});
break;
case "address"://to address_latlng as translation for this map object
this.geo_query({type: "address_latlng", clientid: this.name, callback: "map_change_do", change_type: change_type, query_data: change_data});
break;
}
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* 'map_change_do'
* processes any ajax return required by map_change
*
* @param string change_request free text to send to gmap object
* @param string change_type states what type of query and what action to take, allowing multiple use for this method
*/
GMap.prototype.map_change_do = function (pre_data, callback_data) {
switch (pre_data.change_type) {
case "address":
this.map_change("lat_lng", {latlng: {lat: callback_data.results[0].geometry.location.lat(), lng: callback_data.results[0].geometry.location.lng()}}); //callback_data.results[0].geometry.location.lat()
break;
}
}
/*
* permanently deletes all objects such as map markers (pins) etc
*/
GMap.prototype.clearmapobjects = function () {
this.map.clearMarkers();
}
GMap.prototype.panotestA = function () {
var locquery = "framlingham tech centre";
this.map_change("address", locquery);
this.map.setZoom(20);
this.geo_query("location", locquery)
this.geo_query({type: "address_latlng", clientid: this.name, callback: "panotestB", query_data: locquery});
}
GMap.prototype.panotestB = function (pre_data, callback_data) {
var pin = this.add_pin(callback_data.results[0].geometry.location.lat(), callback_data.results[0].geometry.location.lng(), {infopop: {content: 'hello', streetview: true}});
}
/*
* 'add_pin'
* adds a pin to a map
*
* @param float lat is latitude position
* @param float lng is longitude position
* @param object info_args other pin constructs such as the info displayed while clicking it
* @return object marker
*/
GMap.prototype.add_pin = function (lat, lng, info_args) {
info_args = model.array_defaults(
info_args,
{
width: 16,
height: 16,
animation: google.maps.Animation.DROP,
icon: {
url: "http://" + ROOTUC + "//css/images/icons/ROYOdot_a.gif"
},
infopop: null
}
);
/*
var icon = {
url: info_args.icon.url,
size: new google.maps.Size(20, 20),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(0, 0)
};
*/
var iconImage = new google.maps.MarkerImage(
info_args.icon.url, // url to image inc http://
null, // desired size
null, // offset within the scaled sprite
null, // anchor point is half of the desired size
new google.maps.Size(info_args.width, info_args.height) // required size
);
var pin = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: this.map,
title: info_args.label,
icon: iconImage,
optimized: false, //to allow for animations
animation: info_args.animation,
});
if (info_args.infopop != null) {
/*content is forced into div with black font as google default
* is white text on white background ?!?!?!
*/
info_args.infopop = model.array_defaults(
info_args.infopop,
{
fontcolor: '#000000',
infowidth: '40em',
streetview: false
});
//standard google defs
var infowindow = new google.maps.InfoWindow();
//extra seperate data ref for client apps
infowindow.metadata = {
parent: this,
parentid: this.infowindowindex,
};
this.infowindows[this.infowindowindex] = {
infowidth: info_args.infopop.infowidth,
content: info_args.infopop.content,
fontcolor: info_args.infopop.fontcolor,
latlng: {lat: lat, lng: lng},
streetview: info_args.infopop.streetview
};
this.infowindowindex++;
//build for pin click
google.maps.event.addListener(pin, 'click', function () {
var infodata = infowindow.metadata.parent.infowindows[infowindow.metadata.parentid];
//build content
var infocontent = "<div id='" + infowindow.metadata.parent.name + "infowindow" + (infowindow.metadata.parentid) + "' style='color:" + infodata.fontcolor + ";width:" + infodata.infowidth + "'>";
infocontent += infodata.content;
if (infodata.streetview) {
infocontent += "<div id='" + infowindow.metadata.parent.name + "infowindowsvframe" + (infowindow.metadata.parentid) + "' class='infostreetviewframe' >";
infocontent += ". . . loading view</div>";
}
infocontent += "</div>";
infowindow.setContent(infocontent);
infowindow.open(this.map, pin);
if (infodata.streetview) {
var service = new google.maps.StreetViewService();
infowindow.metadata.parent.geo_query({type: "sv_pano_latlng", latlng: infodata.latlng, radius: 50, clientid: infowindow.metadata.parent.name, streetviewframe: infowindow.metadata.parent.name + "infowindowsvframe" + (infowindow.metadata.parentid), callback: "add_streetview_do"});
}
});
}
}
/*
* 'add_streetview'
* for flexibility addition of a streetview into a named dom element id
*
* @param object args
* @param.args string elemid the element to put the streetview into
*
*/
GMap.prototype.add_streetview_do = function (predata, callbackdata) {
var svelement = document.getElementById(predata.streetviewframe);
if (callbackdata.status == google.maps.StreetViewStatus.OK) {
var targetPOVlocation = predata.latlng
var svLatLng = {lat: callbackdata.results.location.latLng.lat(), lng: callbackdata.results.location.latLng.lng()};
// var svLatLng = new google.maps.LatLng(callbackdata.results.location.latLng.lat(), callbackdata.results.location.latLng.lng());
//var svLatLng = callbackdata.results.location.latLng;
var povyaw = this.getBearing(svLatLng, targetPOVlocation);
var sv = new google.maps.StreetViewPanorama(svelement);
var svoptions = {
position: svLatLng,
addressControl: false,
linksControl: false,
panControl: false,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL
},
pov: {
heading: povyaw,
pitch: 10,
zoom: 1
},
enableCloseButton: false,
visible: true
};
sv.setOptions(svoptions);
} else {
svelement.innerHTML = "no streetview available";
}
}
/*
* 'getBearing'
* calcs bearing from two latlngs
*
*/
GMap.prototype.getBearing = function (fromLatLng, targetLatLng) {
var DEGREE_PER_RADIAN = 57.2957795;
var RADIAN_PER_DEGREE = 0.017453;
var dlat = targetLatLng.lat - fromLatLng.lat;
var dlng = targetLatLng.lng - fromLatLng.lng;
// We multiply dlng with cos(endLat), since the two points are very closeby,
// so we assume their cos values are approximately equal.
var bearing = Math.atan2(dlng * Math.cos(fromLatLng.lat * RADIAN_PER_DEGREE), dlat)
* DEGREE_PER_RADIAN;
if (bearing >= 360) {
bearing -= 360;
} else if (bearing < 0) {
bearing += 360;
}
return bearing;
}
/*
* 'add_polygon'
* adds a polygon to a map using an array of lat lng coords
* these are pushed in the order they appear in the locationarray
*
* @param array locationarray - which event/change type triggers update of this element
*
*/
GMap.prototype.add_polygon = function (args) {
//construct and store polygon
var polyobj = model.array_defaults(args, {name: "polygon", polygon: null, infohtml: null, box: null, focuson: false});
//remove any same named polygon
if (this.polygons[args.name] != undefined) {
this.polygons[args.name].polygon.setMap(null);
this.polygons[args.name] = null;
}
var newpolygon = new Array();
polyobj.box = new google.maps.LatLngBounds();
for (var l in args.polygon) {
var point = new google.maps.LatLng(args.polygon[l].lat, args.polygon[l].lng);
newpolygon.push(point);
polyobj.box.extend(point);
}
polyobj.polygon = new google.maps.Polygon(
{
map: this.map,
paths: newpolygon,
strokeColor: '#00ff00',
strokeOpacity: 0.75,
strokeWeight: 2,
fillColor: '#00ff00',
fillOpacity: 0.15
}
);
this.polygons[args.name] = polyobj;
//set required changes to map
if (args.infohtml != null) {
// content is forced into div with black font as google default is white text on white background ?!?!?!
polyobj.info = new google.maps.InfoWindow(
{
content: "<div style='color:#000000'>" + args.infohtml + "</div>"
});
var selfobj = this;
google.maps.event.addListener(this.polygons[args.name].polygon, 'click', function (event) {
var point = event.latLng;
selfobj.polygons[args.name].info.setPosition(point);
selfobj.polygons[args.name].info.open(selfobj.map);
});
}
if (polyobj.focuson) {
this.map.fitBounds(polyobj.box);
}
}
/*****************************************************************/
/* methods for CLIENT --> GMAP change */
/*********************************************************S ********/
/*
* allows gui to register elements so when changes / events occur in the map gui, its
* parent gui can be updated.
* @param string change_type - which event/change type triggers update of this element
* @param string change_return - data required to be returned eg latlng or address etc
* @param string callback - the page element identifier to update
*/
GMap.prototype.register_elem = function (change_type, event_return_cfg) {
switch (change_type) {
case "location":
switch (event_return_cfg.return_type) {
case "address":
this.gui_reg.location.address.push(event_return_cfg);
break;
}
break;
}
}
/*****************************************************************/
/* methods for GMAP --> CLIENT change */
/*****************************************************************/
/*
* checks registered change type to send back to client
* @param string change_type - which event/change type triggers update of this element
* @param string data_request - data required to be returned
* @param string regelem - the parent gui identifier to update5
* @param object attr - which attribute to update eg for input, its value, for div its html
*/
GMap.prototype.update_client_go = function (change_type, data_request) {
switch (change_type) {
case "location":
var loc = this.get_viewport_coords();
this.geo_query({type: "location", lat: loc.lat, lng: loc.lng});
}
}
/*
* actions client_update
* @param string change_type - which event/change type triggers update of this element
* @param string data_request - data required to be returned
* @param string regelem - the parent gui identifier to update5
* @param object attr - which attribute to update eg for input, its value, for div its html
*/
GMap.prototype.update_client_do = function (pre_data, callback_data) {
if (callback_data.status != "ZERO_RESULTS") {
switch (pre_data.type) {
case "location":
var location_data_registrars = this.gui_reg.location;
for (var loc_type_list in location_data_registrars) {
switch (loc_type_list) {
case "address":
var address = callback_data.results[0].formatted_address;
for (var r in location_data_registrars[loc_type_list]) {
var event_return = location_data_registrars[loc_type_list][r];
if (event_return != undefined) {
event_return.address = this.address_parse("postcode", address);
if (event_return.callback != undefined) {
eval(event_return.callback + "(event_return)");
}
}
}
break;
}
}
break;
}
}
}
/*****************************************************************/
/* PRIVATE METHODS */
/*****************************************************************/
GMap.prototype.address_parse = function (required_part, address) {
var result = "";
address = address.split(", ");
var country = address[address.length - 1];
switch (country) {
case "UK":
switch (required_part) {
case "postcode":
result = address[address.length - 2];
result = result.substr(result.indexOf(" ") + 1);
break;
}
break;
}
return result;
}
GMap.prototype.loadedCallback = function (callback) {
this.calledbackonload = false;
google.maps.event.addListenerOnce(this.map, 'tilesloaded', function () {
google.maps.event.clearListeners(this.map, 'idle');
eval(callback + '()');
});
google.maps.event.addListenerOnce(this.map, 'idle', function () {
eval(callback + '()'); //add idle as a catch all
});
/*
google.maps.event.addListenerOnce(this.map, 'idle', function() {
eval(callback + '()')
});
*/
}
/*
* zooms map to next level from current depending on dir being '+' or '-'
*/
GMap.prototype.incrementZoom = function (dir, callback) {
var newZoom = this.map.getZoom();
if (dir == '+') {
newZoom++;
}
if (dir == '-') {
newZoom--;
}
this.map.setZoom(newZoom);
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* zooms map to fully zoomed in
*/
GMap.prototype.zoomMax = function (callback) {
this.map.setZoom(this.mapoptions.maxzoom);
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* zooms map to fully zoomed out
*/
GMap.prototype.zoomMin = function (callback) {
this.map.setZoom(this.mapoptions.minzoom);
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* zooms map to contain the given box as per gmaps southwest / northeast corner specs
*/
GMap.prototype.boxZoom = function (latfrom, latto, lngfrom, lngto) {
var bottomleft = {lat: latfrom, lng: lngfrom};
var topright = {lat: latto, lng: lngto};
var box = new google.maps.LatLngBounds(bottomleft, topright);
this.map.fitBounds(box);
}
/*
* forces zoom in to min max params and returns true if it has been constrained
* additional callback if required for when zoom to constraints has finished
*/
GMap.prototype.constrainzoom = function (callback) {
if (this.map.getZoom() > this.mapoptions.maxzoom) {
this.map.setZoom(this.mapoptions.maxzoom);
} else if (this.map.getZoom() < this.mapoptions.minzoom) {
this.map.setZoom(this.mapoptions.minzoom);
} else {
return false;
}
if (callback != undefined) {
this.loadedCallback(callback);
}
return true;
}
/*add extras to generic map object*/
google.maps.Map.prototype.clearMarkers = function () {
for (var i = 0; i < this.markers.length; i++) {
this.markers[i].setMap(null);
}
this.markers = new Array();
};
/*
* places an existing div by id as a map control
*/
GMap.prototype.addcontrol = function (divid, posn) {
if (posn == undefined) {
posn = google.maps.ControlPosition.TOP_LEFT;
}
this.map.controls[posn].push(document.getElementById(divid));
}
/* translate normal functions to internal map object */
/* trigger setup */
this.__construct(mapId, options);
}