How to export the React Maps with marker templates?
The marker template set in the React Maps component will not be exported when exporting the component to an image. This is because the marker templates are intended to render any items in the Maps component, such as text, images, or custom HTML elements. Since the Maps is an SVG-based component, the marker template can be rendered as foreignObject elements in the application level. It is important to note that the foreignObject element has compatibility constraints with some web browsers. This section will show you how to dynamically export the Maps with marker template using the foreignObject element.
To begin, we need to create a foreignObject element for each marker template and add it to the Maps component’s SVG element in the same positions as the component. Then, to allow image and PDF export, we must draw this SVG element in an HTML canvas element. Using the HTML image element, this canvas element can be transformed to an image. This image element is now converted to a PNG or JPEG image for export, or it can be inserted into a PDF document using Syncfusion’s ej2-pdf-export library.
In the following example, the processes described above are carried out using the “mapsExport” method. The following example shows how to export the React Maps with marker template to PNG, JPEG, and PDF files.
index.html
<html>
<body>
<div id='container'>
</body>
</html>
index.js
import { usa_map } from './usa.ts';
import { PdfDocument, PdfBitmap } from '@syncfusion/ej2-pdf-export';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
MapsComponent,
LayersDirective,
LayerDirective,
MarkersDirective,
MarkerDirective,
Marker,
Inject,
Maps,
} from '@syncfusion/ej2-react-maps';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
export function App() {
var mode;
var mapsInstance;
var type = [{ value: 'JPEG' }, { value: 'PNG' }, { value: 'PDF' }];
// On an external button click, we can process and obtain the final output in PNG, JPEG, or PDF format.
function onClick(e) {
var fileName = 'Maps';
mapsExport(mode.value, fileName);
}
function mapsExport(formatValue, fileName) {
var markerTemplateEleCount = document.getElementById(
'container_LayerIndex_0_Markers_Template_Group'
).childElementCount;
var markerElements = document.getElementById(
'container_LayerIndex_0_Markers_Template_Group'
);
var svg;
// Creating a new "foreignObject" element for each marker template, adding the marker template element to the "foreignObject" element, and finally appending the newly created "foreignObject" element to the SVG element.
for (var i = 0; i < markerTemplateEleCount; i++) {
var markerIndex = markerElements.children[i].id
.split('_MarkerIndex_')[1]
.split('_')[0];
var layerIndex = markerElements.children[i].id
.split('_LayerIndex_')[1]
.split('_')[0];
var markerEle = document
.getElementById(
'container_LayerIndex_' + layerIndex + '_Markers_Template_Group'
)
.querySelectorAll(
'[id*="container_LayerIndex_' +
layerIndex +
'_MarkerIndex_' +
markerIndex +
'_dataIndex_"]'
);
var marker = markerEle[i];
var foreign = document.createElementNS(
'http://www.w3.org/2000/svg',
'foreignObject'
);
debugger;
foreign.setAttribute(
'width',
(
marker.getBoundingClientRect().width +
mapsInstance.layers[0].markerSettings[0].border.width * 2
).toString()
);
foreign.setAttribute(
'height',
(
marker.getBoundingClientRect().height +
mapsInstance.layers[0].markerSettings[0].border.width * 2
).toString()
);
foreign.setAttribute('x', marker.style.left);
foreign.setAttribute('y', marker.style.top);
foreign.innerHTML = marker.innerHTML;
marker.style.display = 'none';
svg = document.querySelector('#container_svg');
svg.appendChild(foreign);
}
// Create a new canvas element for the marker template, determine the size of the SVG element, and set the same size to the canvas element.
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var svgSize = svg.getBoundingClientRect();
canvas.width = svgSize.width;
canvas.height = svgSize.height;
var ctx = canvas.getContext('2d');
// The below code converts an SVG element into a data URL and renders it as an image on a web page. By serialising the SVG element into a string and then encoding it in base64, the SVG data is integrated inside the data URL. When the web page is loaded, the created data URL can be passed as the src property of an <img> element, causing the SVG image to be rendered.
var svgData = new XMLSerializer().serializeToString(svg);
var img = document.createElement('img');
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(svgData));
// The "onload" event is invoked once the image has finished loading. The loaded image is rendered onto a 2D canvas within the event handler function using the "drawImage" method, with the top-left corner of the image positioned at coordinates (0, 0) on the canvas. This ensures that the loaded image is displayed correctly on the canvas once it has finished loading.
img.onload = function () {
ctx.drawImage(img, 0, 0);
if (formatValue == 'PNG' || formatValue == 'JPEG') {
// It creates a data URL from the canvas image to make a download link with the correct file name and format. The link is then included in the document, and a simulated click event is triggered. This enables the user to download the image in PNG or JPEG format with the specified file name.
var imagedata = canvas.toDataURL('image/png');
var anchor = document.createElement('a');
anchor.download = fileName + '.' + formatValue;
anchor.href = imagedata;
document.body.appendChild(anchor);
anchor.click();
} else if (formatValue == 'PDF') {
// It takes a image from a canvas element, converts it to a JPEG data URL, and inserts it into a PDF document. The "PdfBitmap" class is used to draw the image on a page in the PDF document. By saving the PDF file as "Maps.pdf", a PDF document containing the captured canvas image is created.
var imagedata = canvas.toDataURL('image/jpeg');
imagedata = imagedata.replace('data:image/jpeg;base64,', '');
var image = new PdfBitmap(imagedata);
var pdfdocument = new PdfDocument();
var page1 = pdfdocument.pages.add();
page1.graphics.drawImage(image, 0, 0);
//Save the document.
pdfdocument.save('Maps.pdf');
pdfdocument.destroy();
}
canvas.remove();
};
}
return (
<div className="control-section">
<MapsComponent
id="container"
allowPdfExport="true"
allowImageExport="true"
width="500px"
height="500px"
ref={(g) => (mapsInstance = g)}
>
<Inject services={[Marker]} />
<LayersDirective>
<LayerDirective shapeData={usa_map}>
<MarkersDirective>
<MarkerDirective
visible={true}
template="<div><div style='width: 90px;height: 60px;padding:10;background-color: white;border: 1px solid #000; border-radius: 10px; text-align: center;'> <div style='margin-top:4px;font-size:13px;font-family:Arial'> ${name} <br> ${population} <br> ${percentage}</div></div>"
dataSource={[
{
latitude: 40.245869116133036,
longitude: -73.86689818802188,
population: 187449,
percentage: '13.52%',
name: 'New York',
},
{
latitude: 39.97699722558653,
longitude: -100.23408568802203,
population: 198811,
percentage: '5.61%',
name: 'USA',
},
{
latitude: 26.509761045212812,
longitude: -103.39814818802157,
population: 155608,
percentage: '8.27%',
name: 'Mexico',
},
]}
></MarkerDirective>
</MarkersDirective>
</LayerDirective>
</LayersDirective>
</MapsComponent>
<div style={{ width: '200px', float: 'left' }}>Export Type</div>
<div>
<DropDownListComponent
width={'20%'}
id="etype"
value="JPEG"
ref={(d) => (mode = d)}
dataSource={type}
fields={{ text: 'value', value: 'value' }}
placeholder="JPEG"
/>
</div>{' '}
<br />
<div id="btn-control" style={{ 'margin-left': '10%' }}>
<ButtonComponent
onClick={onClick.bind(this)}
style={{ width: '100px' }}
isPrimary={true}
>
Export
</ButtonComponent>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('container'));
root.render(<App />);
The following screenshot illustrates the output of the above code snippet.
Conclusion
I hope you enjoyed learning how to export React Maps with marker templates.
You can refer to our React Maps feature tour page to know about its other groundbreaking feature representations and documentation, and how to quickly get started for configuration specifications. You can also explore our React Maps example to understand how to create and visualize data.
For current customers, you can check out our components from the License and Downloads page. If you are new to Syncfusion, you can try our 30-day free trial to check out our other controls.
If you have any queries or require clarifications, please let us know in the comments section below. You can also contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!