/**
* @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;
};
};