/** * @class PolygonControl * @version 0.3 * @copyright (c) . 2008 * @author Chris Marx * * @fileoverview MarkerControl is used to create points/markers *

*/ /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * PolygonControl * @constructor * @param {Object} opt_opts * @param {Object} button_opts * @param {String} opt_opts.img_up_url The image to display on the button in the up state * @param {String} opt_opts.img_down_url The image to display on the button in the down state * @param {String} opt_opts.name The name of the button * @param {String} opt_opts.tooltip The button's tooltip text * @param {Object} opt_opts.position Position options * @param {Array} position.controlPosition The offsets for the control's position * @param {Object} opt_opts.tooltip Tooltip Options * @param {Array} tooltip.anchor The offset for postion of tooltip * @param {String} tooltip.cursor_on The url for a custom css cursor * @param {String} tooltip.cursor_off The url for a custom css cursor * @param {Object} titles Titles used for tooltip * @param {String} titles.img_up_url Url of up image * @param {String} titles.img_down_url Url of down image * @param {String} titles.tooltip Text of tooltip * @param {Function} tooltip.callback Optional callback executed for tooltips * @param {Object} newGeometryOptions The options used when creating new geometries * @param {String} newGeometryOptions.strokeColor The stroke (line) color * @param {Integer} newGeometryOptions.strokeWieght The stroke (line) weight (width) * @param {Float} newGeometryOptions.strokeOpacity The stroke (line) opacity * @param {String} newGeometryOptions.fillColor The fill color * @param {Float} newGeometryOptions.fillOpacity The fill opacity * @param {Object} opts * @param {Boolean} opts.clickable Is geometry clickable * @param {Object} geometryListenerOpts Settings for geometry listeners * @param {Boolean} geometryListenerOpts.mouseoverEditingEnabled Is mousover editing enabled * @param {Boolean} geometryListenerOpts.infoWindowHtmlEnabled Is the default infoWindow with html * @param {Boolean} geometryListenerOpts.mouseoverHighlightingEnabled Is mouseover highlighting enabled * @param {Boolean} geometryListenerOpts.infoWindowTabsEnabled Is the default infoWindow with tabs * @param {Function} geometryListenerOpts.assembleInfoWindowTabs Configurable for custom tabs. If default content is desired, add it to the first tab * @param {Boolean} multiEdit Determines whether one can add multiple geometries at once (rather than having the control turn off after each addition) * @param {Object} htmlTemplateParams Optional data object for html template * @param {String} cssId The prefix used for id's in the css * @param {Function} optionalGeometryListeners Optional function to add additional listeners to geometries * @param {Boolean} autoSave Determines whether saving geometry data also triggers a post to a DB */ function PolygonControl(opt_opts) { var me = this; me.type = "polygon"; me.name = me.type + "Control"; me.zuper = null; me.digitizerShape = null; me.editLineHandler = null; me.endLineHandler = null; me.infoWindowHtml = ""; me.infoWindowTabs = []; me.styles = { standard:{}//TODO }; /** * Array used for storage. Remember to check for nulls when exporting data * Geometries are tied to their index, so entries are cleared, not deleted * @see GeometryControls#beans#Geometry For expected strucutre of storage entries */ me.storage = [/*array of GeometryControls#beans#Geometry*/]; //self documenting object with default settings specific for PolygonControl me.Options = { button_opts:{ img_up_url:'http://www.google.com/intl/en_us/mapfiles/ms/t/Bpu.png', img_down_url:'http://www.google.com/intl/en_us/mapfiles/ms/t/Bpd.png', name:'polygon', tooltip:'Draw a shape' }, position:{ controlPosition:[245,3] }, tooltip:{ anchor:[-30,-8], cursor_on:"", //only for overriding default digitizing cursor cursor_off:"", titles:{ start:"Click to start drawing a shape", middle:"Click to continue drawing a shape", end:"Click a vertex once, or double click on the map to end this shape" }, callback:null }, newGeometryOptions: { strokeColor:"#000000", strokeWeight:3, strokeOpacity:0.25, fillColor:"#0000FF", fillOpacity:0.45, opts:{ clickable:true } }, geometryListenerOpts:{ mouseoverEditingEnabled:true, infoWindowHtmlEnabled:true, mouseoverHighlightingEnabled:true, infoWindowTabsEnabled:false, /** * Optional function to load up additional information from html template for tabs * If the original infoWindowHtml content is desired, add it as the first tab in the array. */ assembleInfoWindowTabs:function(){ me.infoWindowTabs.push(new GInfoWindowTab("Geometry Controls", me.infoWindowHtml)); me.infoWindowTabs.push(new GInfoWindowTab("Example Tab", me.zuper.infoWindowHtmlTemplates["infoWindowTabContent1"])); } }, multiEdit:false, //allows for digitzing multiple geometries, useful for points, should polys support it too? htmlTemplateParams:{}, cssId:"emmc-polygon", optionalGeometryListeners:null, autoSave:false }; //TODO candidate to move to GeometryControls //overide the default options if(typeof(opt_opts)!="undefined"){ for (var o in opt_opts) { if(typeof(opt_opts[o]) === "object"){ for (var p in opt_opts[o]){ me.Options[o][p] = opt_opts[o][p]; } } else { me.Options[o] = opt_opts[o]; } } } else { //me._super.debug("??"); } }; PolygonControl.prototype = new GControl(); /** * Expected by GControl, sets control position */ PolygonControl.prototype.getDefaultPosition = function(){ var me = this; return me.zuper.getDefaultPosition(me.Options.position); }; /** * Extend for polygon specific implementation * @param {GMap2} map The map that has had this ExtMapTypeControl added to it. * @return {DOM Object} Div that holds the control */ PolygonControl.prototype.initialize = function(map){ var me = this; me.container = document.createElement("div"); me.container.id = "mymaps-control-"+me.Options.button_opts.name; var button = me.zuper.createButton({ controlName:me.name, button_opts:me.Options.button_opts, startDigitizing:function(){ me.startDigitizing(); }, stopDigitizing:function(t){ me.stopDigitizing(t); } }); me.container.appendChild(button.img); map.getContainer().appendChild(me.container); me.runInitFunctions(); return me.container; }; /** * Remember, all init functions get executed from #initialize because zuper isn't defined * until the control is added to zuper, with GeometryControls#addControl, and that's * when initialize is called automatically by GControl * @see #initialize */ PolygonControl.prototype.runInitFunctions = function(){ var me = this; me.tooltip(); me.assembleInfoWindowHtml(me.Options.htmlTemplateParams); me.extendGPolygon(); }; /** * Starts digitizing process, turns on tooltip, calls function for geometry creation * TODO - break up some of the functions? * @see #newGPolygon */ PolygonControl.prototype.startDigitizing = function() { var me = this, zuper = me.zuper, map = zuper.map, Options = me.Options; me.tooltip.on(Options.tooltip.titles["start"]); me.digitizerShape = me.newGPolygon([], Options.newGeometryOptions); map.addOverlay(me.digitizerShape); me.digitizerShape.enableDrawing({}); //change the tooltip text while digitizing me.editLineHandler = GEvent.addListener(me.digitizerShape,"lineupdated",function(){ switch(me.digitizerShape.getVertexCount()){ case 2: me.tooltip.tooltipContainer.innerHTML = Options.tooltip.titles["middle"]; break; case 3: me.tooltip.tooltipContainer.innerHTML = Options.tooltip.titles["end"]; break; } }); //listen for cancel events GEvent.addListener(me.digitizerShape,"cancelline",function(){ me.stopDigitizing(); }); //create permanent polygon me.endLineHandler = GEvent.addListener(me.digitizerShape,"endline",function(latlng){ var coords = []; for(var i=0;i -1){ var shortName = style.replace("stroke","").toLowerCase(); stroke[shortName] = strokeFlag = styles[style]; } if(style.indexOf("fill") > -1){ var shortName = style.replace("fill","").toLowerCase(); fill[shortName] = fillFlag = styles[style]; } } //TODO dont use flag, check if object is empty if(strokeFlag){ geometry.setStrokeStyle(stroke); } if(fillFlag){ geometry.setFillStyle(fill); } //update unsavedStyles for(var o in styles){ geometry.unsavedStyle[o] = styles[o]; } } //style.replace(/\b[a-z]/g,style[0].toUpperCase()); //capitalizes first letter }; /** * TODO, a mouseover/out implementation for better tooltips (on the polygons) * @param {Object} index */ PolygonControl.prototype.hoverTooltip = function(){ // }; /** * Loads polygons from json * @param {Object} record The json representation of polygon */ PolygonControl.prototype.loadPolygons = function(record){ var me = this; var polygon = me.createPolygon(record.coordinates,me.infoWindowHtml,false,record.style); me.storage[polygon.index].title = [record.title,record.title]; me.storage[polygon.index].description = [record.description,record.description]; me.zuper.map.addOverlay(polygon); return polygon; }; /** * Convenience method to be able to pass in options as an object * @param {Array} coords An array of GLatLngs * @param {Object} opts An object with the standard opts for a GPolygon */ PolygonControl.prototype.newGPolygon = function(coords, opts){ return new GPolygon(coords, opts.strokeColor, opts.strokeWeight, opts.strokeOpacity, opts.fillColor, opts.fillOpacity, opts.opts); }; /** * Convenience add getter/setters for objects that need translation between stored and displayed value * And do basic input validation (and revert to stored values if values are invalid) * Note: if these methods are eventually added to the api, then these methods will need to be updated * to use call() to access super method. */ PolygonControl.prototype.extendGPolygon = function(){ GPolygon.unsavedStyle = {}; GPolygon.savedStyle = {}; GPolygon.prototype.getStrokeWeight = function(){ return (this.unsavedStyle.strokeWeight || this.savedStyle.strokeWeight); }; GPolygon.prototype.setStrokeWeight = function(weight){ if(!isNaN(weight)){ this.unsavedStyle.strokeWeight = (weight > 20) ? 20 : (weight < 1) ? 1 : weight; } else { this.unsavedStyle.strokeWeight = this.savedStyle.strokeWeight; } return this.unsavedStyle.strokeWeight || this.savedStyle.strokeWeight; }; GPolygon.prototype.getStrokeOpacity = function(){ return (this.unsavedStyle.strokeOpacity || this.savedStyle.strokeOpacity) * 100; }; GPolygon.prototype.setStrokeOpacity = function(opacity){ if(!isNaN(opacity)){ var storedOpacity = (opacity > 100) ? 100 : (opacity < 0) ? 0 : opacity; this.unsavedStyle.strokeOpacity = storedOpacity / 100; } else { this.unsavedStyle.strokeOpacity = this.savedStyle.strokeOpacity; } return this.unsavedStyle.strokeOpacity || this.savedStyle.strokeOpacity; }; GPolygon.prototype.getFillOpacity = function(){ return (this.unsavedStyle.fillOpacity || this.savedStyle.fillOpacity) * 100; }; GPolygon.prototype.setFillOpacity = function(opacity){ if(!isNaN(opacity)){ var storedOpacity = (opacity > 100) ? 100 : (opacity < 0) ? 0 : opacity; this.unsavedStyle.fillOpacity = storedOpacity / 100; } else { this.unsavedStyle.fillOpacity = this.savedStyle.fillOpacity; } return this.unsavedStyle.fillOpacity || this.savedStyle.fillOpacity; }; };