我有一个名为
MapThemeFactory
的工厂类。有一种方法叫做
getTheme
方法,我正在传递themeId
。使用我得到的
主题,然后使用 switch case 我正在创建类的实例。
这里CustomMapThemeExe
class是一个抽象类。 StreetThemeExe
和VintageThemeExe
课程
从CustomMapThemeExe
类延伸。
// A factory class that creates a MapTheme object based on the selected theme type.
export class MapThemeFactory {
private config: MapReportConfigModel;
theme: any;
constructor(private http: HttpClient, config: MapReportConfigModel) {
this.config = config;
}
getTheme( themeId: number): Observable<{theme: MapTheme; customHtmlElement: any[]; customLayers: any[];}> {
// Retrieve the theme object based on the themeId
const selectedTheme = this.config.theme.list.find(
(item) => item.id === themeId
);
let themeExe: CustomMapThemeExe;
switch (selectedTheme.name) {
case "street":
themeExe = new StreetThemeExe();
break;
case "vintage":
themeExe = new VintageThemeExe();
break;
default:
}
// Load the theme properties from the config file
const themeProperties$ = this.loadThemeProperties(selectedTheme.config);
const mapThemeModel$ = themeProperties$.pipe(
map((themeData) => {
// Create the MapThemeModel object
const themeModel: MapThemeModel = {
style: themeData.style,
css: themeData.css,
marker: themeData.marker,
};
return themeModel;
})
);
// Use forkJoin to wait for both the theme properties and the theme executor to be loaded
return forkJoin([themeProperties$, mapThemeModel$]).pipe(
map(([themeData, themeModel]) => {
const customHtmlElement = themeData.externalComponents;
customHtmlElement?.forEach((elementString) => {
const element = this.cleanHtmlElement(elementString);
return themeExe.setCustomHtmlElement(element);
});
const layers = themeData.layers;
const sources = themeData.sources;
const newLayers = themeExe.setCustomLayers(layers, sources);
// Create the MapTheme object
const mapTheme: MapTheme = new MapTheme(themeModel, themeExe);
// Return an object containing the theme executor and the MapTheme object
return { theme: mapTheme, customHtmlElement, customLayers: newLayers };
})
);
}
//-----------------------------------------Helper methods--------------------------------------------
// Loads the theme properties from the specified config file.
private loadThemeProperties(configFilePath: string): Observable<any> {
return this.http.get(configFilePath);
}
// Takes in a string and removes all spaces and brackets from it
private cleanHtmlElement(elementString: string): HTMLElement {
const element = document.createElement("div");
element.innerHTML = elementString.replace(/^\[|\]$/g, "").trim();
return element;
}
}
在这部分的这个 getTheme 方法中,我调用
themeExe.setCustomHtmlElement(element)
方法和themeExe.setCustomLayers(layers, sources)
方法。在这里,我将 customHtmlElement
传递给 themeExe.setCustomHtmlElement(element)
方法和图层,传递给 setCustomLayers
method.
// Use forkJoin to wait for both the theme properties and the theme executor to be loaded
return forkJoin([themeProperties$, mapThemeModel$]).pipe(
map(([themeData, themeModel]) => {
const customHtmlElement = themeData.externalComponents;
customHtmlElement?.forEach((elementString) => {
const element = this.cleanHtmlElement(elementString);
themeExe.setCustomHtmlElement(element);
});
const layers = themeData.layers;
const sources = themeData.sources;
const newLayers = themeExe.setCustomLayers(layers, sources);
// Create the MapTheme object
const mapTheme: MapTheme = new MapTheme(themeModel, themeExe);
// Return an object containing the theme executor and the MapTheme object
return { theme: mapTheme, customHtmlElement, customLayers: newLayers };
})
export abstract class CustomMapThemeExe {
setCustomHtmlElement(htmlElements: any): any {}
setCustomLayers(layers: any, sources: any): any {}
}
export class StreetThemeExe extends CustomMapThemeExe {}
我想更改这里的 setCustomHtmlElement 和 setCustomLayers 方法。
export class VintageThemeExe extends CustomMapThemeExe {
setCustomHtmlElement(htmlElements: any): any {
const element = document.createElement("div");
element.appendChild(htmlElements);
return element;
}
setCustomLayers(layers: any, sources: any): any {
const newSource = sources || {};
const newLayer = layers || [];
const newLayers = {
newSource,
newLayer,
};
return newLayers;
}
}
在从工厂类
VintageThemeExe
返回元素和newLayer之后getTheme
方法返回它。在这个MapContainerComponent ``onThemeChange
方法中我设置了customHtmlElement
和customLayers
使用这些方法进行映射。 addCustomSourcesAndLayers
和addCustomHtmlElementAndCss
.
export class MapContainerComponent implements OnInit {
@Input() mapReportConfigData: MapReportConfigModel;
map: mapboxgl.Map;
mapIndividualsList: MapIndividualsViewModel[];
private markers: mapboxgl.Marker[] = [];
constructor(
private messageDialogService: MessageDialogService,
private translateHandler: TranslateHandler,
private mapReportService: MapReportService
) {}
ngOnInit(): void {
this.initMap();
this.subscribeToThemeData();
}
initMap(): void {
try {
const {
accessToken,
container,
zoomLevel,
centerLat,
centerLng,
projection,
} = this.mapReportConfigData;
mapboxgl!.accessToken = accessToken;
this.map = new mapboxgl.Map({
container,
zoom: zoomLevel,
center: [centerLat, centerLng],
});
this.map.setProjection(projection);
// If mapbox server issue
this.map.on("error", (response) => {
this.showError(
"lbl_error_heading",
"mapReport.err_no_map_reports",
"mapReport.err_generating_map_report_message"
).subscribe(() => {
// Return back to dashboard
window.location.href = "#0";
return;
});
});
} catch (error) {
throw new CustomError(error.message, 404, false);
}
}
onThemeChange(theme: any) {
// Remove previously added custom layers, sources and html element
this.removeCustomElements(theme);
// Add style
this.map.setStyle(theme.theme.themeModel.style);
// Add custom html elements and css
this.addCustomHtmlElementAndCss(theme);
// Call initMapIndividuals method
this.initMapIndividuals(theme);
// Add custom sources, layers
this.map.once("styledata", () => {
this.addCustomSourcesAndLayers(theme);
});
}
// Get Individuals List
initMapIndividuals(theme: any) {
this.mapReportService.getAllMapIndividuals().subscribe(
(individualsData) => {
this.mapIndividualsList = individualsData;
this.initMarkers(theme);
},
// If map individuals not found
(error: HttpErrorResponse) => {
this.showError(
"lbl_error_heading",
error.status.toString(),
error.status == 404 ? "mapReport.err_no_map_individuals" : null
);
}
);
}
// Add markers to the map
initMarkers(theme: any) {
// Remove previously added markers
this.markers.forEach((m) => m.remove());
this.markers = [];
this.mapIndividualsList.forEach((individual) => {
let marker: mapboxgl.Marker;
if (theme.theme.themeModel.marker) {
const markerImage = document.createElement("img");
markerImage.src = theme.theme.themeModel.marker;
markerImage.className = "marker";
marker = new mapboxgl.Marker({
element: markerImage,
scale: this.mapReportConfigData.markerScale,
});
} else {
marker = new mapboxgl.Marker({
scale: this.mapReportConfigData.markerScale,
});
}
const m = marker
.setLngLat([individual.longitude, individual.latitude])
.addTo(this.map);
this.markers.push(m);
});
}
// -----------------------------------Helper methods---------------------------------------------
private showError(
title: string,
info: string,
prompt: string
): Observable<any> {
return this.messageDialogService.openError(
this.translateHandler.translate(title),
this.translateHandler.translate(info),
prompt == null ? null : this.translateHandler.translate(prompt)
);
}
// akes in a string and removes all spaces and brackets from it
private cleanHtmlElement(elementString: string): HTMLElement {
const element = document.createElement("div");
element.innerHTML = elementString.replace(/^\[|\]$/g, "").trim();
return element;
}
// Notify the theme change and called onThemeChange method
private subscribeToThemeData(): void {
this.mapReportService.themeData$.subscribe((data) => {
if (data) {
this.onThemeChange(data);
}
});
}
// Remove previously added custom layers, sources and html element
private removeCustomElements(theme: any) {
// Remove previously added custom html elements
const customElements = this.map
? this.map.getContainer().querySelectorAll(".custom-html-element")
: null;
if (customElements && customElements.length > 0) {
customElements.forEach((element) => {
if (this.map) {
this.map.getContainer().removeChild(element);
}
});
}
// // Remove custom sources and layers
if (theme.customLayers) {
const sources = theme.customLayers.newSource;
Object.keys(sources).forEach((sourceId) => {
const source = sources[sourceId];
if (source.type && this.map.getSource(sourceId)) {
this.map.removeSource(sourceId);
}
});
if (theme.customLayers.newLayer) {
theme.customLayers.newLayer.forEach((layer) => {
if (this.map.getLayer(layer.id)) {
this.map.removeLayer(layer.id);
}
});
}
}
}
// Add custom sources , layers
private addCustomSourcesAndLayers(theme: any) {
this.map.once("styledata", () => {
if (theme.customLayers) {
const sources = theme.customLayers.newSource;
// Add custom sources to map
Object.keys(sources).forEach((sourceId) => {
const source = sources[sourceId];
if (source.type && !this.map.getSource(sourceId)) {
this.map.addSource(sourceId, source);
}
});
if (theme.customLayers.newLayer) {
theme.customLayers.newLayer.forEach((layer) => {
if (!this.map.getLayer(layer.id)) {
this.map.addLayer(layer);
}
});
}
}
});
}
// Add custom html elements and css
private addCustomHtmlElementAndCss(theme: any) {
// Add custom html elements to map container
if (theme.customHtmlElement) {
theme.customHtmlElement.forEach((elementString) => {
const element = this.cleanHtmlElement(elementString);
if (this.map) {
element.classList.add("custom-html-element");
this.map.getContainer().appendChild(element);
}
});
}
// Apply css
if (theme.theme.themeModel.css) {
const head = document.getElementsByTagName("head")[0];
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = theme.theme.themeModel.css;
head.appendChild(link);
}
}
}
但在这里我想做这样的事情。我想将这两种方法移到 VintageThemeExe 类中。 addCustomHtmlElementAndCss 和 addCustomSourcesAndLayers。这里我不想在 VintageThemeExe 类中使用此名称,该类已经有 2 个方法 setCustomHtmlElement 和 setCustomLayers。所以 addCustomHtmlElementAndCss = setCustomHtmlElement 和 addCustomSourcesAndLayers = setCustomLayers.
之后在工厂类中调用 setCustomHtmlElement 和 setCustomLayers 方法后我想返回 themeExe 类的实例。
之后在 onThemeChange 中我想调用 setCustomHtmlElement 和 setCustomLayers 方法。 因为它很容易变化,我可以单独保留 customHtmlElement 和 customLayers 部分。
那我该怎么做。