/**
* @namespace WPGMZA
* @module ModernStoreLocatorCircle
* @requires WPGMZA
*/
(function($) {
/**
* This module is the modern store locator circle
* @constructor
*/
WPGMZA.ModernStoreLocatorCircle = function(map_id, settings) {
var self = this;
var map;
if(WPGMZA.isProVersion())
map = this.map = MYMAP[map_id].map;
else
map = this.map = MYMAP.map;
this.map_id = map_id;
this.mapElement = map.element;
this.mapSize = {
width: $(this.mapElement).width(),
height: $(this.mapElement).height()
};
this.initCanvasLayer();
this.settings = {
center: new WPGMZA.LatLng(0, 0),
radius: 1,
color: "#63AFF2",
shadowColor: "white",
shadowBlur: 4,
centerRingRadius: 10,
centerRingLineWidth: 3,
numInnerRings: 9,
innerRingLineWidth: 1,
innerRingFade: true,
numOuterRings: 7,
ringLineWidth: 1,
mainRingLineWidth: 2,
numSpokes: 6,
spokesStartAngle: Math.PI / 2,
numRadiusLabels: 6,
radiusLabelsStartAngle: Math.PI / 2,
radiusLabelFont: "13px sans-serif",
visible: false
};
if(settings)
this.setOptions(settings);
};
WPGMZA.ModernStoreLocatorCircle.createInstance = function(map, settings) {
if(WPGMZA.settings.engine == "google-maps")
return new WPGMZA.GoogleModernStoreLocatorCircle(map, settings);
else
return new WPGMZA.OLModernStoreLocatorCircle(map, settings);
};
WPGMZA.ModernStoreLocatorCircle.prototype.initCanvasLayer = function() {
}
WPGMZA.ModernStoreLocatorCircle.prototype.onResize = function(event) {
this.draw();
};
WPGMZA.ModernStoreLocatorCircle.prototype.onUpdate = function(event) {
this.draw();
};
WPGMZA.ModernStoreLocatorCircle.prototype.setOptions = function(options) {
for(var name in options)
{
var functionName = "set" + name.substr(0, 1).toUpperCase() + name.substr(1);
if(typeof this[functionName] == "function")
this[functionName](options[name]);
else
this.settings[name] = options[name];
}
};
WPGMZA.ModernStoreLocatorCircle.prototype.getResolutionScale = function() {
return window.devicePixelRatio || 1;
};
WPGMZA.ModernStoreLocatorCircle.prototype.getCenter = function() {
return this.getPosition();
};
WPGMZA.ModernStoreLocatorCircle.prototype.setCenter = function(value) {
this.setPosition(value);
};
WPGMZA.ModernStoreLocatorCircle.prototype.getPosition = function() {
return this.settings.center;
};
WPGMZA.ModernStoreLocatorCircle.prototype.setPosition = function(position) {
this.settings.center = position;
};
WPGMZA.ModernStoreLocatorCircle.prototype.getRadius = function() {
return this.settings.radius;
};
WPGMZA.ModernStoreLocatorCircle.prototype.setRadius = function(radius) {
if(isNaN(radius))
throw new Error("Invalid radius");
this.settings.radius = radius;
};
WPGMZA.ModernStoreLocatorCircle.prototype.getVisible = function(visible) {
return this.settings.visible;
};
WPGMZA.ModernStoreLocatorCircle.prototype.setVisible = function(visible) {
this.settings.visible = visible;
};
/**
* This function transforms a km radius into canvas space
* @return number
*/
WPGMZA.ModernStoreLocatorCircle.prototype.getTransformedRadius = function(km)
{
throw new Error("Abstract function called");
}
WPGMZA.ModernStoreLocatorCircle.prototype.getContext = function(type)
{
throw new Error("Abstract function called");
}
WPGMZA.ModernStoreLocatorCircle.prototype.getCanvasDimensions = function()
{
throw new Error("Abstract function called");
}
WPGMZA.ModernStoreLocatorCircle.prototype.draw = function() {
var settings = this.settings;
var canvasDimensions = this.getCanvasDimensions();
var canvasWidth = canvasDimensions.width;
var canvasHeight = canvasDimensions.height;
var map = this.map;
var resolutionScale = this.getResolutionScale();
context = this.getContext("2d");
context.clearRect(0, 0, canvasWidth, canvasHeight);
if(!settings.visible)
return;
context.shadowColor = settings.shadowColor;
context.shadowBlur = settings.shadowBlur;
// NB: 2018/02/13 - Left this here in case it needs to be calibrated more accurately
/*if(!this.testCircle)
{
this.testCircle = new google.maps.Circle({
strokeColor: "#ff0000",
strokeOpacity: 0.5,
strokeWeight: 3,
map: this.map,
center: this.settings.center
});
}
this.testCircle.setCenter(settings.center);
this.testCircle.setRadius(settings.radius * 1000);*/
// Reset transform
context.setTransform(1, 0, 0, 1, 0, 0);
var scale = Math.pow(2, map.getZoom()) * resolutionScale;
context.scale(scale, scale);
// Translate by world origin
var offset = this.getWorldOriginOffset();
context.translate(offset.x, offset.y);
// Get center and project to pixel space
var center = new WPGMZA.LatLng(this.settings.center);
var worldPoint = this.getCenterPixels();
var rgba = WPGMZA.hexToRgba(settings.color);
var ringSpacing = this.getTransformedRadius(settings.radius) / (settings.numInnerRings + 1);
// TODO: Implement gradients for color and opacity
// Inside circle (fixed?)
context.strokeStyle = settings.color;
context.lineWidth = (1 / scale) * settings.centerRingLineWidth;
context.beginPath();
context.arc(
worldPoint.x,
worldPoint.y,
this.getTransformedRadius(settings.centerRingRadius) / scale, 0, 2 * Math.PI
);
context.stroke();
context.closePath();
// Spokes
var radius = this.getTransformedRadius(settings.radius) + (ringSpacing * settings.numOuterRings) + 1;
var grad = context.createRadialGradient(0, 0, 0, 0, 0, radius);
var rgba = WPGMZA.hexToRgba(settings.color);
var start = WPGMZA.rgbaToString(rgba), end;
var spokeAngle;
rgba.a = 0;
end = WPGMZA.rgbaToString(rgba);
grad.addColorStop(0, start);
grad.addColorStop(1, end);
context.save();
context.translate(worldPoint.x, worldPoint.y);
context.strokeStyle = grad;
context.lineWidth = 2 / scale;
for(var i = 0; i < settings.numSpokes; i++)
{
spokeAngle = settings.spokesStartAngle + (Math.PI * 2) * (i / settings.numSpokes);
x = Math.cos(spokeAngle) * radius;
y = Math.sin(spokeAngle) * radius;
context.setLineDash([2 / scale, 15 / scale]);
context.beginPath();
context.moveTo(0, 0);
context.lineTo(x, y);
context.stroke();
}
context.setLineDash([]);
context.restore();
// Inner ringlets
context.lineWidth = (1 / scale) * settings.innerRingLineWidth;
for(var i = 1; i <= settings.numInnerRings; i++)
{
var radius = i * ringSpacing;
if(settings.innerRingFade)
rgba.a = 1 - (i - 1) / settings.numInnerRings;
context.strokeStyle = WPGMZA.rgbaToString(rgba);
context.beginPath();
context.arc(worldPoint.x, worldPoint.y, radius, 0, 2 * Math.PI);
context.stroke();
context.closePath();
}
// Main circle
context.strokeStyle = settings.color;
context.lineWidth = (1 / scale) * settings.centerRingLineWidth;
context.beginPath();
context.arc(worldPoint.x, worldPoint.y, this.getTransformedRadius(settings.radius), 0, 2 * Math.PI);
context.stroke();
context.closePath();
// Outer ringlets
var radius = radius + ringSpacing;
for(var i = 0; i < settings.numOuterRings; i++)
{
if(settings.innerRingFade)
rgba.a = 1 - i / settings.numOuterRings;
context.strokeStyle = WPGMZA.rgbaToString(rgba);
context.beginPath();
context.arc(worldPoint.x, worldPoint.y, radius, 0, 2 * Math.PI);
context.stroke();
context.closePath();
radius += ringSpacing;
}
// Text
if(settings.numRadiusLabels > 0)
{
var m;
var radius = this.getTransformedRadius(settings.radius);
var clipRadius = (12 * 1.1) / scale;
var x, y;
if(m = settings.radiusLabelFont.match(/(\d+)px/))
clipRadius = (parseInt(m[1]) / 2 * 1.1) / scale;
context.font = settings.radiusLabelFont;
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = settings.color;
context.save();
context.translate(worldPoint.x, worldPoint.y)
for(var i = 0; i < settings.numRadiusLabels; i++)
{
var spokeAngle = settings.radiusLabelsStartAngle + (Math.PI * 2) * (i / settings.numRadiusLabels);
var textAngle = spokeAngle + Math.PI / 2;
var text = settings.radiusString;
var width;
if(Math.sin(spokeAngle) > 0)
textAngle -= Math.PI;
x = Math.cos(spokeAngle) * radius;
y = Math.sin(spokeAngle) * radius;
context.save();
context.translate(x, y);
context.rotate(textAngle);
context.scale(1 / scale, 1 / scale);
width = context.measureText(text).width;
height = width / 2;
context.clearRect(-width, -height, 2 * width, 2 * height);
context.fillText(settings.radiusString, 0, 0);
context.restore();
}
context.restore();
}
}
})(jQuery);