@hmscore/react-native-hms-map
Version:
React Native HMS Map Kit
608 lines (596 loc) • 18.6 kB
JavaScript
/*
* Copyright 2020-2024. Huawei Technologies Co., Ltd. All rights reserved.
*
* 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
*
* https://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.
*/
import HMSMap, {
HMSInfoWindow,
HMSMarker,
Hue,
MapTypes,
FillMode,
RepeatMode,
Interpolator,
} from "@hmscore/react-native-hms-map";
import React from "react";
import {
Button,
SafeAreaView,
ScrollView,
Text,
TextInput,
View,
Switch,
Image,
ToastAndroid,
StyleSheet,
} from "react-native";
import { styles } from "../styles/styles";
let planeRef;
let count = 0;
const exampleAnimation = {
rotate: {
fromDegree: 0,
toDegree: 150,
duration: 3000,
},
translate: {
latitude: 40.97511809993672,
longitude: 29.066076168319412,
duration: 10000,
interpolator: Interpolator.ACCELERATE_DECELERATE,
},
scale: {
fromX: 0.5,
fromY: 0.5,
toX: 1.5,
toY: 1.5,
repeatCount: 1,
},
alpha: {
fromAlpha: 0.6,
toAlpha: 1,
repeatCount: 2,
},
};
const defaultOptions = {
duration: 5000,
fillMode: FillMode.FORWARDS,
repeatCount: 0,
repeatMode: RepeatMode.REVERSE,
interpolator: Interpolator.LINEAR,
};
export default class Markers extends React.Component {
static options = {
topBar: {
title: {
text: "Markers",
},
},
};
state = {
defaultActionOnClick: true,
rotation: 0,
markerClustering: true,
params: {},
fillMode: FillMode.FORWARDS,
repeatMode: RepeatMode.RESTART,
interpolator: 0,
duration: 2000,
repeatCount: 0,
animation: {},
additionalMarkers: [],
};
renderAdditionalMarkers() {
return this.state.additionalMarkers.map((m) => (
<HMSMarker
key={m.id}
coordinate={m.coordinate}
title={"Marker-" + m.id}
clusterable
snippet={`Lat:${m.coordinate.latitude.toFixed(
4
)}, Long:${m.coordinate.longitude.toFixed(4)}`}
icon={{ hue: (m.id * 30) % 360 }}
onInfoWindowLongClick={() => {
this.setState((state) => ({
additionalMarkers: state.additionalMarkers.filter(
(value) => value.id != m.id
),
}));
}}
/>
));
}
CustomInfoWindow = () => (
<HMSInfoWindow>
<View style={customStyles.transparent}>
<View style={customStyles.container}>
<Text style={[customStyles.infoText, customStyles.font13]}>
Galata Tower
</Text>
<Text style={[customStyles.infoText, customStyles.font11]}>
Custom info window
</Text>
</View>
<View style={customStyles.infoWindow} />
</View>
</HMSInfoWindow>
);
TextFieldView = () => {
const { params } = this.state;
return (
<View style={styles.flexCol}>
<View>
<Text style={customStyles.font11}>
Param1: fromDegree/fromAlpha/fromX/latitude
</Text>
<Text style={customStyles.font11}>
Param2: toDegree/toAlpha/toX/longitude
</Text>
<Text style={customStyles.font11}>Param3: fromY</Text>
<Text style={customStyles.font11}>Param4: toY</Text>
</View>
<View style={[styles.flexRow, { justifyContent: "space-between" }]}>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ params: { ...params, param1: parseFloat(text) } })
}
placeholder="Param1"
keyboardType="number-pad"
/>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ params: { ...params, param2: parseFloat(text) } })
}
placeholder="Param2"
keyboardType="number-pad"
/>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ params: { ...params, param3: parseFloat(text) } })
}
placeholder="Param3"
keyboardType="number-pad"
/>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ params: { ...params, param4: parseFloat(text) } })
}
placeholder="Param4"
keyboardType="number-pad"
/>
</View>
<View style={[styles.flexRow]}>
<View style={[styles.flexRow, { alignItems: "center", padding: 4 }]}>
<Switch
value={this.state.fillMode == FillMode.FORWARDS ? false : true}
onValueChange={() => {
this.setState((state) => ({
fillMode:
state.fillMode == FillMode.FORWARDS
? FillMode.BACKWARDS
: FillMode.FORWARDS,
}));
}}
/>
<View style={styles.width100}>
<Text>
{this.state.fillMode == FillMode.FORWARDS
? "FORWARDS"
: "BACKWARDS"}
</Text>
</View>
</View>
<View style={[styles.flexRow, { alignItems: "center", padding: 4 }]}>
<Switch
value={this.state.repeatMode == RepeatMode.RESTART ? false : true}
onValueChange={() => {
this.setState((state) => ({
repeatMode:
state.repeatMode == RepeatMode.RESTART
? RepeatMode.REVERSE
: RepeatMode.RESTART,
}));
}}
/>
<View style={styles.width100}>
<Text>
{this.state.repeatMode == RepeatMode.RESTART
? "RESTART"
: "REVERSE"}
</Text>
</View>
</View>
</View>
<View style={[styles.flexRow]}>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ duration: parseInt(text, 10) })
}
placeholder="Duration"
keyboardType="number-pad"
/>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ repeatCount: parseInt(text, 10) })
}
placeholder="Repeat Count"
keyboardType="number-pad"
/>
<TextInput
style={styles.textInput}
onChangeText={(text) =>
this.setState({ interpolator: parseInt(text, 10) })
}
placeholder="Interpolator 0-9"
keyboardType="number-pad"
/>
</View>
<View style={[styles.flexRow]}>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
this.setState((state) => ({
animation: {
...state.animation,
rotate: {
fromDegree: state.params.param1,
toDegree: state.params.param2,
duration: state.duration,
repeatMode: state.repeatMode,
fillMode: state.fillMode,
repeatCount: state.repeatCount,
interpolator: state.interpolator,
},
},
}));
ToastAndroid.show(`rotation set`, ToastAndroid.SHORT);
}}
title="Set Rotate"
/>
</View>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
this.setState((state) => ({
animation: {
...state.animation,
alpha: {
fromAlpha: state.params.param1,
toAlpha: state.params.param2,
duration: state.duration,
repeatMode: state.repeatMode,
fillMode: state.fillMode,
repeatCount: state.repeatCount,
interpolator: state.interpolator,
},
},
}));
ToastAndroid.show(`alpha set`, ToastAndroid.SHORT);
}}
title="Set Alpha"
/>
</View>
</View>
<View style={[styles.flexRow]}>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
this.setState((state) => ({
animation: {
...state.animation,
scale: {
fromX: state.params.param1,
toX: state.params.param2,
fromY: state.params.param3,
toY: state.params.param4,
duration: state.duration,
repeatMode: state.repeatMode,
fillMode: state.fillMode,
repeatCount: state.repeatCount,
interpolator: state.interpolator,
},
},
}));
ToastAndroid.show(`scale set`, ToastAndroid.SHORT);
}}
title="Set Scale"
/>
</View>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
this.setState((state) => ({
animation: {
...state.animation,
translate: {
latitude: state.params.param1,
longitude: state.params.param2,
duration: state.duration,
repeatMode: state.repeatMode,
fillMode: state.fillMode,
repeatCount: state.repeatCount,
interpolator: state.interpolator,
},
},
}));
ToastAndroid.show(`translate set`, ToastAndroid.SHORT);
}}
title="Set Translate"
/>
</View>
</View>
<View style={[styles.flexRow]}>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
planeRef.setAnimation(this.state.animation);
planeRef.startAnimation();
}}
title="Animate"
color="#841584"
/>
</View>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
this.setState({ animation: {} });
}}
title="Clear"
color="red"
/>
</View>
<View style={[styles.flex1, styles.m2]}>
<Button
onPress={() => {
planeRef.setAnimation(exampleAnimation, defaultOptions);
planeRef.startAnimation();
}}
title="FLY PLANE"
color="orange"
/>
</View>
</View>
</View>
);
};
render() {
return (
<SafeAreaView>
<HMSMap
onMapLongClick={(e) => {
console.log("HMSMap onMapLongClick, result", e.nativeEvent);
const coordinate = e.nativeEvent.coordinate;
this.setState((state) => ({
additionalMarkers: [
...state.additionalMarkers,
{
coordinate: coordinate,
id: count++,
},
],
}));
}}
zoomControlsEnabled
markerClustering={this.state.markerClustering}
compassEnabled
markerClusterColor={[255, 230, 130, 0]}
markerClusterTextColor={[200, 0, 0, 0]}
markerClusterIcon={{
asset: "ic_launcher.png",
}}
style={styles.height200}
mapType={MapTypes.NORMAL}
camera={{
target: {
latitude: 41.02155220194891,
longitude: 29.0037998967586,
},
zoom: 12,
}}
>
<HMSMarker
title="Maiden's Tower"
snippet="This is a default marker"
rotation={this.state.rotation}
draggable
onClick={() => {
this.setState((state) => ({
rotation: (state.rotation + 30) % 360,
}));
}}
defaultActionOnClick={this.state.defaultActionOnClick}
coordinate={{
latitude: 41.02155220194891,
longitude: 29.0037998967586,
}}
/>
<HMSMarker
icon={{ hue: Hue.ORANGE }}
title="Ayasofia"
snippet="This is a colored default marker"
clusterable
coordinate={{
latitude: 41.008699470240245,
longitude: 28.98015976031702,
}}
/>
<HMSMarker
icon={{ hue: Hue.MAGENTA }}
title="Sultan Ahmet"
snippet="This is a colored default marker"
clusterable
coordinate={{
latitude: 41.00542331543524,
longitude: 28.97691153026857,
}}
/>
<HMSMarker
icon={{ hue: Hue.AZURE }}
title="Topkapı Museum"
snippet="This is a colored default marker"
clusterable
coordinate={{
latitude: 41.01223774385668,
longitude: 28.983498212850378,
}}
/>
<HMSMarker
icon={{ hue: Hue.ROSE }}
title="Column of Constantine"
snippet="This is a colored default marker"
clusterable
coordinate={{
latitude: 41.0087711,
longitude: 28.97133142052397,
}}
/>
<HMSMarker
icon={{ hue: Hue.CYAN }}
title="Gülhane Parkı"
snippet="This is a colored default marker"
clusterable
coordinate={{
latitude: 41.01358808151719,
longitude: 28.98213804657346,
}}
/>
<HMSMarker
icon={{
uri: Image.resolveAssetSource(
require("../assets/galata-tower.png")
).uri,
width: 140,
height: 150,
}}
clickable={true}
coordinate={{
latitude: 41.02564844393837,
longitude: 28.974169719709817,
}}
>
{this.CustomInfoWindow()}
</HMSMarker>
<HMSMarker
title="Validebağ Korusu"
snippet="This is custom icon from url."
icon={{
uri:
"https://www-file.huawei.com/-/media/corp/home/image/logo_400x200.png",
width: 200,
height: 100,
}}
coordinate={{
latitude: 41.01432234547145,
longitude: 29.046580953343877,
}}
/>
<HMSMarker
ref={(e) => {
planeRef = e;
}}
onAnimationStart={(e) =>
console.log(`Animation ${e.nativeEvent.type} Started`)
}
onAnimationEnd={(e) =>
console.log(
`Animation ${e.nativeEvent.type} Ended in ${e.nativeEvent.duration} ms`
)
}
icon={{
asset: "plane.png",
}}
coordinate={{
latitude: 41.02664844393837,
longitude: 28.984169719709817,
}}
/>
{this.renderAdditionalMarkers()}
</HMSMap>
<ScrollView style={styles.height450}>
<View style={[styles.flexRow]}>
<View
style={[styles.flexRow, { alignItems: "center", padding: 4 }]}
>
<Switch
value={this.state.defaultActionOnClick}
onValueChange={() => {
this.setState((state) => ({
defaultActionOnClick: !state.defaultActionOnClick,
}));
}}
/>
<View style={styles.width100}>
<Text>Default Action on Click</Text>
</View>
</View>
<View
style={[styles.flexRow, { alignItems: "center", padding: 4 }]}
>
<Switch
value={this.state.markerClustering}
onValueChange={() => {
this.setState((state) => ({
markerClustering: !state.markerClustering,
}));
}}
/>
<View style={styles.width100}>
<Text>Marker Clustering</Text>
</View>
</View>
</View>
<View style={styles.p4}>
<Text>Animate Plane</Text>
{this.TextFieldView()}
</View>
</ScrollView>
</SafeAreaView>
);
}
}
const customStyles = StyleSheet.create({
viewHeight: { height: 400 },
flexMargin: { flex: 1, margin: 1 },
container: {
backgroundColor: "rgb(49,49,49)",
borderRadius: 6,
paddingHorizontal: 14,
paddingVertical: 6,
},
font11: { fontSize: 11 },
font13: { fontSize: 13 },
transparent: { backgroundColor: "transparent" },
infoText: { color: "#fff", fontFamily: "Muli", fontSize: 12 },
infoWindow: {
width: 0,
height: 0,
backgroundColor: "transparent",
borderStyle: "solid",
borderLeftWidth: 8,
borderRightWidth: 8,
borderBottomWidth: 16,
borderLeftColor: "transparent",
borderRightColor: "transparent",
borderBottomColor: "rgb(49,49,49)",
alignSelf: "center",
transform: [{ rotate: "180deg" }],
},
});