_mapbox.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { Text } from 'app/design/typography'
  2. import { View, Row } from 'app/design/view'
  3. import { useState, useRef, useCallback } from 'react';
  4. import Link from 'app/ui/atoms/link'
  5. //import Mapbox from "@rnmapbox/maps";
  6. import { useWindowHeight } from 'app/context/measure';
  7. import { BlockWrapper } from 'app/components/block-wrapper'
  8. import { appSetting } from 'app/lib/util';
  9. //TODO SMALL POINTS + desc
  10. /*
  11. extra: {
  12. .....
  13. "RNMAPBOX_MAPS_DOWNLOAD_TOKEN": process.env.RNMAPBOX_MAPS_DOWNLOAD_TOKEN
  14. },
  15. */
  16. //https://blog.logrocket.com/building-custom-maps-react-native-mapbox/
  17. export default function ElementMapBox({ data, blockWrapperProps }) {
  18. return <></>
  19. /*Mapbox.setAccessToken(appSetting('config', 'api_keys', 'mapbox'));
  20. const mapRef = useRef(null);
  21. const [selectedlayers, setSelectedLayers] = useState(['incarcerees']);
  22. const [popupInfo, setPopupInfo] = useState(null);
  23. const windowHeight = useWindowHeight();
  24. const [viewport, setViewport] = useState({
  25. longitude: data.center[0],
  26. latitude: data.center[1],
  27. zoom: data.zoom
  28. })
  29. const dataSources = data.sources;
  30. const clickableLayers = dataSources.filter(source => selectedlayers.includes(source.key)).flatMap(source =>
  31. (source.layers || []).map(layer => ({
  32. ...layer,
  33. key: source.key
  34. }))
  35. )
  36. .filter(layer => layer.clickable === true).map(layer => layer.id);
  37. const infoFields = {
  38. 'num_people_excluded': 'Number of people excluded: ',
  39. 'sos_category': '',
  40. 'location': 'Location: ',
  41. 'camp': 'Camp: ',
  42. 'pe_location': 'Pre-evacuation Location: ',
  43. };
  44. const convertLayoutToStyle = (p) => {
  45. if (!p) return {}; // Guard: p may be missing
  46. const mapping = {
  47. layout: {
  48. 'icon-image': 'iconImage',
  49. 'icon-size': 'iconSize',
  50. 'text-field': 'textField',
  51. 'text-font': 'textFont',
  52. 'text-size': 'textSize',
  53. },
  54. paint: {
  55. 'circle-color': 'circleColor',
  56. 'circle-radius': 'circleRadius',
  57. 'circle-strokeWidth': 'circleStrokeWidth',
  58. 'circle-stroke-color': 'circleStrokeColor',
  59. 'fill-color': 'fillColor',
  60. 'fill-opacity': 'fillOpacity',
  61. 'line-color': 'lineColor',
  62. 'line-width': 'lineWidth',
  63. },
  64. };
  65. return Object.keys(mapping).reduce((style, key) => {
  66. if (p[key]) {
  67. Object.entries(mapping[key]).forEach(([sourceKey, targetKey]) => {
  68. if (p[key][sourceKey] !== undefined) {
  69. style[targetKey] = p[key][sourceKey];
  70. }
  71. });
  72. }
  73. return style;
  74. }, {});
  75. };
  76. const handleOnPress1 = useCallback((event) => {
  77. setPopupInfo(null)
  78. }, [clickableLayers]);
  79. const handleOnPress2 = useCallback((event) => {
  80. setPopupInfo(null)
  81. }, [clickableLayers]);
  82. const handleOnPress = useCallback((event) => {
  83. const features = event.features;
  84. if (features.length > 0) {
  85. const feature = features[0];
  86. if (feature.properties.cluster_id) {
  87. const clusterId = feature.properties.cluster_id;
  88. const coordinates = feature.geometry.coordinates;
  89. console.log("aaaa", {
  90. longitude: coordinates[0],
  91. latitude: coordinates[1],
  92. zoom: Math.min(viewport.zoom + 2, 18)
  93. })
  94. setViewport({
  95. longitude: coordinates[0],
  96. latitude: coordinates[1],
  97. zoom: Math.min(viewport.zoom + 2, 18)
  98. })
  99. }
  100. else {
  101. if (feature.geometry.type == "Point") {
  102. const coordinates = feature.geometry.coordinates.slice();
  103. setPopupInfo({
  104. coordinates: coordinates,
  105. object: feature.properties,
  106. });
  107. }
  108. if (feature.geometry.type == "Polygon") {
  109. const coordinates = feature.geometry.coordinates[0][0];
  110. setPopupInfo({
  111. coordinates: coordinates,
  112. object: feature.properties,
  113. });
  114. }
  115. }
  116. }
  117. }, [clickableLayers]);
  118. const layerComponents = {
  119. circle: Mapbox.CircleLayer,
  120. fill: Mapbox.FillLayer,
  121. line: Mapbox.LineLayer,
  122. symbol: Mapbox.SymbolLayer,
  123. };
  124. return (
  125. <BlockWrapper {...blockWrapperProps}>
  126. <View className="w-full " style={{ height: windowHeight - 134 }}>
  127. <Row className="gap-x-4 mb-2 justify-center">
  128. {dataSources.map((layer, index) => (
  129. <Button size="xs" key={layer.key} title={layer.name} pressed={selectedlayers.includes(layer.key)} onPress={() => selectedlayers.includes(layer.key) ? setSelectedLayers(selectedlayers.filter(name => name !== layer.key)) : setSelectedLayers([...selectedlayers, layer.key])} />
  130. ))}
  131. </Row>
  132. <View className="flex-1" >
  133. <Mapbox.MapView style={{ flex: 1 }} ref={mapRef} onPress={handleOnPress1} styleURL="mapbox://styles/mapbox/light-v11">
  134. <Mapbox.Camera
  135. zoomLevel={viewport.zoom}
  136. centerCoordinate={[viewport.longitude, viewport.latitude]}
  137. animationMode="flyTo"
  138. animationDuration={2000}
  139. />
  140. {selectedlayers.map((layer, index) => {
  141. const lr = dataSources.find(l => l.key === layer);
  142. return (
  143. <Mapbox.ShapeSource key={lr.key} id={lr.key} url={lr.source} {...lr.props} onPress={handleOnPress}>
  144. {lr.layers.map((layer1, index1) => {
  145. const style = convertLayoutToStyle(layer1);
  146. const LayerComponent = layerComponents[layer1.type];
  147. return <LayerComponent key={lr.key + layer1.id} id={layer1.id} filter={layer1.filter} style={style} />
  148. })}
  149. </Mapbox.ShapeSource>
  150. )
  151. })}
  152. {popupInfo && (
  153. <Mapbox.PointAnnotation
  154. id="popup"
  155. coordinate={popupInfo.coordinates}
  156. >
  157. <View className="bg-white p-2 rounded-lg shadow-sm max-w-xs">
  158. {popupInfo.object.link ? <Link href={popupInfo.object.link}><Text className=" text-base font-medium mb-2">{popupInfo.object.name}{popupInfo.object.facility_name}{popupInfo.object.order_name}</Text></Link> : <Text className=" text-base font-medium mb-2">{popupInfo.object.name}{popupInfo.object.facility_name}{popupInfo.object.order_name}</Text>}
  159. {Object.entries(popupInfo.object)
  160. .filter(([key]) => Object.keys(infoFields).includes(key)) // Drop keys not in infoFields
  161. .map(([key, value]) => (
  162. <Text key={key} className="mb-2">
  163. {infoFields[key]}{value}
  164. </Text>
  165. ))}
  166. <Text className=" text-xs">{popupInfo.object.description}{popupInfo.object.facility_description}</Text>
  167. </View>
  168. </Mapbox.PointAnnotation>
  169. )}
  170. </Mapbox.MapView>
  171. </View>
  172. </View>
  173. </BlockWrapper>
  174. )*/
  175. }