I have a Google Maps Circle drawn on v3 api. When the user has plotted there circle (or polygon if they choose), they can save the data to the server. If the user has picked
This solution uses the significantly more versatile Canvas API to draw over the map image . All code is in Typescript, so simply remove type declarations if you're using Javascript.
ADVANGES OF USING CANVAS:
USAGE:
// DEFINE BASIC VARIABLES
const latitude: -34.3566871,
const longitude: 18.4967666
const mapZoom = 12;
const imageWidth: 100;
const imageHeight: 100;
// INVOKE UTILITY FUNCTION
savePlaceImage({
// SET BASIC PROPS
latitude,
longitude,
mapZoom,
imageWidth,
imageHeight,
fileName: 'Cape Point',
// DRAW IMAGE USING CANVAS API
draw: ctx => {
// draw location as dot
ctx.fillStyle = '#FF3366';
ctx.beginPath();
ctx.arc(imageWidth / 2, imageHeight / 2, 10, 0, 2 * Math.PI);
ctx.fill();
// draw circle around location with 1 kilometer radius
ctx.strokeStyle = '#0000FF';
ctx.beginPath();
ctx.arc(imageWidth / 2, imageHeight / 2, pixelsPerMeter(latitude) * 1000, 0, 2 * Math.PI);
ctx.stroke();
}
})
UTILITIES:
function savePlaceImage(
config: {
latitude: number,
longitude: number,
mapZoom: number,
imageWidth: number,
imageHeight: number,
fileName: string,
draw: (ctx: CanvasRenderingContext2D) => void,
},
) {
// DOWNLOAD MAP IMAGE FROM GOOGLE'S STATIC MAPS API AS A BLOB
return from(axios.get<Blob>(`https://maps.googleapis.com/maps/api/staticmap`, {
params: {
key: GOOGLE_MAPS_API_KEY,
size: `${config.imageWidth}x${config.imageHeight}`,
zoom: `${config.mapZoom}`,
center: `${config.latitude},${config.longitude}`,
style: 'feature:all|element:labels|visibility:off',
},
responseType: 'blob',
// CONVERT BLOB TO BASE64 ENCODED STRING
}).then(response => {
const reader = new FileReader();
reader.readAsDataURL(response.data);
return new Promise<string>(resolve => reader.onloadend = () => resolve(reader.result as string));
// CREATE HTML IMG ELEMENT, SET IT'S SRC TO MAP IMAGE, AND WAIT FOR IT TO LOAD
}).then(response => {
const image = document.createElement('img');
image.src = response;
return new Promise<HTMLImageElement>(resolve => image.onload = () => resolve(image));
// CREATE HTML CANVAS ELEMENT, THEN DRAW ON TOP OF CANVAS USING CANVAS API, THEN CONVERT TO BLOB
}).then(image => {
const canvas = document.createElement('canvas');
canvas.width = config.imageWidth;
canvas.height = config.imageHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
config.draw(ctx);
return new Promise<Blob>(resolve => canvas.toBlob(blob => resolve(blob)));
// ATTACH BLOB TO HTML FORM WHICH CONVERTS IT TO A FILE TO BE POSTED, THEN SEND FILE TO SERVER
}).then(blob => {
const form = new FormData();
form.append('blob', blob, `${config.fileName}.png`);
const file = form.get('blob') as File;
return axios.post<{ file }>('https://www.my-api.com/save-image', form);
}));
}
function pixelsPerMeter(latitude: number) {
const radiusOfEarthInKilometers = 6371;
return Math.cos(latitude * Math.PI / 180) * 2 * Math.PI * radiusOfEarthInKilometers / (256 * Math.pow(2, 12));
}