Earthquake Realtime BMKG

Full-Stack

Akhirnya saya membuatnya, project ini adalah impian saya dulu ketika tertarik dengan kegempaan waktu kuliah dulu, dengan perawalan menyukai dasar ilmu struktur geologi, tentu juga sedimentologi, karena stratigrafi akan menjadi nilai 0 jika tanpa struktur geologi, bumi sudah tua, deformasi dimana-dimana, baik natural maupun karena tingkah kita (manusia), ya masa-masa deformasi akan terus berlanjut sampai memang kala waktu yang menghentikannya. Ini bukan puisi, melainkan awalan dalam membuat Personal project Earthquake Realtime BMKG.


Requirement

Untuk mengawali project ini dibutuhkan beberapa tools diantaranya:

  1. NodeJS
  2. Code editor
  3. Your head 🧄

Untuk tech stack yang digunakan sebagai berikut, beserta kegunaannya:

  1. ReactJS sebagai framework
  2. OpenLayers sebgai main library mapping
  3. RLayers sebagai support OpenLayers di dalam React
  4. React-timeseries-charts sebagai library timeseries untuk react
  5. React-Query sebagai state dan fetching management
  6. Moment sebagai support time library
  7. TailwindCSS sebagai support library style css
  8. API dari BMKG itu sendiri yang open public

Perlu dijelaskan disini, API dari BMKG data yang tersedia hanya menampilkan kegempaan diatas 5 Magnitudo, kegempaan yang di bawah itu tidak tersedia. Dan project ini hanya untuk dekstop friendly, karena timeseries dan custom feature di dalamnya sedikit berat, dan butuh penyesuaian lagi untuk mendisplay beberapa feature didalamnya


Getting Started

Inisialisasi directory pada laptop anda dan jalankan create react app dengan diikuti nama project setelahnya

npx create react-app earthquake-bmkg

Initialisasi Map

Tambahkan reference dan center untuk map

src/App.js
const mapRef = useRef(null);
const [centerMap, setCenterMap] = useState({ center: fromLonLat([119.8917871, 0.838468]), zoom: 5 });

Buat file MapRender.js, Masukan pada props yang disediakan di RMap

<div className='w-screen h-[100svh]'>
  <RMap
    className='relative w-screen h-screen z-0 ol-control'
    initial={props.centerMap}
    ref={props.mapRef}
    onPointerDrag={(e) => {
      return (
        <div>
          <RInteraction.RModify snapTolerance={0.001} />
        </div>
      )
    }}
  >
    {/* option tile layer pertama */}
    <ROSM />
    {/* option tile layer kedua */}
    <RLayerTile
      useInterimTilesOnError={true}
      url='YOUR_URL_FROM_MAP_TILES-/{z}/{x}/{y}.png?'
      attributions='this site created by <a href="https://www.maptiler.com/copyright/" target="_blank">'
      className="-z-0"
    />
    {props.children}
    <RControl.RFullScreen />
    <RControl.RScaleLine />
    <RControl.RAttribution />
    <RControl.RZoom />
  </RMap>
</div>

Initialisasi Layer Maps

Add Layers pada children component MapRender, in here I use React.memo to define component on App.js

const [eqView, setEqView] = useState(true);
const [faultView, setFaultView] = useState(true);

{eqView && (
  <EqMemo
    eqLayer={eqLayer} // state of data from react-query
    isStale={isStale} // state of stale from react-query
    isLoading={isLoading} // state of loading from react-query
    mapRef={mapRef} // ref maps
    setCenterMap={setCenterMap} // for handling zoom
    setLayers={setLayers} // for save layer select on state
    setForceInfoLayer={setForceInfoLayer} // for triger open sidebar
  />
)}
{faultView && (
  <FaultMemo />  // layer using geojson on public directory
)}

Berikut untuk cara mendeskripsikan data geojson untuk di integrasikan menjadi feature dari OpenLayers, pada kode dibawah ini kita membuat definisi class Feature dengan data geojson yang kita miliki

const data = eqLayer?.features
const eqFeatures = Object.keys(data ?? {}).map(
  (key) =>
    new Feature({
      geometry: new Point(
        fromLonLat(data[key].geometry.coordinates),
      ),
      id: data[key].properties.id,
      mag: data[key].properties.mag,
      place: data[key].properties.place,
      time: data[key].properties.time,
      depth: data[key].properties.depth,
      status: data[key].properties.status,
      fase: data[key].properties.fase
    }),
);

Initialisasi Vector Mapping With Multiple Style

Pada file EqMemo sebagai berikut:

{eqFeatures.map((val) => (
  <RLayerVector
    key={val.get("id")}
    zIndex={10}
    onClick={(e) => setLayers(e)}
  >
    <RStyle.RStyle ref={style} zIndex={val.get('depth') ? val.get('depth') + 15 : 5}>
      <RStyle.RCircle
        radius={
          parseFloat(val.get('mag')) > 7 ? parseFloat(val.get('mag')) + 5
          : parseFloat(val.get('mag')) > 6 ? parseFloat(val.get('mag')) + 4
            : parseFloat(val.get('mag')) > 5.5 ? parseFloat(val.get('mag')) + 2 : 4}>
        <RStyle.RFill color={parseFloat(val.get('depth')) > 1000 ? 'purple'
          : parseFloat(val.get('depth')) > 500 ? 'green'
            : parseFloat(val.get('depth')) > 100 ? 'yellow'
              : parseFloat(val.get('depth')) > 30 ? 'blue' :
                'red'} />
        <RStyle.RStroke color="#2cb5db" width={1} />
      </RStyle.RCircle>
    </RStyle.RStyle>
    <RFeature
      feature={val}
      onClick={(e) => {
        e.map.getView().fit(e.target.getGeometry().getExtent(), {
          duration: 300,
          maxZoom: 10
        })
        setLayers(e)
        setForceInfoLayer(true)
      }}
    >
    </RFeature>
  </RLayerVector>
))}

Initialisasi Vector GeoJSON file static

Dan pada file FaultMemo sebagai berikut

<RLayerVector
  zIndex={10}
  url='/file/faults.geojson' // ditujukan file geojson yang ada di public directory
  format={
    new GeoJSON({
      featureProjection: "EPSG:3857",
      dataProjection: "EPSG:4326",
    })
  }
>
  <RStyle.RStyle ref={style}>
    <RStyle.RStroke color="red" width={1} />
  </RStyle.RStyle>
</RLayerVector >

Done..!!


Semoga tulisan ini bermanfaat, adapun untuk demo applikasinya anda bisa akses langsung pada laman berikut https://earthquake-maps.vercel.app/


© 2023, ngrhadi
Jambi, Indonesia