김승훈

중간보고서 및 소스코드

1 +node_modules/
2 +venv/
...\ No newline at end of file ...\ No newline at end of file
1 +node_modules/
2 +venv/
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 + "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 +}
1 +node_modules/**/*
2 +.expo/*
3 +npm-debug.*
4 +*.jks
5 +*.p8
6 +*.p12
7 +*.key
8 +*.mobileprovision
9 +*.orig.*
10 +web-build/
11 +web-report/
12 +
13 +# macOS
14 +.DS_Store
1 +import { Component } from 'react';
2 +import { StyleSheet, Text, View, AppRegistry } from 'react-native';
3 +import { createAppContainer } from 'react-navigation';
4 +import {createStackNavigator } from 'react-navigation-stack'
5 +import MainScreen from './components/Mainscreen';
6 +import React from 'react'
7 +
8 +const AppStackNavigator = createStackNavigator({
9 + Main:{
10 + screen: MainScreen
11 + }
12 +});
13 +
14 +export default createAppContainer(AppStackNavigator);
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "expo": {
3 + "name": "HelloWorld",
4 + "slug": "PME",
5 + "platforms": [
6 + "ios",
7 + "android",
8 + "web"
9 + ],
10 + "version": "1.0.0",
11 + "orientation": "portrait",
12 + "icon": "./assets/icon.png",
13 + "splash": {
14 + "image": "./assets/splash.png",
15 + "resizeMode": "contain",
16 + "backgroundColor": "#ffffff"
17 + },
18 + "updates": {
19 + "fallbackToCacheTimeout": 0
20 + },
21 + "assetBundlePatterns": [
22 + "**/*"
23 + ],
24 + "ios": {
25 + "supportsTablet": true
26 + },
27 + "android" : {
28 + "package" : "com.A.BLE",
29 + "config": {
30 + "googleMaps": {"apiKey":"AIzaSyCe_1-Mml2OyihnmS2oiPUj73X-Aj1j_2k"}
31 + }
32 + }
33 + }
34 +}
1 +module.exports = function(api) {
2 + api.cache(true);
3 + return {
4 + presets: ['babel-preset-expo'],
5 + };
6 +};
1 +import React, { memo, useState, useEffect, useMemo, createRef } from "react";
2 +import { Dimensions, LayoutAnimation, Platform } from "react-native";
3 +import MapView, { Marker, Polyline } from "react-native-maps";
4 +import SuperCluster from "supercluster";
5 +import ClusterMarker from "./ClusteredMarker";
6 +import {
7 + isMarker,
8 + markerToGeoJSONFeature,
9 + calculateBBox,
10 + returnMapZoom,
11 + generateSpiral
12 +} from "./helpers";
13 +
14 +const ClusteredMapView = ({
15 + radius,
16 + maxZoom,
17 + minZoom,
18 + extent,
19 + nodeSize,
20 + children,
21 + onClusterPress,
22 + onRegionChangeComplete,
23 + preserveClusterPressBehavior,
24 + clusteringEnabled,
25 + clusterColor,
26 + clusterTextColor,
27 + spiderLineColor,
28 + layoutAnimationConf,
29 + animationEnabled,
30 + renderCluster,
31 + ...restProps
32 +}) => {
33 + const [markers, updateMarkers] = useState([]);
34 + const [spiderMarkers, updateSpiderMarker] = useState([]);
35 + const [otherChildren, updateChildren] = useState([]);
36 + const [superCluster, setSuperCluster] = useState(null);
37 + const [currentRegion, updateRegion] = useState(
38 + restProps.region || restProps.initialRegion
39 + );
40 +
41 + const [isSpiderfier, updateSpiderfier] = useState(false);
42 + const [spiderfierMarker, updateSpiderfierMarker] = useState(null);
43 + const [clusterChildren, updateClusterChildren] = useState(null);
44 + const mapRef = createRef();
45 +
46 + const propsChildren = useMemo(() => React.Children.toArray(children), [
47 + children
48 + ]);
49 +
50 + useEffect(() => {
51 + const rawData = [];
52 + const otherChildren = [];
53 +
54 + if (!clusteringEnabled) {
55 + updateChildren(propsChildren);
56 + return;
57 + }
58 +
59 + React.Children.forEach(children, (child, i) => {
60 + if (isMarker(child)) {
61 + rawData.push(markerToGeoJSONFeature(child, i));
62 + } else {
63 + otherChildren.push(child);
64 + }
65 + });
66 +
67 + const superCluster = new SuperCluster({
68 + radius,
69 + maxZoom,
70 + minZoom,
71 + extent,
72 + nodeSize
73 + });
74 +
75 + superCluster.load(rawData);
76 +
77 + const bBox = calculateBBox(currentRegion);
78 + const zoom = returnMapZoom(currentRegion, bBox, minZoom);
79 + const markers = superCluster.getClusters(bBox, zoom);
80 +
81 + updateMarkers(markers);
82 + updateChildren(otherChildren);
83 + setSuperCluster(superCluster);
84 + }, [children, restProps.region, restProps.initialRegion]);
85 +
86 + useEffect(() => {
87 + if (isSpiderfier && markers.length > 0) {
88 + let positions = generateSpiral(
89 + markers[0].properties.point_count,
90 + markers[0].geometry.coordinates,
91 + clusterChildren
92 + );
93 + updateSpiderMarker(positions);
94 + updateSpiderfierMarker({
95 + latitude: markers[0].geometry.coordinates[1],
96 + longitude: markers[0].geometry.coordinates[0]
97 + });
98 + } else {
99 + updateSpiderMarker([]);
100 + }
101 + }, [isSpiderfier]);
102 +
103 + const _onRegionChangeComplete = region => {
104 + if (superCluster) {
105 + const bBox = calculateBBox(region);
106 + const zoom = returnMapZoom(region, bBox, minZoom);
107 + const markers = superCluster.getClusters(bBox, zoom);
108 +
109 + if (animationEnabled && Platform.OS === "ios") {
110 + LayoutAnimation.configureNext(layoutAnimationConf);
111 + }
112 +
113 + if (zoom >= 17 && markers.length === 1 && clusterChildren) {
114 + updateSpiderfier(true);
115 + } else {
116 + updateSpiderfier(false);
117 + }
118 +
119 + updateMarkers(markers);
120 + onRegionChangeComplete(region, markers);
121 + updateRegion(region);
122 + }
123 + };
124 +
125 + const _onClusterPress = cluster => () => {
126 + const children = superCluster.getLeaves(cluster.id);
127 + updateClusterChildren(children);
128 +
129 + if (preserveClusterPressBehavior) {
130 + onClusterPress(cluster, children);
131 + return;
132 + }
133 +
134 + const coordinates = children.map(({ geometry }) => ({
135 + latitude: geometry.coordinates[1],
136 + longitude: geometry.coordinates[0]
137 + }));
138 +
139 + mapRef.current.fitToCoordinates(coordinates, {
140 + edgePadding: restProps.edgePadding
141 + });
142 +
143 + onClusterPress(cluster, children);
144 + };
145 +
146 + return (
147 + <MapView
148 + {...restProps}
149 + ref={map => {
150 + restProps.mapRef(map);
151 + mapRef.current = map;
152 + }}
153 + onRegionChangeComplete={_onRegionChangeComplete}
154 + >
155 + {markers.map(marker =>
156 + marker.properties.point_count === 0 ? (
157 + propsChildren[marker.properties.index]
158 + ) : !isSpiderfier ? (
159 + renderCluster ? (
160 + renderCluster({
161 + onPress: _onClusterPress(marker),
162 + clusterColor,
163 + clusterTextColor,
164 + ...marker
165 + })
166 + ) : (
167 + <ClusterMarker
168 + key={`cluster-${marker.id}`}
169 + {...marker}
170 + onPress={_onClusterPress(marker)}
171 + clusterColor={clusterColor}
172 + clusterTextColor={clusterTextColor}
173 + />
174 + )
175 + ) : null
176 + )}
177 + {otherChildren}
178 + {spiderMarkers.map(marker => (
179 + <Marker
180 + key={marker.latitude}
181 + coordinate={marker}
182 + image={marker.image}
183 + onPress={marker.onPress}
184 + ></Marker>
185 + ))}
186 + {spiderMarkers.map((marker, index) => {
187 + {
188 + return (
189 + spiderfierMarker && (
190 + <Polyline
191 + key={index}
192 + coordinates={[spiderfierMarker, marker, spiderfierMarker]}
193 + strokeColor={spiderLineColor}
194 + strokeWidth={1}
195 + />
196 + )
197 + );
198 + }
199 + })}
200 + </MapView>
201 + );
202 +};
203 +
204 +ClusteredMapView.defaultProps = {
205 + clusteringEnabled: true,
206 + animationEnabled: true,
207 + preserveClusterPressBehavior: false,
208 + layoutAnimationConf: LayoutAnimation.Presets.spring,
209 + // SuperCluster parameters
210 + radius: Dimensions.get("window").width * 0.06,
211 + maxZoom: 20,
212 + minZoom: 1,
213 + extent: 512,
214 + nodeSize: 64,
215 + // Map parameters
216 + edgePadding: { top: 50, left: 50, right: 50, bottom: 50 },
217 + // Cluster styles
218 + clusterColor: "#00B386",
219 + clusterTextColor: "#FFFFFF",
220 + spiderLineColor: "#FF0000",
221 + // Callbacks
222 + onRegionChangeComplete: () => {},
223 + onClusterPress: () => {},
224 + mapRef: () => {}
225 +};
226 +
227 +export default memo(ClusteredMapView);
1 +import React, { memo } from "react";
2 +import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
3 +import { Marker } from "react-native-maps";
4 +import { returnMarkerStyle } from "./helpers";
5 +
6 +const ClusteredMarker = ({
7 + geometry,
8 + properties,
9 + onPress,
10 + clusterColor,
11 + clusterTextColor
12 +}) => {
13 + const points = properties.point_count;
14 + const { width, height, fontSize, size } = returnMarkerStyle(points);
15 +
16 + return (
17 + <Marker
18 + coordinate={{
19 + longitude: geometry.coordinates[0],
20 + latitude: geometry.coordinates[1]
21 + }}
22 + style={{ zIndex: points + 1 }}
23 + onPress={onPress}
24 + >
25 + <TouchableOpacity
26 + activeOpacity={0.5}
27 + style={[styles.container, { width, height }]}
28 + >
29 + <View
30 + style={[
31 + styles.wrapper,
32 + {
33 + backgroundColor: clusterColor,
34 + width,
35 + height,
36 + borderRadius: width / 2
37 + }
38 + ]}
39 + />
40 + <View
41 + style={[
42 + styles.cluster,
43 + {
44 + backgroundColor: clusterColor,
45 + width: size,
46 + height: size,
47 + borderRadius: size / 2
48 + }
49 + ]}
50 + >
51 + <Text style={[styles.text, { color: clusterTextColor, fontSize }]}>
52 + {points}
53 + </Text>
54 + </View>
55 + </TouchableOpacity>
56 + </Marker>
57 + );
58 +};
59 +
60 +const styles = StyleSheet.create({
61 + container: {
62 + display: "flex",
63 + justifyContent: "center",
64 + alignItems: "center"
65 + },
66 + wrapper: {
67 + position: "absolute",
68 + opacity: 0.5,
69 + zIndex: 0
70 + },
71 + cluster: {
72 + display: "flex",
73 + justifyContent: "center",
74 + alignItems: "center",
75 + zIndex: 1
76 + },
77 + text: {
78 + fontWeight: "bold"
79 + }
80 +});
81 +
82 +export default memo(ClusteredMarker);
1 +import { Component } from 'react';
2 +import { StyleSheet, Text, View } from 'react-native';
3 +import { Icon } from 'native-base'; // 추가된 코드
4 +import React from 'react'
5 +import {Marker} from 'react-native-maps'
6 +import Constants from 'expo-constants';
7 +import * as Location from 'expo-location';
8 +import MapView from "./ClusteredMapView";
9 +
10 +
11 +export default class MainScreen extends Component {
12 +
13 + // navigationOptions 코드 추가
14 + static navigationOptions = {
15 + headerLeft: <Icon name='ios-camera' style={{ paddingLeft:10 }}/>,
16 + title: 'PME Service',
17 + headerRight: <Icon name='ios-send' style={{ paddingRight:10 }}/>,
18 + }
19 + constructor(props) {
20 + super(props)
21 + this.state= {
22 + location:null,
23 + errorMsg:null
24 + }
25 + }
26 + componentDidMount() {
27 + (async () => {
28 + let { status } = await Location.requestPermissionsAsync();
29 + if (status !== 'granted') {
30 + this.setState({
31 + errorMsg:'Permission to access location was denied'
32 + })
33 + }
34 +
35 + let location = await Location.getCurrentPositionAsync({});
36 + console.log(location)
37 + this.setState({
38 + location
39 + },() => {
40 + console.log(this.state.location.coords.latitude)
41 + })
42 + })();
43 + }
44 + render() {
45 + return (
46 + this.state.location?
47 + <MapView
48 + style={{ flex: 1 }}
49 + initialRegion={ { latitude: this.state.location.coords.latitude, longitude: this.state.location.coords.longitude
50 + ,latitudeDelta: 0.0922, longitudeDelta: 0.0421}}
51 + zoomEnabled={true}
52 + pitchEnabled={true}
53 + showsUserLocation={true}
54 + followsUserLocation={true}
55 + showsCompass={true}
56 + showsBuildings={true}
57 + showsTraffic={true}
58 + showsIndoors={true}
59 + extent={512}>
60 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.1, longitude: this.state.location.coords.longitude }} />
61 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.5, longitude: this.state.location.coords.longitude }} />
62 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.0001, longitude: this.state.location.coords.longitude }} />
63 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.0003, longitude: this.state.location.coords.longitude }} />
64 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.03, longitude: this.state.location.coords.longitude }} />
65 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.05, longitude: this.state.location.coords.longitude }} />
66 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.01, longitude: this.state.location.coords.longitude }} />
67 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.2, longitude: this.state.location.coords.longitude }} />
68 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.3, longitude: this.state.location.coords.longitude }} />
69 + <Marker coordinate={{ latitude: this.state.location.coords.latitude+0.7, longitude: this.state.location.coords.longitude }} />
70 +
71 + </MapView>:<Text>Loading..</Text>
72 + );
73 + }
74 +}
75 +
76 +const styles = StyleSheet.create({
77 + container: {
78 + flex: 1,
79 + alignItems: 'center',
80 + justifyContent: 'center',
81 + },
82 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import GeoViewport from "@mapbox/geo-viewport";
2 +import { Dimensions } from "react-native";
3 +
4 +const { width, height } = Dimensions.get("window");
5 +
6 +export const isMarker = child =>
7 + child &&
8 + child.props &&
9 + child.props.coordinate &&
10 + child.props.cluster !== false;
11 +
12 +export const calculateBBox = region => {
13 + let lngD;
14 + if (region.longitudeDelta < 0) lngD = region.longitudeDelta + 360;
15 + else lngD = region.longitudeDelta;
16 +
17 + return [
18 + region.longitude - lngD, // westLng - min lng
19 + region.latitude - region.latitudeDelta, // southLat - min lat
20 + region.longitude + lngD, // eastLng - max lng
21 + region.latitude + region.latitudeDelta // northLat - max lat
22 + ];
23 +};
24 +
25 +export const returnMapZoom = (region, bBox, minZoom) => {
26 + const viewport =
27 + region.longitudeDelta >= 40
28 + ? { zoom: minZoom }
29 + : GeoViewport.viewport(bBox, [width, height]);
30 +
31 + return viewport.zoom;
32 +};
33 +
34 +export const markerToGeoJSONFeature = (marker, index) => {
35 + return {
36 + type: "Feature",
37 + geometry: {
38 + coordinates: [
39 + marker.props.coordinate.longitude,
40 + marker.props.coordinate.latitude
41 + ],
42 + type: "Point"
43 + },
44 + properties: {
45 + point_count: 0,
46 + index,
47 + ..._removeChildrenFromProps(marker.props)
48 + }
49 + };
50 +};
51 +
52 +export const generateSpiral = (count, centerLocation, clusterChildren) => {
53 + let res = [];
54 + res.length = count;
55 + let angle = 0;
56 +
57 + for (let i = 0; i < count; i++) {
58 + angle = 0.25 * (i * 0.5);
59 + let latitude = centerLocation[1] + 0.0002 * angle * Math.cos(angle);
60 + let longitude = centerLocation[0] + 0.0002 * angle * Math.sin(angle);
61 + res[i] = {
62 + longitude,
63 + latitude,
64 + image: clusterChildren[i] && clusterChildren[i].properties.image,
65 + onPress: clusterChildren[i] && clusterChildren[i].properties.onPress
66 + };
67 + }
68 +
69 + return res;
70 +};
71 +
72 +export const returnMarkerStyle = points => {
73 + if (points >= 50) {
74 + return {
75 + width: 84,
76 + height: 84,
77 + size: 64,
78 + fontSize: 20
79 + };
80 + }
81 +
82 + if (points >= 25) {
83 + return {
84 + width: 78,
85 + height: 78,
86 + size: 58,
87 + fontSize: 19
88 + };
89 + }
90 +
91 + if (points >= 15) {
92 + return {
93 + width: 72,
94 + height: 72,
95 + size: 54,
96 + fontSize: 18
97 + };
98 + }
99 +
100 + if (points >= 10) {
101 + return {
102 + width: 66,
103 + height: 66,
104 + size: 50,
105 + fontSize: 17
106 + };
107 + }
108 +
109 + if (points >= 8) {
110 + return {
111 + width: 60,
112 + height: 60,
113 + size: 46,
114 + fontSize: 17
115 + };
116 + }
117 +
118 + if (points >= 4) {
119 + return {
120 + width: 54,
121 + height: 54,
122 + size: 40,
123 + fontSize: 16
124 + };
125 + }
126 +
127 + return {
128 + width: 48,
129 + height: 48,
130 + size: 36,
131 + fontSize: 15
132 + };
133 +};
134 +
135 +const _removeChildrenFromProps = props => {
136 + const newProps = {};
137 + Object.keys(props).forEach(key => {
138 + if (key !== "children") {
139 + newProps[key] = props[key];
140 + }
141 + });
142 + return newProps;
143 +};
This diff could not be displayed because it is too large.
1 +{
2 + "main": "node_modules/expo/AppEntry.js",
3 + "scripts": {
4 + "start": "expo start",
5 + "android": "expo start --android",
6 + "ios": "expo start --ios",
7 + "web": "expo start --web",
8 + "eject": "expo eject"
9 + },
10 + "dependencies": {
11 + "@expo/vector-icons": "^10.1.0",
12 + "@react-native-community/masked-view": "^0.1.10",
13 + "expo": "~37.0.3",
14 + "expo-constants": "^9.0.0",
15 + "expo-location": "^8.1.0",
16 + "native-base": "^2.13.12",
17 + "react": "~16.9.0",
18 + "react-dom": "~16.9.0",
19 + "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
20 + "react-native-gesture-handler": "^1.6.1",
21 + "react-native-map-clustering": "^3.1.2",
22 + "react-native-maps": "^0.27.1",
23 + "react-native-safe-area-context": "^0.7.3",
24 + "react-native-screens": "^2.7.0",
25 + "react-native-web": "~0.11.7",
26 + "react-navigation": "^4.3.8",
27 + "react-navigation-stack": "^2.3.13"
28 + },
29 + "devDependencies": {
30 + "@babel/core": "^7.8.6",
31 + "babel-preset-expo": "~8.1.0"
32 + },
33 + "private": true
34 +}
This diff could not be displayed because it is too large.
1 +{
2 + "dependencies": {
3 + "@expo/vector-icons": "^10.1.0",
4 + "native-base": "^2.13.12",
5 + "react-navigation": "^4.3.8"
6 + }
7 +}
This diff is collapsed. Click to expand it.
No preview for this file type